分布式事务

面试被问Redis和zk两种分布式锁的对比

一、基于数据库实现分布式锁1.悲观锁利用select…where…forupdate 排他锁注意:其他附加功能与实现一基本一致,这里需要注意的是“wherename=lock”,name字段必须要走索引,否则会锁表。有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。2.乐观锁所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程

一、基于数据库实现分布式锁

1. 悲观锁

利用select … where … for update 排他锁

注意: 其他附加功能与实现一基本一致,这里需要注意的是“where name=lock ”,name字段必须要走索引,否则会锁表。有些情况下,比如表不大,mysql优化器会不走这个索引,导致锁表问题。

2. 乐观锁

所谓乐观锁与前边最大区别在于基于CAS思想,是不具有互斥性,不会产生锁等待而消耗资源,操作过程中认为不存在并发冲突,只有update version失败后才能觉察到。

我们的抢购、秒杀就是用了这种实现以防止超卖。

通过增加递增的版本号字段实现乐观锁

二、基于缓存(Redis等)实现分布式锁

1、官方叫做 RedLock 算法,是 redis 官方支持的分布式锁算法。

这个分布式锁有 3 个重要的考量点:

  • 1.互斥(只能有一个客户端获取锁)

  • 2.不能死锁

  • 3.容错(只要大部分 redis 节点创建了这把锁就可以)

2、下面是redis分布式锁的各种实现方式和缺点,按照时间的发展排序

1、直接setnx

直接利用setnx,执行完业务逻辑后调用del释放锁,简单粗暴

缺点:如果setnx成功,还没来得及释放,服务挂了,那么这个key永远都不会被获取到

2、setnx设置一个过期时间

为了改正第一个方法的缺陷,我们用setnx获取锁,然后用expire对其设置一个过期时间,如果服务挂了,过期时间一到自动释放

缺点:setnx和expire是两个方法,不能保证原子性,如果在setnx之后,还没来得及expire,服务挂了,还是会出现锁不释放的问题

3、set nx px

redis官方为了解决第二种方式存在的缺点,在2.8版本为set指令添加了扩展参数nx和ex,保证了setnx+expire的原子性,使用方法:set key value ex 5 nx

缺点:

  • 如果在过期时间内,事务还没有执行完,锁提前被自动释放,其他的线程还是可以拿到锁

  • 上面所说的那个缺点还会导致当前的线程释放其他线程占有的锁

4、加一个事务id

上面所说的第一个缺点,没有特别好的解决方法,只能把过期时间尽量设置的长一点,并且最好不要执行耗时任务

第二个缺点,可以理解为当前线程有可能会释放其他线程的锁,那么问题就转换为保证线程只能释

原创不易,完成人机校验,阅读全文

相关推荐