​秒杀服务的限流策略​

  1. 合法性限流
    鉴定非法请求:
  1. 验证码(剔除机器人,使用户的请求时间随机分布)
  2. 非法IP限制
  3. 隐藏秒杀按钮入口
  1. 负载限流
  1. 负载均衡分发请求到每个服务器
  2. 多级(级联)负载,第二层MAC负载,第三层IP负载,第四层端口号负载,第七层nginx负载
  3. 级联复杂均衡,级联数的权衡
  4. 软件、硬件负载均衡
  1. 服务限流
  1. servlet服务器限流,配置连接数
  2. 算法限流(令牌桶)
  3. 队列限流,MQ中间件
  4. 缓存限流,多级缓存,缓存级数的权衡,缓存一致性问题,多级缓存的请求耗时
  5. 监控,服务降级

​跨语言的RPC调用​

WS的两个主要缺点:1.传输格式XML冗余浪费带宽编码解码复杂效率低;2.传输协议基于应用层HTTP,路径长效率低。
Pb的问题:只能转换数据,不能进行数据传输。
引入netty,两者结合诞生gRPC,基于HTTP2。

​Spring Boot使用HTTP/2​

​server.http2.enable=true​​​ + SSL证书
JDK8 + Tomcat 9 + libtcnative
JDK9 + Tomcat 9,自动支持HTTP/2
Undertow 1.4.0 支持HTTP/2
Jetty 9.4.8 + org.eclipse.jetty:jetty-alpn-conscrypt-server + org.eclipse.jetty.http2:http2-server
keytool + curl
定制Tomcat connector
底层http库可配置,如okhttp3

​高可用配置中心​

基于数据库的配置中心
缓存 + MySQL的实现方式:服务请求获取配置,先从JVM本地缓存查找,没有找到则去配置中心查找配置,即实际上去MySQL查找配置。
存储于MySQL的配置信息如何同步到缓存的三种方式:

  1. 缓存过期
  2. 定时轮询
  3. MQ通知,mysql binlog

问题:一致性,数据库压力,延迟性,

基于zookeeper的配置中心
watcher机制,

​避免资损问题 ​

  1. 网络异常问题
  2. 查询和
  3. 幂等性问题
  4. 通知问题
  5. 状态同步问题

​Spring Data JPA动态复杂查询​

复杂查询: 涉及多表,join,子查询等
动态查询:查询条件未知
Spring Data JPA能够减少代码量,适用于事务相关的OLTP场景
Mybatis支持手写SQL,可以跟进查询条件动态拼接参数,适用于数据分析的OLAP场景。

JPA支持动态复杂查询的三种方式:

  1. @Query注解,手写复杂SQL,需要遍历各种不同组合情况的查询条件去实现动态查询;
  2. @OneToOne, @OneToMany, @ManyToMany, @JoinColumn, @JoinTable等注解,需要学习其属性如cascadeType, FetchType, MappedBy,外键和级联的性能差。
  3. JPA + querydsl-jpa
    引入依赖
    QueryDSL: predicate, projection, QEntity
    定义maven profile: qentity, all.

​Spring Data JPA动态部分更新​

Spring Data JPA更新Entity时,对数据表中除主键外所有字段进行全量更新
repository.save(),在更新时,先查询
​​​NULL值问题​​ @DynamicUpdate + BeanUtils.copyNullProperties()

​高并发场景下锁的使用技巧​

乐观锁的实现方式

  1. 体现在数据库层面,就是版本号
  2. 体现在Java编码,就是CAS

高并发场景下扣库存的解决方案:

  1. synchronize,同步排它锁,重量级,问题:线程串行导致的性能问题、无法解决分布式情况下跨进程/跨JVM的问题
  2. 数据库行锁,悲观锁,select for update,可以解决跨进程问题,但是:
  1. 性能问题,select for update会一直阻塞直到事务提交,串行执行
  2. 需要设置数据库事务隔离级别read committed,否则其他事务不能看到提交的数据
  3. 如果事务中有第三方超时接口,会导致这个事务的连接一直阻塞,打满数据库连接
  4. 在多个业务的加锁顺序控制不好的情况下,发生交叉死锁。
  1. 分布式锁(Redis)的问题:
  1. 设置锁和超时时间的原子性;Redis 2.6.12版本之前,setnx不支持expire,需要setnx + expire两条命令,非原子操作
  2. 不设置超时时间的问题:代码耗时过长,超过超时时间,锁失效?错误地将其他线程同一个key的锁删除??
  3. 服务宕机或者线程超时阻塞的问题:
  4. 超时时间设置不合理的问题:业务经验值,续命锁,守护线程,实现复杂
  1. 数据库乐观锁(版本号)
  2. 人工补偿

