分布式系统-分布式锁

  分布式锁,接触过分布式系统的,一定都听过。那么,它解决了什么问题?怎么做一把好用安全的分布式锁?

  解决场景:

  你是否经常碰到过类似这样的场景,比如你有一个生成唯一序列号的服务,逻辑如下:

  1. 查询数据库保存的序列号最新值seq。

  2. 将seq+1刷新到数据库。

  3. 将seq作为返回值返回给调用方。

  工作经验丰富的同学,一定会想,如果出现并发调用我这个服务的场景,就会出现重复的seq,这可怎么办呢?你一定想到了,加锁。于是,有的同学就会兴致勃勃的加上一把靓丽的本地锁(比如java的synchronized)。当功能上线后,同学信心满满。直到某一天,业务部门找上来,发现有两个一样的seq,同学满腹疑虑的一顿排查,最后恍然大悟:咱需要的是一把分布式锁啊!

  对,这就是分布式锁的一个应用场景。

  再举一个例子,你在商品系统写了一个扣减库存的服务。业务写完后,你一定很担心,如果由于并发,库存扣减后数量不对,或者出现负数,那可糟糕了!

  对,这也是分布式锁要解决的一个典型场景。

  最后再举一个例子,你要做一个秒杀活动,为了并发安全,是不是得做锁呢?

  分布式锁,可分为阻塞分布式锁、非阻塞分布式锁。具体的实现技术工具,有db、redis、zookeeper。

  在我列举的第一个第二个案例中,需要用到阻塞分布式锁,可以通过select * from table where id=? for update,也就是数据库悲观锁来实现。悲观锁能解决并发安全的问题,但也有一个致命的问题,那就是容易导致死锁。比如节点A在获取到悲观锁后,突然宕机,此时节点B再来获取悲观锁时,就会一直阻塞,系统极有可能因此而陷入假死状态。

  第二个案例中,其实也不需要阻塞分布式锁,或者说只要控制不超卖即可,对于这种场景,其实只需要使用乐观锁即可。update table set num=num-1 where id=? and num>=1;  如果更新数量为0,则说明即将超卖,要将报错文言提示给前端。

  最后一个例子,其实是需要一把非阻塞分布式锁。redis,就是一个很不错的选择。

  redis的setnx,是一个原子命令,可以用来做锁。具体逻辑如下:

  1. 获取锁 redis.set("NX","PX",key,value,expireTime);

  2. 获取成功,则执行业务,业务执行完后,再释放锁,为了避免释放错锁,del前需要判断value是否一致。判断和del操作需要做成原子操作,可使用lua脚本来实现。

  3. 获取失败,则直接返回系统繁忙。

  分布式锁,是分布式系统一个很关键的技术,在实际的分布式系统中,有很广泛的应用场景。总体而言,要避免使用阻塞分布式锁,尽量使用非阻塞分布式锁;要少用悲观锁,多用乐观锁。

posted @ 2023-11-17 00:34  天NULL  阅读(16)  评论(0)    收藏  举报