1、本地缓存、堆外内存off-heap、
3、redis (jedis cluster的sharding jedisCluster读写 lettuce读写分离)
4、多级缓存 (不一致 本地缓存更新策略 穿透思考)
5、扣库存问题(行锁 redis乐观锁 redis+lua 同步给db alisql)
一、本地缓存
缺点:占用系统内存oom、有一致性问题, 分布式缓存有单点问题时,结合用
例:hibernate缓存用Ehcatch,对jdbc封装
二、堆外内存off-heap
避免热点数据,占用系统内存
1、优点: 1)减少gc次数 降低暂停时间 2)扩展和使用更多内存 3)省去物理内存和heap间复制
2、淘宝jdk实现: 生命周期长对象从heap 内移外, gc不能管理。ps:逃逸分析技术成熟,也可在栈上分配
3、如何使用物理内存 :可限制容量,超出oom
4、何时释放
DirectByteBuffer 对象被gc时,堆外内存一起释放
三、redis
本地缓存无法水瓶扩容,cluster容量可无限延伸
1、基于jedis客户端操作:
2、cluster的sharding
1)hash算法:扩容,历史数据要全部迁移
2)一致性hash算法: 无需全部迁移,但node少时,会 数据倾斜 ,在节点ip/主机名后增加 编号, 让其均匀分布
3)分槽:介于两者之间,slot固定,永远被路由到同一个
3、基于jedisCluster的读写操作
可指定单个节点
4、基于lettuce客户端的读写分离
jedis不支持集群的读写分离,lettuce可同步/异步,底层基于nio模型的netty
优点:水平扩容, 无限延伸,不用手动调整连接吃maxTotal,避免本地缓存穿透
四、多级缓存
本地:共享一个进程内的heap,存热点数据
cdn:存商品图片、视频
1.不一致问题
因时差造成,允许 脏读 ,扣库存时显示售完
2.本地缓存更新策略(主/被动)
1)被动更新: 过期则回源,保证单线程,避免失效大量请求,穿透引起雪崩
guava
2)主动更新:修改后,异步写到队列,更新缓存
3、缓存穿透思考
大促前从运营那熟悉热点key,放在配置中心内
五、扣库存问题
1、行锁
表中version,流量高峰引起大量线程竞争行锁,影响db tps,rt上升,引起雪崩
2、redis乐观锁
(1)基本命令
1) watch :监视key(可多个),事务发生前key改 变 ,事务则 失败
2) multi :将事务内多条 命令 ,按先后顺序 放进队列
3) exec :最后原子性 提交执行
(2)watch内部实现原理
客户端如何感知:每个db都是redis.h/redis.db结构表示,起内部存了watch_keys(被监视的目标key)
multi.c/touchWatchKey函数 对watch_keys字典检查
,有修改的key标记为redis_dirty_cas,后续提交事务发生中断
(3)jedis乐观锁
(4)缺省路由实现
缺点:并发越高,watch碰撞概率越大,解决:读写合成一条命令,嵌入式lua脚本
3、redis+lua (性能最好)
redis2.6之后,内置lua解释器,但 eval / evalsha 命令执行时,redis把它当成 单条在执行
(1)lua脚本
(2)eval / evalsha 嵌入redis执行
1) eval: 重复向redis传相同lua脚本,网络开销大
2) evalsha :从redis获取已缓存好的脚本,节省。但用 evalsha 前,先用script load命令加载
lua到缓存 中,等redis会等sha1 校验码 ,后续用时,传校验码即可
用evalsha执行lua脚本
4、库存变化后如何同步给db
synchronized:竞争激烈用更好
cas:https://www.jianshu.com/p/6d1f3b2a3ac0 会因无限重试,占用过多cpu
5、AliSQL
对热点数据做hash,收集所有库存一次性提交(设置阈值)