微服务分布式锁思想集合
- [1. 传统锁](#1. 传统锁)
- 2.基于redis的分布式锁
- 3.基于zk的分布式锁
- 4.基于mysql的分布式锁
-
- [4.1 mysql悲观锁中使用行级锁](#4.1 mysql悲观锁中使用行级锁)
- [4.2 mysql](#4.2 mysql)
- 基于扣减库存演示超卖,使用分布式锁解决这种超卖的情况
- 使用nginx做负载均衡、使用jmeter做压力测试
- 环境要求:jdk1.8+、maven3.x
- 技术要求:SpringBoot2.x、Springmvc、spring、mybatis、springData-redis、mysql、
redis- 开发工具:idea、nginx、jmeter
- 其他: zookeeper、lua脚本等
1. 传统锁
JVM本地锁(synchronized、ReentrantLock)
2.基于redis的分布式锁
redis乐观锁
- 手写实现
- 基本实现
- 防死锁
- 防误删
- 可重入
- 可续期
- 红锁算法
- Redisson框架
- 可重入锁及源码分析
- 公平锁FairLock
- 联锁、红锁
- 读写锁
- 信号量
- 闭锁
3.基于zk的分布式锁
- 手写实现
- Zookeeper的安装
- 常用指令及Java客户端
- 基本实现分布式锁
- 实现阻塞锁
- 实现可重入式分布式锁
- Curator框架
- 可重入锁及源码解读
- 可重入的读写锁
- 信号量
- 栅栏
- 共享计数器
4.基于mysql的分布式锁
一句sql扣减库存
update stock set count = count -1 where product_code = '1001' and count >= 1
这个update语句会自动加锁,这是靠数据库引擎的(表锁)
优点:
- 数据库引擎加锁执行更新保证了服务集群不会出现超卖现象
- 吞吐量也会很高
缺点:
- 锁范围的问题(表锁、行锁)
- 同一商品有多条库存记录
- 无法记录库存变化前后的状态
Mysql悲观锁:
select ... for update
- 查询并锁定信息
java@select("select * from db_stock where product_code = #{productCode} for update") List<Stock> queryStock(String productCode); List<Stock> stocks = this.stockMapper.queryStock("1001"); Stock stock = stocks.get(0);
- 判断库存充足
javaif(stock != null && stock.getCount >0) { stock.setCount(stock.getCount -1); this.stockMapper.updateById(stock); }
- 扣减库存
配和事务注解@Transational
4.1 mysql悲观锁中使用行级锁
python
# 1.锁的查询或者更新条件必须是索引字段
# 2.查询或更新条件必须是具体值(可以是in,= 但不能是!=,like)
4.2 mysql
java
/*
1. jvm本地锁:三种情况导致锁失效 600
多例模式
事务 Read Uncommitted
集群部署
2. 一个sql语句:更新数量时判断 2000
解决:三个锁失效
问题:
1. 锁范围问题 表级锁 行级锁
2. 同一个商品有多条库存记录
3. 无法记录库存变化前后的状态
3. 悲观锁:select ... for update
问题:
1. 性能问题
2. 死锁问题:对多条数据加锁时,加锁数据一致。
①事务,对第一条数据加锁,请求第二条数据的锁;
②事务,对第二天数据加锁,请求第一条数据的锁;
两个事务争抢对方的锁,造成死锁问题。
3. 库存操作要统一:select..for update 普通的select
4. 乐观锁:时间戳 version版本号 CAS机制
问题:
1.高并发情况下,性能极低
2.ABA问题
3.读写分离情况下乐观锁不可靠(主从数据库 数据拷贝的原因)
总结:
性能:一个sql > 悲观锁 > jvm锁 > 乐观锁
如果追求极致性能,业务场景简单并且不需要记录数据变化前后的情况下:
优先选择:一个sql
如果写并发量较低(多读),争抢不是很激烈的情况下优先选择:乐观锁
如果写并发量较高,一般会经常冲突,此时选择乐观锁的话,会导致业务代码不间断的重试。
优先选择:mysql悲观锁
不推荐jvm本地锁。
*/