面试笔记总结

思维导图详见https://www.processon.com/view/link/62b2e08f5653bb0724fd7359

网络协议

网络分层模型

  - OSI七层体系
  - TCP/IP五层

TCP

  - 面向连接、可靠、基于字节流、传输层通信协议
  - 三次握手
  - 四次挥手
  - 数据传输过程

UDP

  - 面向连接、不保证可靠、基于报文、无阻塞的传输层协议

HTTP

  - 无状态、无连接、数据格式灵活、简单快速、应用层的超文本传输协议
  - 报文格式:报文首部(请求行、多个请求体/状态行、多个响应头)、空行、报文主体
  - 请求类型:GET/POST/HEAD/PUT/DELETE/TRACE/OPTIONS/CONNECT
  - 状态码及含义
     - 1xx 请求处理中
     - 2xx 请求成功处理
     - 3xx 重定向
        - 301:Moved Permanently 永久重定向
        -  302:Found 临时重定向
     - 4xx 客户端错误,请求不合法
        - 400 Bad Request 请求语法错误
        - 401 Unauthorized 未经授权
        - 403 Forbidden 拒绝提供服务
        - 404 Not Found 资源不存在
     - 5xx 服务端错误
        - 500 Internal Server Error 不可预期错误
        - 503 Server Unavailable 服务不能处理请求不可用
  - 支持B/S、C/S架构
  - Cookies与Session
     - Session由服务端生成,可存放于文件、数据库、内存
     - cookies在客户端,客户端请求服务端会生成对应sessionid,并返回客户端,通过setCookies将其放入,后续请求发送cookie可查验sessionid保证其状态
  - 完整的http请求过程

1、DNS域名解析
2、建立socket连接,发起TCP三次握手
3、客户端给服务端发送请求命令(请求头和数据)
4、服务端返回响应头与数据
5、关闭socket连接,TCP四次挥手
6、客户端根据返回渲染数据

HTTPS

  - HTTP+加密+认证+完整性保护=HTTPS
  - 在TCP与HTTP之间加入SSL/TLS为上层安全保驾护航
  - 单向认证
  - 双向认证
  - WEB攻击
     - DOS攻击、SQL注入、中间人攻击、OS命令等

JVM

内存模型

  - 程序计数器
  - 虚拟机栈
  - 本地方法栈
  - Java堆
  - 方法区
  - 运行时常量池
  - 直接内存

对象分配与创建过程

OOM、内存溢出与内存泄漏常见场景

虚拟机性能监控、故障处理工具

  - jps -l:查询虚拟机进程
  - jstat -gc/-gcutil:监控虚拟机运行状态
  - jinfo -flag xxx:实时查看虚拟机参数
  - jmap -dump:format=b,file=eclipse.bin PID:生成对应堆转储快照
  - -XX:+HeapDumpOnOutOfMemoryError/-XX:+HeapDumpOnCtrlBreak:在对应条件下生成堆转储快照
  - jhat:堆转储快照分析工具
  - jstack -l PID:堆栈跟踪工具

对象死亡判断

  - 引用计数法
  - 可达性分析
     - GCRoots对象:
        - 1、虚拟机栈中引用的对象,被调用方法的参数、局部变量、临时变量等
        - 2、方法区的类静态属性引用的对象
        - 3、方法区中的常量引用的对象
        - 4、本地方法栈JNI引用的对象

垃圾回收算法

  - 标记-清除
     - 内存碎片、导致内存不连续、使用率不高导致无法分配触发GC
     - 效率随对象数量增长而降低
  - 标记-复制
     - 只使用一半内存,不过没有内存碎片
     - 复制在存活对象较多时复制效率低
  - 标记--整理
     - 没有内存 碎片
     - 标记整理效率不高

垃圾回收器

  - ParNew+CMS
     - 初始标记与重新标记会STW
  - G1
  - ZGC

常用的调优命令

  - Xms=Xmx、PermSize=MaxPermSize,避免内存扩展
  - 子主题

