分布式锁的作用
在单机场景下可以使用内置锁来实现进程同步,但在分布式场景下需要同步的进程可能位于不同节点上,就需要用到分布式锁, 可以保证在分布式部署的应用集群中,同一个方法在同一操作只能被一台机器上的一个线程执行。
简而言之分布式锁是解决分布式环境中同一个方法被客户端调用的一致性问题。
分布式锁的三种实现方式
基于数据库实现分布式锁;
基于缓存(Redis等)实现分布式锁;
基于Zookeeper实现分布式锁;
redis分布式锁Redisson解决方案
模拟springCloud分布式环境中商品秒杀问题
- 假设秒杀服务分布在2个节点上,每个节点上有1000件商品。
- 分别创建eureka,zuul,springboot-reddsion三个服务,其中springboot-reddsion服务作为秒杀服务,并启动2个实例(8000,8001),模拟2个节点
- 通过zuul网关统一入口购买商品,默认zuul网关负载均衡是轮询策略,因此访问购买接口时会轮询调用springboot-reddsion实例接口
- 启动20个线程(模拟20个用户)购买商品,直到接口返回库存不足时结束购买,不加分布式锁和加分布式锁分别测试10次
测试结果如下:
第一批测试
========不加redisson锁-测试20个用户同时购买2个节点上(各1000件)共2000件商品========
第1次测试,一共购买2012个
第2次测试,一共购买2004个
第3次测试,一共购买2028个
第4次测试,一共购买2032个
第5次测试,一共购买2014个
第6次测试,一共购买2004个
第7次测试,一共购买2066个
第8次测试,一共购买2084个
第9次测试,一共购买2072个
第10次测试,一共购买2072个
========加redisson锁-测试20个用户同时购买2个节点上(各1000件)共2000件商品========
第1次测试,一共购买1986个
第2次测试,一共购买1996个
第3次测试,一共购买2000个
第4次测试,一共购买1974个
第5次测试,一共购买2000个
第6次测试,一共购买1992个
第7次测试,一共购买2000个
第8次测试,一共购买1992个
第9次测试,一共购买1976个
第10次测试,一共购买1990个
--------------------------------------------------------------------------
第二批测试
========不加redisson锁-测试20个用户同时购买2个节点上(各1000件)共2000件商品========
第1次测试,一共购买2064个
第2次测试,一共购买2046个
第3次测试,一共购买2062个
第4次测试,一共购买2028个
第5次测试,一共购买2048个
第6次测试,一共购买2042个
第7次测试,一共购买2036个
第8次测试,一共购买2062个
第9次测试,一共购买2044个
第10次测试,一共购买2048个
========加redisson锁-测试20个用户同时购买2个节点上(各1000件)共2000件商品========
第1次测试,一共购买1978个
第2次测试,一共购买1990个
第3次测试,一共购买1944个
第4次测试,一共购买1974个
第5次测试,一共购买2000个
第6次测试,一共购买2000个
第7次测试,一共购买1952个
第8次测试,一共购买1956个
第9次测试,一共购买1966个
第10次测试,一共购买1996个
结论
很明显不加锁出现了超卖现象,而加锁没有出现超卖问题。
分析
-
不加锁超卖是由于没做同步导致,但加锁后为什么大部分购买总件数没达到2000件就结束了呢?
-
加锁状态下总购买件数小于2000,是由于直接在项目中写死了1000件(偷懒了)。模拟调用时接口时只要返回没库存时就结束购买,
-
由于zuul网关默认是轮序负载均衡,所以当轮询到已经卖完的节点就会结束当前用户的购买,因此测试结果才小于等于2000。
-
如果节点使用同一数据源就不会出现小于总数的问题了。到此为止加分布式锁时,可有效解决分布式环境中同一个接口数据一致性问题。
测试代码见https://gitee.com/zhangsike/springboot-learn仓库springboot-redisson项目