面试总结
2022-06-02 16:15:26 0 举报
AI智能生成
中高级Java面试笔记总结
作者其他创作
大纲/内容
网络协议
网络分层模型
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引用的对象
1、虚拟机栈中引用的对象,被调用方法的参数、局部变量、临时变量等
2、方法区的类静态属性引用的对象
3、方法区中的常量引用的对象
4、本地方法栈JNI引用的对象
垃圾回收算法
标记-清除
内存碎片、导致内存不连续、使用率不高导致无法分配触发GC
效率随对象数量增长而降低
标记-复制
只使用一半内存,不过没有内存碎片
复制在存活对象较多时复制效率低
标记--整理
没有内存 碎片
标记整理效率不高
垃圾回收器
ParNew+CMS
初始标记与重新标记会STW
G1
ZGC
常用的调优命令
Xms=Xmx、PermSize=MaxPermSize,避免内存扩展
子主题
类加载过程
加载-验证-准备-解析-初始化-使用-卸载
双亲委派机制
类加载过程中选择类加载器过程源码判断
破坏双亲委派机制
JavaSPI与Dubbo 增强SPI
6以前的Thead Context Classloader和之后的ServiceLoader,
父类加载器请求子类加载器加载类
6以前的Thead Context Classloader和之后的ServiceLoader,
父类加载器请求子类加载器加载类
OSGi模块化热部署
为什么优先父类加载?
避免重复加载,父类加载后,子类无需加载
安全性,避免用户自定义类替换掉Java核心类
Java基础
String为何选用31做hashcode方法的乘数?
31是个不大不小的质数,是优选乘数
31可被jvm优化为31*i=(i<<5)-i
质数太小,hash区分度不够,容易冲突
质数太大,hash结果太大,int类型表示不了
质数太大,hash结果太大,int类型表示不了
Integer缓存
-128~127位缓存数据,超出该范围才会new Integer
类实例化顺序
父类静态变量-父类静态代码块-子类静态变量-子类静态代码块-父类非静态变量-父类构造方法-子类非静态变量-子类构造方法
ArrayList扩容
size>>!+size 1.5倍扩容
初始值为10,最大值为integer最大值
HashMap
扩容获取比当前容量大的第一个2的指数
因为get()通过hash&(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使用率高,开启过多线程,产生上下文切换开销
CPU使用率高,开启过多线程,产生上下文切换开销
IO密集型
2N+1
CPU使用率不高,在IO等待时处理别的任务
CPU使用率不高,在IO等待时处理别的任务
混合型
分别使用各自线程池
(线程等待时间/线程CPU时间+1)*CPU期望使用率*CPU核数
过期时间
1s内任务数*单线程单个任务时间*80%(八二原则)
队列大小
1s线程可处理任务数
自定义threadFactory的name前缀,方便问题追踪
拒绝策略
AbortPolicy(抛出异常)
CallerRunsPolicy(调用线程执行任务)
DiscardPolicy(丢弃,不抛异常)
DiscardOldestPolicy(丢弃最老任务,执行当前任务)
线程池任务提交过程
1、任务为空,抛出NPE
2、当前线程数是否小于核心线程数
如小于开启新线程运行
如小于开启新线程运行
3、否则判断线程池状态是否为Running,
如果是添加任务到阻塞队列
再次判断如果不是Running,从阻塞队列中删除,并执行拒绝策略
否则如果线程池为空,新建线程执行
如果是添加任务到阻塞队列
再次判断如果不是Running,从阻塞队列中删除,并执行拒绝策略
否则如果线程池为空,新建线程执行
4、如果队列满,则新增线程执行,新增失败执行拒绝策略
Fork-Join框架
RecursiveTask
RecusiveAction
生产者-消费者模式
CompletableFuture
MySql
架构
Server层
连接器:管理连接,权限校验
分析器:词法语法分析
优化器:执行计划生成,索引选择等
执行器:操作引擎,返回结果
缓存:8.0之后删除
引擎层
InnoDb
支持事务
有crash-safe能力(redo log)
支持并发行锁
MyISAM
不支持事务
只支持表锁
Memory等
长链接内存耗用严重
Mysql执行过程中使用的内存都是在连接对象中的,断开连接后才会释放
连接累积会导致内存占用严重,OOM,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状态,提交事务
写入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) = 110717
Mysql是将字符串转数字
=select * from tablea where CAST(tradeid as signed int) = 110717
Mysql是将字符串转数字
隐式字符编码转换
两表字符集不同为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)获取下一秒的)
保留-时间戳-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>
<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>
<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>
<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
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
获取@EnableAutoConfiguration注解元信息
通过SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,getBeanClassLoader())
获取META-INF/spring.factories资源内的实现类
获取META-INF/spring.factories资源内的实现类
移除重复的实现类
通过注解的exclude、excluedename、spring.autoconfiguration.exclude获取需要排除的实现类集合
检查排除类的合法性
移除排除类
通过SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader)获取过滤实现类,并执行过滤逻辑
OnBeanCondition
OnClassCondition
OnWebApplicationCondition
OnBeanCondition
OnClassCondition
OnWebApplicationCondition
触发自动装配事件,AutoConfigurationImportEvent
读取配置方式
@Value
@ConfigurationPropertites(prefix="xxx")
@PropertySource(value="config/db-config.propertites")+@Value
@PropertySource(value="config/db-config.propertites")+@CondigurationPropertites
@Autowired
private Enviroment env
private 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小的得分高
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选择对应的实现类
Compiler compiler =
ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension()
动态编译生成对应的适配器类,并根据@SPI选择对应的实现类
zk目录结构
Root-Service-Type-URL
Dubbo-com.xxx.xxx.xxxService-Providers-127.0.0.1:20881
Dubbo-com.xxx.xxx.xxxService-Providers-127.0.0.1:20881
zk实现分布式锁
临时顺序节点
各属性的含义及使用
SpringCloud
Eureka
Zuul
Hystrix
Ribbon
Feign
Config
Bus
分布式事务
sagas
tcc
其它
Netty
0 条评论
下一页