类加载过程

  - 加载-验证-准备-解析-初始化-使用-卸载

双亲委派机制

  - 类加载过程中选择类加载器过程源码判断
  - 破坏双亲委派机制
     - JavaSPI与Dubbo 增强SPI6以前的Thead Context Classloader和之后的ServiceLoader,父类加载器请求子类加载器加载类
     - OSGi模块化热部署
  - 为什么优先父类加载?
     - 避免重复加载,父类加载后,子类无需加载
     - 安全性,避免用户自定义类替换掉Java核心类

Java基础

String为何选用31做hashcode方法的乘数?

  - 31是个不大不小的质数,是优选乘数
  - 31可被jvm优化为31*i=(i<<5)-i
  - 质数太小,hash区分度不够,容易冲突质数太大,hash结果太大,int类型表示不了

Integer缓存

  - -128~127位缓存数据,超出该范围才会new Integer

类实例化顺序

  - 父类静态变量-父类静态代码块-子类静态变量-子类静态代码块-父类非静态变量-父类构造方法-子类非静态变量-子类构造方法

ArrayList扩容

  - size>>!+size 1.5倍扩容
  - 初始值为10,最大值为integer最大值

HashMap

  - 扩容获取比当前容量大的第一个2的指数
     - 因为get()通过hash&amp;(n-1)来获取
  - hash函数:key.hashcode()^(h>>>16)
     - 通过低位使得hash更加分散,保留高位信息
  - 链表树化 Treeify_Threshold=8,元素最少64
     - 理想情况,负载因为为0.75,链表个数出现概率服从泊松分布,长度为8概率为0.00000006,不足千万分之一
  - 红黑树退化为链表 UnTreeify_Threshold=6
     - 以8、7为退化阈值,会因为树化阈值为8,而反复在临界附近树化退化影响性能
  • HashMap与HashTable

Java并发

多线程开发良好实践

  - 给线程、线程池命名,方便问题追踪
  - 最小化同步范围
  - 优先使用volatile,而不是synchronized
  - 尽量使用更高层次并发工具,而非notify-wait实现线程通信
  - 优先使用并发容器
  - 考虑使用线程池

提高锁性能

  - 减少锁持有时间,降低锁冲突可能
     - 将可能冲突代码往后放,提高并发能力
  - 减小锁粒度,分割数据结构分段加锁
     - ConcurrentHashMap
  - 读写锁替换独占锁
     - 功能点分割
  - 锁分离
     - 插入数据和取出数据分离,阻塞队列
  - 锁粗化
     - 多个锁统一资源,一个整个锁

JVM对锁优化

  - 锁偏向
     - 之前获取锁的线程再次获取锁,无需任何同步操作
  - 轻量级锁
  - 自旋锁
     - 偏向锁-失败-获取轻量级锁-失败-膨胀为重量级锁-自旋-失败-线程挂起
  - 锁消除
     - 去除不可能存在竞争的锁,逃逸分析

volatile

  - JMM内存模型
  - 只保证可见性,不保证原子性
  - 避免指令重排序

CAS

  - rt.jar的sun.misc.Unsafe
  - CAS与wait应使用while做条件判断,避免虚假唤醒

Atomic类

  - AtomicInteger
  - AtomicReference
  - AtomicStampedReference

ThreadLocal

  - Thread类维护threadlocalmap<TheadLocal,Object>,其中ThreadLocal为弱引用,可能出现内存泄漏
  - 业务完成后finally块中手动threadLocal.remove(),防止内存泄漏
  - inheritaleThreadLocal可访问父线程变量,在thread中也维护对应的map

CopyOnWriteArrayList

  - 写时复制COW
  - 增删改使用独占锁,复制array副本,在副本做操作,完成后赋值给array
  - 获取与遍历取得为快照,存在弱一致性

LockSupport

Semapore

  - acquire、release 等待达到对应数量要求后进行后续逻辑,可重复使用

ContDownLatch

  - countDown、await 火箭发射,一次性使用,主线程等待所有检查完成

