分布式:分布式id、分布式锁
分布式id
使用改造的雪花算法64位: 0/1, 41位的时间戳差值, 10位机器号, 12位自增模型
1+41+10+12=64
分布式锁
分布式数据库锁
这是最根本的基于数据库做分布式锁:数据库的乐观锁
悲观锁:数据库级别加锁
乐观锁:代码逻辑上控制, 没有任何锁和死锁, 一般用每次插入更新操作,累加int或者timestamp作为version字段
场景:商品库存扣减时,尤其是在秒杀、聚划算这种高并发的场景下,若采用version号作为乐观锁,则每次只有一个事务能更新成功,业务感知上就是大量操作失败。悲观锁的缺点:高并发的数据库级别的锁行锁表噩梦啊举例子:
更新前查询商品id为100, select kucun, version from kucun_table where id=100;
此时version=10, 也就是如果再更新version不变就说明是正常的,没有被修改过, 如果被修改过就说明有其他分布式程序已经修改了, 这次更新不作数。
更新:update kucun_table set kucun=kucun-1 where id=100 and version=10 and inventory-1>0;
分布式redis锁
要把数据库东西一次性搞到缓存里, 缓存里扣减完毕同步到数据库。
基于redis:
在这种场景(主从结构)中存在明显的竞态:
客户端A从master获取到锁,
在master将锁同步到slave之前,master宕掉了。
slave节点被晋级为master节点,
客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。安全失效!
EX second :设置键的过期时为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。XX :只在键已经存在时,才对键进行设置操作。
设置秒数, 毫秒数, 如下设置5秒
NX 代表只在键不存在时,才对键进行设置操作。
PX 5000 设置键的过期时间为5000毫秒。
为什么设置过期时间:服务器宕机,锁就永远删除不了了, 因此就是自动释放锁。
uid就是锁
value客户端生成的唯一的字符串, 唯一的说明是任何一个自己设置的锁。nx, px连续使用。
以下可以理解为这个东西不存在的时候开始设置为5秒过期, 存在了就不管,这就是加锁, 加锁就是给key一个值
SET key value NX PX 5000
删除锁就是解锁, 当前锁的字符串和传入的字串相等就解锁
这个value还是类似于数据库的version, 而这个value的生存时间不会很长,不需要存储, 往往是需要的处理的瞬间,在设置的读取的同时使用, 过期作废
而这个key的场景比如是减去商品id的库存,比如商品id是唯一的, product_id=168, 那么key可以设置成168_inventory
SET 商品idkey, uuid字符串 NX PX 5000
- 3个进程:A和B和C,在执行任务,并争抢锁,此时A获取了锁,并设置自动过期时间为10s
- A开始执行业务,因为某种原因,业务阻塞,耗时超过了10秒,此时锁自动释放了
- B恰好此时开始尝试获取锁,因为锁已经自动释放,成功获取锁
- A此时业务执行完毕,执行释放锁逻辑(删除key),于是B的锁被释放了,而B其实还在执行业务
- 此时进程C尝试获取锁,也成功了,因为A把B的锁删除了。
问题出现了:B和C同时获取了锁,违反了互斥性!如何解决这个问题呢?我们应该在删除锁之前,判断这个锁是否是自己设置的锁,如果不是(例如自己 的锁已经超时释放),那么就不要删除了。所以我们可以在set 锁时,存入当前线程的唯一标识!删除锁前,判断下里面的值是不是与自己标识释放一 致,如果不一致,说明不是自己的锁,就不要删除了。
分布式zookeeper锁
基于zookeeper:
这个简单,zookeeper就是有序节点的znode创建是唯一的, 一个在创建的时候其他会创建失败, 因此在创建成功的时候,执行数据库扣减事务,然后删除节点即可
防止超卖:
update item set quantity=quantity-1 where id=1 and quantity-1>0;

浙公网安备 33010602011771号