MENU

分布式锁Mysql篇

分布式锁

CREATE TABLE `stock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_code` varchar(20) NOT NULL,
  `warehouse` varchar(20) NOT NULL,
  `count` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_pc` (`product_code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

image-20221226222452119

1.JVM本地锁

2.一个sql

3.悲观锁:select ...from update

mysql悲观锁中使用行级锁:

1.锁的查询或者更新条件必须是索引字段

2.查询或者更新条件必须是具体值 不能使用 like这样的模糊查询

image-20221226220923640

Mapper代码实现:

@select("select * from stock where product_code = #{productCode} for update;")
List<Stock> queryStock(String productCode);

service实现:

image-20221226223858395

解决了锁定的范围 同时一个商品的多条库存记录 记录了库存变化前后的状态

缺点问题:

1.性能问题 性能较低

2.会产生死锁: 对多条数据加锁时,加锁的顺序要一致

3.库存操作要统一:用select ...for update 而用普通select锁不住

4.mysql的乐观锁:时间戳version版本号CAS机制

CREATE TABLE `stock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_code` varchar(20) NOT NULL,
  `warehouse` varchar(20) NOT NULL,
  `count` int(11) NOT NULL,
  `version` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_pc` (`product_code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

image-20221226230803783

如果version与mysql中的不相等就更新失败 再次进行尝试!

servic实现:

image-20221226231822544

会出现栈溢出 还有超时问题!

解决栈溢出问题

image-20221226231854623

MDL 更新删除操作 会进行加锁 就会出现超时问题

去掉手动事务@Transactional(事务也会加锁) 后面当进行到 if(this....update...)时候 本来有悲观锁 当执行失败的就会放掉锁 后面就不会阻塞

最终:

image-20221226232615555

乐观锁存在的问题:

1.高并发的情况下,性能极低。 (乐观锁适合读多写少的情况)

2.CAS会产生ABA问题。

3.读写分离的情况下导致乐观锁不可靠

image-20221226235830887

myqsl锁总结:

性能:一个sql>悲观锁>JVM锁>乐观锁

如果追求极致的性能,业务场景简单并不需要记录前后变化的情况下 优先选择:一个sql;

如果写并发量低(读多),争抢不是很激烈的情况下 优先选择:乐观锁;

如果写并发量较高,一般会经常冲突,此时选择乐观锁的话,会导致业务代码间的不断重试。 应该优先选择:悲观锁;

不推荐使用JVM本地锁;

Last Modified: April 8, 2023