CyclicBarrier

  - await  public CyclicBarrier(int parties,Runnable barrierAction)

Guava的RateLimiter

  - 限流漏桶算法令牌桶算法

AQS AbstractQueuedSynchronizer

  - exclusiveOwnerThread:锁持有线程
  - head tail:Node双端队列,AQS队列
  - newCondition(),返回conditionObject对象,维护条件队列
  - tryAcquire与tryRelease需要子类实现,修改对应state状态并返回
  - lock方法拿不到锁,进入AQS队列等待;trylock拿不到直接返回false
  - xxxShared:共享锁实现
  - xxxInteruptibly:对中断进行响应,中断后抛出InterrupterException

ReentrantLock

  - state:可重入次数
  - 实现newCondition()
  - 实现AQS要求的各种方法
  - 内部有Sync的子类FairSync与NonFairSync,构造方法有是否公平参数

ReentrantReadWriteLock

  - state:高16位为读锁状态,低16位为写锁状态
  - 读读不阻塞,其余都阻塞
  - ReadLock不支持condition

队列

  - ConcurrentLinkedQueue 无界非阻塞队列  单链表+volatile+CAS
  - LinkedBlockingQueue 有界阻塞队列  单链表+reentrantlock
  - ArrayBlockingQueue 有界阻塞队列  数组+独占锁
  - PriorityBlockingQueue 优先级无界阻塞队列  最小堆实现有序
  - DelayQueue 无界阻塞延时队列

JDK并发容器

  - ConcurrentHashMap
  - CopyOnWriteArrayList
  - ConcurrentLinkedQueue
  - BlockingQueue
  - ConcurrentSkipListMap
  - Vector

线程池选择

  - 高三位表示线程池状态,低29位表示线程数
  - 构造器各参数含义

线程池大小设置

  - CPU密集型
     - N+1
     - CPU使用率高,开启过多线程,产生上下文切换开销
  - IO密集型
     - 2N+1
     - CPU使用率不高,在IO等待时处理别的任务
  - 混合型
     - 分别使用各自线程池
     - (线程等待时间/线程CPU时间+1)*CPU期望使用率*CPU核数

过期时间

  - 1s内任务数*单线程单个任务时间*80%(八二原则)

队列大小

  - 1s线程可处理任务数

自定义threadFactory的name前缀,方便问题追踪

拒绝策略

AbortPolicy(抛出异常)
CallerRunsPolicy(调用线程执行任务)
DiscardPolicy(丢弃,不抛异常)
DiscardOldestPolicy(丢弃最老任务,执行当前任务)

线程池任务提交过程

1、任务为空,抛出NPE
2、当前线程数是否小于核心线程数如小于开启新线程运行
3、否则判断线程池状态是否为Running,如果是添加任务到阻塞队列再次判断如果不是Running,从阻塞队列中删除,并执行拒绝策略否则如果线程池为空,新建线程执行
4、如果队列满,则新增线程执行,新增失败执行拒绝策略

Fork-Join框架

  - RecursiveTask
  - RecusiveAction

生产者-消费者模式

CompletableFuture

MySql

架构

Server层

  - 连接器:管理连接,权限校验
  - 分析器:词法语法分析
  - 优化器:执行计划生成,索引选择等
  - 执行器:操作引擎,返回结果
  - 缓存:8.0之后删除

引擎层

  - InnoDb
     - 支持事务
     - 有crash-safe能力(redo log)
     - 支持并发行锁
  - MyISAM
     - 不支持事务
     - 只支持表锁
  - Memory等

长链接内存耗用严重

  - Mysql执行过程中使用的内存都是在连接对象中的,断开连接后才会释放连接累积会导致内存占用严重,OOM,Mysql异常重启
  - 定期断开长连接
  - 5.7之后,执行mysql_reset_connection重新初始化连接,过程中不需要鉴权或重连

