setnx就可以实现加锁,del实现解锁,但是这样不具备原子性,存在无法释放的可能。
因此可以使用在加锁时增加过期时间命令,做到原子性的加锁并且可以自动释放。
一些问题:
解决:
可重入性需要自己开发,安全性方面可能丢失锁(redis主从复制)
可以了解一下基于多节点的高可用分布式锁的算法 RedLock。
加入缓存就不可避免的引入数据一致性的问题,所以在这里讨论,先说结论,我在项目上使用的是旁路缓存模式——
读策略:
从缓存中读取数据;如果缓存命中,则直接返回数据;如果缓存不命中,则从数据库中查询数据;查询到数据后,将数据写入到缓存中,并且返回给用户。
写策略:
更新数据库中(HBASE,Redis)的记录;删除缓存记录。
然后探讨一下各种模式的优缺点以及相应的解决方案。
再来对比一下几种策略:
先更新数据库,再更新缓存
先更新数据库,再删除缓存
先更新缓存,再更新数据库
先删除缓存,再更新数据库
第一个问题是这个过程不具备原子性,可能存在
a改库
b改库
b更新缓存
a更新换粗
导致出现了更改丢失的情况,同时每次更新db都去更新缓存,如果是写多读少的情况下,无疑是浪费感情。
问题:
缓存失效
a去查数据库
b改库
b删缓存
a放置旧数据缓存
但是细想就会发现这个事情的概率比较低,因为并发的情况下,写库要比读库的耗时更长,正常情况下应该是b执行删缓存放到最后。
另外更新可以看做先删后加,删除就只有一步,相当于一个惰性处理,即懒加载的思想,只有用到的时候才去加在,因为缓存的成本是较高的,尽量需要保证会被访问到才缓存。
遗留问题,如果删缓存失败也会导致存在数据不一致问题,这种情况下可以依赖过期时间(缓存尽量不要设置为永久,如果能永久的我相信本地缓存或数据库更合适一些),允许一段时间的脏数据来达到最终一致性。
或者再引入一个中间件,删除缓存失败的时候入mq,随机时间后进行重试(不推荐,搞这么复杂还是别用缓存了)
同样属于双更模式,双更模式带来的不确定性比较大,并非原子操作,不推荐。
可能删完马上又有读请求过来查库读到了脏数据放入缓存中,毕竟读快于写,不推荐