持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情
1 业务场景
1.1 秒杀场景
当10个人同时抢2部秒杀的手机的时候,若是10个人都同时下单,获取库存的时候,都从数据库读到了还有两台,然后也就都下单成功了,执行之后的减库存业务的时候,就将库存减到了负数。这就发生了超卖的现象。
1.2 抢单
当一个用户在一个打车平台发布了用车信息,然后两个司机都看到了这个订单。抢的时候,判定状态都是没有人接单,然后又都抢到了,当更新订单数据的时候,一号司机先更新,二号司机再更新,都以为自己抢到了订单。这也是一个错误的逻辑。
1.3 单体架构
当然,这个问题如果在单体架构上,就是说每个节点只有一个服务器,那么加一个jvm锁就可以解决问题。但是分布式架构上,每个节点不只是一个server,每个server都有可能拿到这个jvm锁,所以在分布式架构上,jvm锁是解决不了并发超卖的问题的。
2 分布式锁思路分析
当服务A和服务B处理同一段业务的时候,A和B相互并不知道对方正在做解决相同的问题,也并不能控制对方不解决这个问题。这个时候,需要来一个中介-服务C。
特别的是:这个中介C只能顺序执行业务,他不会同时服务于A和B,只会谁先来服务谁。
当服务A执行到锁之后,告诉中介,我拿到锁了,别让其他人拿锁了。然后中介C就记录这个状态。当B来获取锁的锁的时候,中介C告诉B,这个锁已经被人拿走了,你得等他释放锁才行。然后B就在等,直到A释放锁,B获取之后,继续执行业务。
总体思路是,A来执行一段业务的时候,在某个地方创建一个标记。当其他人也想执行这段业务的时候会查看这个标记,若是有标记,就等A执行完删除了标记,再继续执行。若是没有标记则自己创建一个标记,阻塞其他人,自己执行。
针对这个中介C,我们平时在系统中一般有三个思路。
- mysql方案
- Redis思路
- zookeeper思路
3 mysql分布式锁&性能分析
当使用mysql作为分布式锁解决方案的时候,思路是这样的:
当获取到锁的时候,在一张表中添加一条主键为1的数据,当其他用户来获取锁并且添加逐渐为1的数据的时候,由于主键的唯一性,其他用户是不能添加成功的,所以也就拿不到锁。也就是利用mysql的主键冲突实现了互斥的功能。
当不能使用主键的时候,使用唯一索引也可以实现这个问题,唯一索引中也不能出现相同的数据。
但是mysql是基于磁盘的IO,在服务器配置不高的情况下,势必会有性能问题。
4 redis分布式锁原理
redis则主要由SETNX实现
setnx key value
:将key的值设置为value,当且仅当key不存在的时候才能设置成功。
若是给定的key已经存在,则SETNX不会做任何操作。
SETNX是【SET if Not eXist】 的缩写,即如果不存在,则set。
获取锁:setnx key value
释放锁:del key
由于redis是基于内存的IO,会比同配置的mysql性能高。
但是reids基本的加锁会有一些问题,这个我们下节详谈。今天就告辞了。。。。