日志模块

  - redo log
     - 属于InnDB,通过它保证crash-safe能力
     - 内存结构类似于RingBuffer,环形链表大小固定,满了会将头部记录刷入库后擦除
     - 是物理日志,记录在某个数据页中做了什么修改
  - binlog
     - 属于Server层,所有引擎都可用
     - 文件记录,可追加写入不会覆盖
     - 记录逻辑操作,分为statement、row、mix等格式
  - 两阶段提交
     - redo log写入会分为prepare和commit阶段
     - 更新操作到内存后,写入redo log此时为prepare状态写入binlog成功后,redo log为commit状态,提交事务
     - 如果没有两阶段提交,redo log与binlog任何一个写入失败,binlog与redolog数据都不一致,会导致从库同步或者异常恢复导致数据不一致

ACID及隔离级别

  - 原子性、一致性、隔离性、持久性
  - 读未提交
     - 脏读:未提交变更其他事务可见
  - 读提交
     - 不可重复读:提交变更其他事务可见
  - 可重复读
     - 幻读:前后读取数量不一致
  - 串行化
     - 以上问题都解决,但并发性能差
  - 一致性视图及MVCC、undolog

索引

常见索引实现方式

  - Hash表
     - 只有等值查询场景,没有范围查询,Nosql引擎使用
  - 有序数组
     - 不适合更新,只适用静态存储引擎
  - 搜索树(N叉树)
     - 即可等值查询,也可范围查询

InnoDB实现

  - B+树
     - 主键索引叶子节点存储整行数据,非主键索引存储的主键(回表,到主键索引查询整行数据)
     - 叶子节点会存储链指针,实现快速区间查询
     - B+树为N叉树,一般去N为1200左右,树高为4时已到十亿级别,减少IO次数
  - 分类
     - 主键索引(聚簇索引)
     - 普通索引(非聚簇索引)
           - 唯一索引
           - 联合索引

联合索引创建原则

  - 调整顺序可减少索引,便优先考虑这个顺序
  - 高频查询选择交换顺序,争取占用内存最小
  - 最左前缀原则

覆盖索引

  - 索引覆盖了查询需求,减少回表,提升性能

索引下推

  - 索引遍历过程中,对索引包含字段先做判断,过滤不满足条件的

普通索引与唯一索引

  - 查询过程
     - 普通索引会查询到第一个不满足条件的记录为止
     - 查询到满足条件记录即停止
  - 更新过程
     - 如果记录数据页在内存中
           - 普通索引查询到对应记录,更新,执行结束
           - 唯一索引查询到对应记录,判断唯一性(需要回表查询整行),更新,执行结束
     - 如果记录数据页不在内存中
           - 普通索引将更新记录在changebuffer中,执行结束
           - 唯一索引将数据页读入内存,判断没有冲突,更新,执行结束
  - changebuffer    
     - 只适用于普通索引
     - 适用于读少写多的场景,页面写完后马上被访问的概率小
     - 减少磁盘访问
     - 唯一索引更新操作需要判断唯一性,读取数据页到内存,无法使用changebuffer
  - changebuffer与redolog
     - redo log 主要节省随机写磁盘的IO操作,转为一定数量的顺序写
     - changebuffer主要节省随机读磁盘的IO消耗
  - changebuffer merge过程

1、从磁盘读入数据页到内存
2、从changebuffer中找出该数据页的记录,一次应用,得到新版数据页(脏页)
3、写redo log 该redo log包含数据变更和changebuffer变更

  - 全局锁
     - flush tables with read lock 全库做逻辑备份
  - 表级锁
     - lock tables ... read/write
  - 行锁
     - 两阶段锁协议行锁是在需要的时候加上,并不是不需要立即释放,等事务结束才会释放
  - next-key lock
     - 行锁+间隙锁(可重复读条件下生效)实现
     - 解决幻读
     - 加锁规则
        - 原则1:加锁基本单位next-key lock,前开后闭
        - 原则2:查找过程中访问到的都加锁
        - 优化1:索引上的等值查询,给唯一索引加锁时,next-key lock退化为行锁
        - 优化2:索引上的等值查询。向右遍历且最后一值不满足条件,next-key lock退化为间隙锁
        - 一个bug:唯一索引上范围查询会访问到第一个不满足条件的值为止