​ZAB v.s. Paxos​

ZK,observer不参与Leader的选举过程和过半写成功策略
事务操作,改造zk状态的操作,如znode的创建、删除、内容更新
zxid,全局唯一的id,二阶段提交协议
其他算法的区别

算法

关键字

Paxos

过半选举,数据副本一致性

Raft

相对Paxos协议进行简化

ZAB

适用于离线的海量数据处理

hash路由

es请求路由

一致性hash

负载均衡

Cassandra

最终一致性

​中台服务化编排​

服务间的协作通过流程编排自动执行,实现一个完整的业务流程

  1. Zeebe
  1. 基于BPMN2.0规范,
  2. 任务通知模型采用消息驱动和发布-订阅两种模式
  3. Raft算法实现无中心化选举
  4. activiti6.0以下版本,基于数据库记录状态来驱动工作流进行,适合传统行业;
  5. Zeebe,消息机制驱动
  6. 适用于传统工作流升级为分布式工作流
  1. Nexflix Conductor
  1. JSON DSL定义服务执行流程
  2. 带服务管理监控界面
  3. 异步消息
  1. Apache Camel
  1. 基于组件,扩展性强
  2. 路由引擎控制器
  3. 5种DSL:Java DSL、Spring XML、Blueprint XML、REST DSL、Annotation DSL

服务编排的6个考虑:
编排框架选型、编排流程DSL、可视化编排设计系统、编排业务隔离、编排引擎的高可用、监控

​数据库优化场景​

  1. 分页limit优化,偏移量,返回结果集
  1. 增加where条件解决
  2. 业务层记录索引定位符,有数据删除或者插入的情况
  3. 子查询 + 索引定位:​​select id from (select id from info_table where id < 4900000 order by id desc limit 20 ) as temp_info order by id limit 1​
  1. 链/连表查询,join查询拆分成单表查询,避免使用临时表
  2. 子查询过重,需要优化

​反射和泛型编程​

多看看Spring Data JPA源码

​高性能、高可用的MySQL架构​

高性能:
MySQL 主从同步,bin log、relay log
读写分离, 80-20原则,
分库分表, 水平+垂直
MyCat
慢查询日志, mysqldumpslow
数据库优化:字段类型选取,数据库缓存,关闭无用服务,数据库参数调优,选择合适的存储引擎,SQL语句编写,索引处理

高可用:
去中心化集群,2个MyCat集群,vip
海量请求层层处理:限流,削峰填谷,缓存

​自动化测试​

客户端(GUI)+服务端(API),后者对业务逻辑层+数据层
工具:
postman+newman,不能测试RPC接口?
JMeter+插件,可以测试RPC接口;
JMeter+Jenkins:命令行或performance
rest-assured+TestNG+Jenkins

​Postman做接口自动化测试​

pre-request script(预处理脚本)+ 测试脚本
自动生成测试脚本;脚本分类:变量设置、响应处理相关、断言
postman+jenkins

​binlog数据恢复​

数据恢复依赖于备份方式. 主流备份方案:

  1. 全量备份,备份快,适合于数据量较小
  2. 全量+增量,节约磁盘空间,备份慢,适合于数据量大,

binlog三种模式:

  1. statement level, 老版本默认模式, 只存储SQL,不能进行数据恢复
  2. row level, 新版本默认模式
  3. mixed level,默认是statement,切换到row level模式

解析工具:mysqlbinlog

数据恢复工具:

  1. binlog-rollback, perl脚本
  2. MyFlash,美团开源

一种全新的思路:
在MySQL集群中引入延迟从库,可以设置24小时延迟同步。每日监控,若发现误操作,将延迟从库的数据同步到其他节点。

​通过软引用和弱引用提升JVM内存使用效率​

对于非核心数据,使用软引用​​HashMap<String, SoftReference<Content>>​​​,
弱引用类似。

​从Java线程内存模型角度分析线程是否安全​

主内存和线程内存,存的是副本;过程:读取,加载,操作,回写

线程安全

不安全的结构

StringBuffer

StringBuilder

volitile,保证立即度和立即写,提升性能,不能保证原子性,不能保证线程安全

​分布式定时任务系统​

调度器,执行器,管理界面;注解扫描;任务拆分到执行器;
高可用,调度器(master)选举,zk;

​Git分支工作流​

分支的定义;MR, merge request;
约定大于一切;版本号;git cherry-pick;git subtree

​操作系统&Java代码运行​

​时序存储引擎​

涉猎太少,听不太懂。
开源产品:RRD,Graphite,OpenTSDB,InfluxDB,prometheus

