假如有这么一个场景,redis缓存了一个购物网站的商品库存,同时有两个用户对A商品下单购买了,分别都从redis读取了库存量,然后减一,又分别存回了数据库,此时商品库存应该减2,但是只减少了 1。
对此,多个客户端并发访问redis时,需要进行一定的访问控制
1:像是简单的数值加减操作,可以调用redis提供的一些原子操作INCR/DECR进行增值减值操作
2:将多个不是原子操作的的操作合并在一起中写入LUA脚本执行,redis能保证lua脚本执行时是原子性的
由于分布式系统无法像运行在单个JVM的应用程序那样共用共享内存中的变量实现加锁操作
所以分布式锁需要用到一个共享的存储系统
同时由于高并发的分布式系统中 并发访问量大 对此获取锁与释放锁都需要性能比较高
且次共享存储系统必须是高可靠的 防止单台机器发生故障 分布式系统便无法利用锁来实现并发控制
redis便可以很好的实现这些特性
1:redis可以支持多个客户端的连接,是一个共享的key value数据库
2:redis支持高性能的读写 且redis多于多个客户端的请求是顺序执行命令的
3:redis也可以进行集群部署 部署主节点的备库 实现高可用
一般通过
SET key value EX/PX NX 命令进行加锁操作
NX能够保证key不存在则创建 返回OK(加锁成功) key存在则返回nil(获取锁失败)
EX/PX是设置过期时间 防止某个客户端加锁之后 执行业务逻辑时发生异常退出 不进行释放锁操作
使得其它客户端一直获取不到锁
每个客户端加锁时 value都设置成一个客户端唯一标识
防止客户端A加锁成功之后 客户端C执行DEL命令 将锁释放
所以每次释放锁之前 先比对一下value是否为本客户端唯一标识 是代表自己获取到了锁 可以释放
释放锁操作同样要保证原子性,因为设计三个步骤
1:读取key的value
2:比对value和自己的唯一标识
3:删除key
所以释放锁操作可以写入LUA脚本执行保证原子性
分布式锁算法RedLock
每次加锁时 向多个Redis实例依次发生加锁命令,满足下列两个条件加锁成功
1:超过半数的实例加锁成功
2:执行加锁的总耗时没有超过锁的有效时间
优势
1:减少网络开销 一次向redis服务器发送多个请求
2:保证脚本多个操作整体的原子性
3:复用,其它客户端可以复用服务器执行过的lua脚本