count(*)

  - 遍历哪个索引树结果都一样,会找最小的索引树遍历
  - InnDB不记录表中记录数,需读出计数
  - count(字段)<count(主键id)<count(1)约等于count(*)

SQL逻辑相同性能差距巨大

  - 对索引字段做函数操作,可能破坏索引值得有序性,优化器决定放弃索引
  - 条件字段函数操作
     - select count(*) from tablea where month(t_modified) = 7;
  - 隐式类型转换
     - select * from tablea where tradeid = 110717=select * from tablea where CAST(tradeid as signed int) = 110717Mysql是将字符串转数字
  - 隐式字符编码转换
     - 两表字符集不同为utf-8和utf-8m64,两表字段做表关联,utf-8表做主表,关联条件变为CONVERT(tradeid USING utf-8m64)

慢sql分析

  - 配置慢sql条件收集慢sql
  - 使用explain分析索引使用情况,是否排序、临时表等
  - 分析索引创建是否合理且足够
  - 查询infomation-schema.optimize_trace,看优化器索引选择情况
  - 排除执行期间,是否有MDL、flush操作、大量循环执行sql等情况

分库分表

方式

  - 垂直拆分
     - 专库专用,单表拆分主子平行表
  - 水平拆分
     - hash取模
     - 某字段范围(id、时间戳等)

问题

  - 查询非分区键子弹,需要携带分区键查询
     - 维护查询字段与分区键的映射关系
  - 跨库join与count
     - 跨库join有分页数据不会太多,业务代码中处理
     - count使用redis或count表存储
  - group-range

迁移

  - 停机部署
  - 双写部署,基于业务
  - 双写部署,基于binlog

ID算法

  - UUID
  - SnowFlake
     - 1bit-41bit-10bit-12bit保留-时间戳-workerID(进程、机房id)-序列号(如超过4096序列号,sleep(1s)获取下一秒的)

sharding-sphere

数据结构与算法

Mybatis

Spring

Spring AOP

定义

  - 连接点:join point
  - 切点:point cut
     - execution匹配表达式

execution
within
bean
@annotation
表达式组合

  - 增强:advice
     - before advice
     - after returning advice
     - after throwing advice
     - after finally advice
     - around advice
  - AOP proxy
     - 一个类被AOP织入advice,生成包含原类和增强逻辑的代理类

JDK动态代理
Cglib动态代理(为一个未实现任何接口的类进行代理)

使用场景

1、HTTP接口鉴权
创建切面,获取request.cookie,进行鉴权处理
2、方法调用日志
before after returnning throwing advice打印对应日志
3、方法耗时统计
around advice 记录proceedingJoinPoint.proceed前后时间差
4、统一异常处理
@ControllerAdvice+@ExceptionHandler

AOP织入方式

  - 编译器织入
  - 类装载期织入
  - 动态代理织入

AspectJ

  - 采用编译器织入和类装载期织入,Spring采用动态代理
  - 还支持属性级别增强,Spring仅支持方法级增强
  - 使用方式
     - @EnableAspectJAutoProxy
     - <aop:aspectj-autoproxy/>

Spring Transaction

分类

  - 声明式事务
  - 编程式事务
     - 手动控制事务相关操作,开启提交回滚等

Spring如何与多种数据持久层做集成

1、Spring事务通过platformTransactionManager进行管理
2、PlatformTransactionManager的抽象子类AbstractPlatformTransactionManager有多种实现:JDBC、DatasourceTransactionManager

事务的相爱难相关属性

  - 传播级别:propagation
     - 常用Required、Required-New
  - 超时:timeout
  - 只读:readonly
  - 回滚:rollbackfor
  - 隔离属性:isolation