Facebook内存数据库Gorilla论文
作者开源的:https://github.com/lindb/lindb
特性:

  1. 数据是追加操作,Append only
  2. 时间强关联性
  3. 冷热数据明显,一般查询近期数据,写多读少
  4. 有很多维度可以对数据进行过滤

组成部分
metric name,tags(k-v对,可以有多个kv对),timestamp,filed value

(metric name,tags)一般是索引。
索引存储的数据结构:
posting list
位图bitmap
roaring bitmap
SSTable
Offset Block
Index Block
Footer

​如何快速开发数据平台​

Grafana,插件

​斐波那契数列​

解决:

  1. 递归,n到40的执行时间过大,O(n^2)
  2. 动态规划,迭代思想,O(n)
  3. 通项公式,有幂运算,O(log(n))
  4. 线性代数,矩阵运算,O(log(n))
// 解法1:递归
long fib1(int n) {
return (2 > n) ? (long) n : Fib(n -1) + Fib(n - 2);
}

// 解法2:线性复杂度迭代,常数空间复杂度
public static int fib2(int n) {
// 初始化:Fib(0) = 0,Fib(1) = 1
int prev = 0;
int next = 1;
while(n-- > 1) {
next = prev + next;
prev = next - prev;
}
return next;
}

// 解决2的迭代版本
public static int fib(int n, int first, int second) {
if (n < 2) {
return n;
}
if (n == 2) {
return first + second;
} else {
return Fib(n - 1, second, first + second);
}
}

private static long matrixFib(int num) {
if (num <= 1) {
return num;
}
// 二维矩阵
Matrix first = new Matrix(2, 2);
first.setElement(1, 1, 1);
first.setElement(1, 2, 1);
first.setElement(2, 1, 1);
first.setElement(2, 2, 0);

Matrix result = new Matrix(2, 1);
result.setElement(1, 1, 1);
result.setElement(2, 1, 0);
// 根据递推式求第num项,只需求first矩阵的num - 1次方
int n = num - 1;
while (n > 0) {
if (n % 2 != 0) {
result = first.multiMatrix(result);
}
if ((n /= 2) > 0) {
first = first.multiMatrix(first);
}
}
return result.getElement(1, 1);
}

/**
* 一个当作矩阵的二维数组
*/
class Matrix {
/**
* 当前矩阵的行数
*/
private int row;
/**
* 当前矩阵的列数
*/
private int col;
/**
* 二维数组用于保存矩阵
*/
private ArrayList<ArrayList<Integer>> matrix;

/**
* 传入行数和列数构造一个零矩阵
*/
Matrix(int row, int col) {
this.row = row;
this.col = col;
matrix = new ArrayList<>(row);
for (int i = 0; i < row; i++) {
ArrayList<Integer> list = new ArrayList<>(col);
for (int j = 0; j < col; j++) {
list.add(0);
}
matrix.add(list);
}
}

private int getRow() {
return row;
}

private int getCol() {
return col;
}

int getElement(int row, int col) {
return matrix.get(row - 1).get(col - 1);
}

void setElement(int row, int col, int value) {
matrix.get(row - 1).set(col - 1, value);
}

/**
* 获取某一行向量的值
*/
private ArrayList<Integer> getRow(int row) {
return matrix.get(row - 1);
}

/**
* 获取某一列向量的值
*/
private ArrayList<Integer> getCol(int col) {
ArrayList<Integer> arrCol = new ArrayList<>();
for (int i = 0; i < row; i++) {
arrCol.add(matrix.get(i).get(col - 1));
}
return arrCol;
}

/**
* 向量点乘
*/
private int multiVec(ArrayList<Integer> v1, ArrayList<Integer> v2) {
if (v1.size() != v2.size()) {
return -1;
}
int result = 0;
for (int i = 0; i < v1.size(); i++) {
result += (v1.get(i)) * (v2.get(i));
}
return result;
}

/**
* 矩阵乘法,只有第一个矩阵的列数等于第二个矩阵的行数才能相乘
*/
Matrix multiMatrix(Matrix matrix1) {
if (getCol() != matrix1.getRow()) {
return null;
}
Matrix matrix2 = new Matrix(getRow(), matrix1.getCol());
for (int i = 1; i <= getRow(); i++) {
for (int j = 1; j <= matrix1.getCol(); j++) {
matrix2.setElement(i, j, multiVec(getRow(i), matrix1.getCol(j)));
}
}
return matrix2;
}
}

​当小内存遇上大数据​

  1. 压缩:
    无论是哪种方式,都需要保证计算结果的正确性
  1. 无损压缩
  2. 有损压缩
  1. 分块:
    每次只加载部分数据
  2. 索引:
    索引存在RAM,数据存在硬盘