目前很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。
基于 CAP理论,任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。
我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。通常大家都会采redis做分布式锁,但这样就可以高枕无忧了吗?
分布式与单机情况下最大的不同在于其不是多线程而是多进程,而数据只有一份(或有限制),也就是说单机的共享内存已解决不了一致性写问题,此时需要利用锁的技术控制某一时刻修改数据的进程数。
当在分布式模型下,分布式锁还是可以将标记存在内存,只是该内存不是某个进程分配的内存而是公共内存(Redis、Memcache)。至于利用数据库、文件等做锁与单机的实现是一样的,只要保证标记能互斥就行。
如果按分布式该具备的特性来逐条匹配,特别是高可用(存在单点)、高性能是硬伤
一般都使用 setnx(set if not exists) 指令,只允许被一个客户端占有,先来先得, 用完后再通过 del 指令释放。
如果中间逻辑执行时发生异常,可能会导致 del 指令没有被执行,这样就会陷入死锁,怎么破?
对,给锁加个过期时间(即使出现异常也可以保证几秒之后锁会自动释放)!
但setnx 和 expire 之间redis服务器突然挂掉,怎么破?
其实该问题的根源就在于 setnx 和 expire 是两条指令而不是原子指令。为了解决这个疑难,Redis 开源社区涌现了一堆分布式锁的 解决方案。为了治理这个乱象,Redis 2.8 版本中加入了 set 指令的扩展参数,使得 setnx 和 expire 指令可以一起执行,彻底解决了分布式锁的乱象。
总之,setnx 和 expire 组合就是分布式锁的奥义所在。
如果在加锁和释放锁之间的逻辑执行的太长,超出了超时限制,怎么破?
也就是说第一个线程持有的锁过期了但临界区的逻辑还没有执行完,这个时候第二个线程就提前重新持有了这把锁,导致每个请求执行临界区代码时不能严格的串行执行。
Redis 的分布式锁不能解决超时问题,建议分布式锁不要用于较长时间的任务。
稍微安全一点的方案是为 set 指令的 value 参数设置为一个随机数,释放锁时先匹配随机数是否一致,一致的话再删除 key,这是可以确保当前线程占有的锁不会被其它线程释放,但是并不能解决锁被redis服务器自动释放的。
How to do distributed locking以上为个人经验,希望能给大家一个参考,也希望大家多多支持。