使用

  - 注解
     - @EnableTransactionManagement
     - @Transactional(rollbackfor=xxx.class,propagation=Propagation.Required,readonly=true,timeout=30,isolation=Isolation.Default})
  - xml
     - <tx:annotation-driven><bean id = "transactionManager" class = "xxx.DatasourceTransactionManager">    <property name = "dataSource" ref = "xxxDataSource"></bean>
     - <tx:advice = "mainTxAdvice" transaction-manager="transactionManager"   <tx:attributes>      <tx:method name = "query*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>   </tx:attributes></tx:advice><aop:config>   <aop:pointcut id = "businessServiceMethods" expression = "execution(* com.xxx.xxx.*.*(..))"/>   <aop:advisor advice-ref="mainTxAdvice" pointcut-ref = "businessServiceMethods"/></aop:config>

Spring IOC

实现机制

  - 工厂模式+反射

SpringBean生命周期

  - org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)加载对应资源类并注册beanDefination到容器(添加到beanDefinationMap与beanDefinationNames属性中)
  - beanFactory获取从缓存中获取bean
     - 三级缓存
           - 如何解决循环依赖
           - 两级缓存就可以解决循环依赖为什么设置三级缓存
  - 按对应scope进行bean实例化
     - 根据是否是单例、是否允许循环依赖、是否在创建中等条件判断是否放入三级缓存
     - 进行属性填充,处理循环依赖
  - 初始化
     - 调用对应aware接口的setxxx方法
     - 调用org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
     - 调用初始化方法,init-method/afterPropertiesSet
     - 调用org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(AOP在这里获取工厂生产的代理类)
  - 注册到IOC
     - 新创建的bean放入一级缓存并清除二三级缓存,调用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
  - 销毁
     - 容器关闭调用destory-method/afterPropertiesSet

AbstractApplicationContext.refresh(),刷新Spring应用上下文

  - 加锁startupShutdownMonitor
  - prepareRefresh() 时间参数等属性的初始化
  - obtainFreshBeanFactory() 旧BeanFactory销毁,创建新BeanFactory并加载beanDefination
  - prepareBeanFactory(beanFactory)
  - postProcessBeanFactory(beanFactory) 允许子类实现对应BeanFactoryPostProcessor增强逻辑
  - invokeBeanFactoryPostProcessors(beanFactory) 激活beanFactoryPostProcessor
  - registerBeanPostProcessors(beanFactory) 激活BeanPostProcessors
  - initMessageSource() 初始化上下文资源文件,如国际化
  - initApplicationEventMulticaster() 初始化广播器
  - onRefresh() 子类扩展初始化其他bean
  - registerListeners() 注册监听器,并发布前期事件
  - finishBeanFactoryInitialization(beanFactory) 初始化剩余单例bean
  - finishRefresh() 完成刷新并发布对应事件

SpringBoot

  • 构造在Spring之上的Boot启动器
  • 编码、配置、部署、监控变简单,未提供注册中心、微服务相关的解决方案

统一引用版本

  - 继承spring-boot-starter-parent项目
  - 导入spring-boot-dependencies项目依赖<type>pom</type><scope>import</scope>

不同环境部署

  - spring.profiles.active=xxx/ymal文件中spring.profiles:xxx+---分隔符
  - spring.profiles.active=xxx+application-{profiles}.propertites
  - 使用配置中心
  - maven profiles标签

配置加载顺序

  - 命令行参数
  - 命令行spring.application.json=xxx
  - JNDI参数
  - java系统变量
  - 操作系统环境变量
  - 外部带profile的application配置 application-{profile}.propertites/yaml
  - 内部带profile的application配置application-{profile}.propertites/yaml
  - 外部不带profile的application配置application.propertites/yaml
  - 内部不带profile的application配置application.propertites/yaml
  - 自定义@Configuration中的@PropertySource

配置方式

  - xml
  - 注解
  - Java Config

