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