自动装配

  - @EnableAutoConfiguration@Import(AutoConfigurationImportSelector.class)org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
  - 获取@EnableAutoConfiguration注解元信息
  - 通过SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,getBeanClassLoader())获取META-INF/spring.factories资源内的实现类
  - 移除重复的实现类
  - 通过注解的exclude、excluedename、spring.autoconfiguration.exclude获取需要排除的实现类集合
  - 检查排除类的合法性
  - 移除排除类
  - 通过SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader)获取过滤实现类,并执行过滤逻辑OnBeanConditionOnClassConditionOnWebApplicationCondition
  - 触发自动装配事件,AutoConfigurationImportEvent

读取配置方式

  - @Value
  - @ConfigurationPropertites(prefix="xxx")
  - @PropertySource(value="config/db-config.propertites")+@Value
  - @PropertySource(value="config/db-config.propertites")+@CondigurationPropertites
  - @Autowiredprivate Enviroment env

启动时运行代码

  - 实现ApplicationRunner/CommandLineRunner接口,并注册bean

自定义Starter

  - 命名规则:{module-name}-spring-boot-starter

1、添加spring-boot-starter基础依赖
<optional>true</optional>,不传递依赖,不与引包项目版本冲突
2、创建对应配置类xxxAutoconfiguration,并添加@Configuration
3、将其全类名添加到META-INF/spring.factories文件的EnableAutoConfiguration下的实现类
4、打包构建,项目添加xxx-spring-boot-starter依赖,可以正常获取自动加载相关bean

Redis

优点

  - 有丰富的数据类型
  - 支持数据持久化
  - 支持实施恢复
  - 支持集群cluster
  - 使用单线程的IO多路复用
  - 支持发布订阅、lua脚本、事务等
  - 删除策略同时使用惰性删除和定期删除

为什么使用redis实现缓存

  - 高性能
     - 内存访问速度快
  - 高并发
     - 单机redis支持QPS远高于mysql

常用数据结构

  - String 动态字符串,可保存文本、二进制、int等,常用于计数器
  - list 链表/压缩列表,双向链表实现
  - hash hashtable/压缩列表,适合存储档案信息
  - set intset/hashtable 存储不可重复对象,支持并集、交集、差集计算,常用于统计共同关注共同喜欢等
  - zset ziplist/skiplist 有序排列,常用于排行榜、在线用户、弹幕等
  - bitmap bit表示某元素状态,常用于用户签到、活跃用户、行为统计等

单线程模型

  - 基于Reactor模式开发的文件事件处理器,通过IO多路复用监听客户端的大量连接
  - 大量socket-IO多路复用-任务队列-文件事件分排气-分发给连接应答处理器、命令请求处理器、命令回复处理器

持久化机制

快照持久化RDB

  - 类似binlog,save 60 1000  60s1000个key发生变化触发BGSAVE生成快照

AOF

  - 类似redolog,只追加文件,appendonly yes appendonly everysec 每秒同步一次

混合持久化

  - aof-use-rdb-permble
     - RDB以一定频率进行,两次之间使用AOF记录增量记录

AOF重写

  - 压缩aof文件
  - 后台子线程bgwriteof完成,避免阻塞主线程
  - fork线程子线程是需要拷贝处理大量数据,未完成前是阻塞的
  - fork操作本身也是阻塞主线程的
  • RDB是二进制,AOF是命令,磁盘IORDB效率高,从库恢复,RDB效率高于AOF

redis事务

  - MULTI EXEC DISCARD WATCH
  - EXEC之前,会将所有命令放入队列,一起执行
  - Redis不满足回滚即原子性和持久性

问题

缓存穿透

  - 热点数据缓存失效
  - 对于频繁访问的任店数据。不设置过期时间

缓存击穿

  - 大量请求key不存在,直接访问数据库
  - 缓存物料key,避免数据库压力过大
  - 布隆过滤器
     - 多个hash函数,结果存于二进制位上,后续字符串进行相同多个hash,如返回结果位数据都为1,则存在
     - 不存在一定不存在,存在不一定存在

缓存雪崩

  - 同一时间缓存大面积失效,请求直接访问数据库
  - 采用redis集群,避免单机流量过大不可用
  - 限流,非核心业务返回降级信息,核心业务查询数据库
  - 设置不同的失效时间,加随机数
  - 缓存永不失效

保证缓存与数据库数据一致

旁路缓存模式

  - 先更新DB,删除缓存
  - 如缓存删除失败,添加对应的重试机制,重试后仍不可用存入队列,待服务可用时删除

延时双删

  - 先删缓存,更新DB,sleep线程读取数据写入缓存的最大时间后再删除缓存

主从同步

  - 建议使用主从从结构,将生成RDB的压力分散到一级从库上

哨兵机制

监控

  - 周期性给所有主从发送ping命令,看是否在线,如未返回,则认为主观下线
  - 超过quorum值哨兵认为主观下线,则标记为客观下线

选主

  - 一定的筛选条件和规则打分,得分最高的为主库
  - 筛选条件:主库的运行状态,及以往的网络连接状态,如从库多次与主库断联超过一定阈值则排除
  - 规则:

1、优先级高的从库得分高,slave_priority
2、和旧主库同步程度最接近的得分高
3、id小的得分高

通知

  - 基于Pub/Sub机制建立哨兵集群
     - 主库上"__sentinel__:info"频道
  - 基于INFO命令返回从库列表,哨兵建立与所有从库连接
  - 基于Pub/Sub实现客户端与哨兵、哨兵与哨兵、哨兵与从库的连接
  - 哨兵通过leader选举出一个哨兵执行主从切换
     - 超过quorum并超过半数才能当选,不满足永远选不出

哨兵服务配置应保持一致

集群与数据切片

  - RedisCluster
     - CRC32%1024获取slot编号
  - Codis
     - CRC16%16384获取槽编号

GEO

实现消息队列

  - list实现
  - streams实现

Redis秒杀场景

要求:瞬时并发访问量高,读多写少

阶段

  - 活动前
     - 商品详情页静态化资源缓存
  - 活动开始
     - 库存查验、库存扣减(保证原子性,在redis处理避免请求压力大,更新不及时导致超售)
     - 订单处理(数据库处理,真正下单请求压力小,添加重试机制保证成功)
     - 结束(请求压力小,订单追踪等)

关键环节

  - 请求拦截流控,恶意攻击黑名单、流控等
  - 库存信息过期时间处理,防止缓存击穿不设置过期时间
  - 订单异常处理,添加重试机制
  - 建议:秒杀商品存量信息、分布式锁信息单独实例保存,与日常业务区分,避免相互影响

库存查验、库存扣减原子性保证

  - 原子操作实现
     - 单个redis保存商品信息hash,lua脚本实现库存查验和扣减
     - 分布式锁
  - 子主题

无锁的原子性操作

  - redis多个命令实现为一个操作:Incr/Decr
  - 多个命令写入Lua脚本,原子性方式执行(避免不需要并发的操作写入,通过参数传递)
  - 实现分布式锁
     - 加锁使用set key value NX PX/EX expiretime
     - 解锁通过Lua命令获取唯一id并判断相等后释放锁
  - 基于多节点实现:RedLock

Dubbo

架构图

  - Consumer/Provider/Registry/Monitor
  - 分层架构
     - Service/Config/Proxy/Registry/Cluster/Monitor/Protcol等

增强SPI

  - ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(DubboProtocol.NAME);Compiler compiler =                ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()动态编译生成对应的适配器类,并根据@SPI选择对应的实现类

zk目录结构

  - Root-Service-Type-URLDubbo-com.xxx.xxx.xxxService-Providers-127.0.0.1:20881

zk实现分布式锁

  - 临时顺序节点

各属性的含义及使用

SpringCloud

  - Eureka
  - Zuul
  - Hystrix
  - Ribbon
  - Feign
  - Config
  - Bus

分布式事务

  - sagas
  - tcc
  - 其它

Netty

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。