MySql 乐观锁 与 悲观锁 的概念与使用
2020-04-23 17:45
507 查看
MySql 的乐观锁 与 悲观锁
先上图:
乐观锁
乐观锁,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
乐观锁适用于 读多写少 的应用场景,可以提高吞吐量。
乐观锁:假设数据不会发生变化,只在提交操作时检查是否违反数据完整性。
乐观锁的两种实现方式:
-
使用数据版本(version)记录机制实现。
即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “
version
” 字段来实现。当读取数据时,将version
字段的值一同读出,数据每更新一次,对此version
值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version
值进行比对,如果数据库表当前版本号与第一次取出来的version
值相等,则予以更新,否则认为是过期数据。 -
使用时间戳。
悲观锁
悲观锁,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
Java synchronized就属于悲观锁的一种实现,每次线程要修改数据时都先获得锁,保证同一时刻只有一个线程能操作数据,其他线程则会被block。
实例:
电商系统中的下单流程,商品的库存量是固定的,如何保证商品数量不超卖? 其实需要保证数据一致性:某个人点击秒杀后系统中查出来的库存量和实际扣减库存时库存量的一致性就可以。
MySql 数据库中商品库存表结构定义如下:
CREATE TABLE `tb_product_stock` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID', `product_id` bigint(32) NOT NULL COMMENT '商品ID', `number` INT(8) NOT NULL DEFAULT 0 COMMENT '库存数量', `create_time` DATETIME NOT NULL COMMENT '创建时间', `modify_time` DATETIME NOT NULL COMMENT '更新时间', PRIMARY KEY (`id`), // 主键 UNIQUE KEY `index_pid` (`product_id`) // 索引 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品库存表';
对应的实体类:
public class ProductStock { private Long productId; //商品id private Integer number; //库存量 public Long getProductId() { return productId; } public void setProductId(Long productId) { this.productId = productId; } public Integer getNumber() { return number; } public void setNumber(Integer number) { this.number = number; } }
如果不考虑并发的情况下,更新库存代码如下:
/** * 更新库存(不考虑并发) * @param productId * @return */ public boolean updateStockRaw(Long productId){ ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId); if (product.getNumber() > 0) { int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId); if(updateCnt > 0){ //更新库存成功 return true; } } return false; }
但是在多线程并发情况下,会存在超卖的可能。
加上悲观锁的代码如下:
/** * 更新库存(使用悲观锁) * @param productId * @return */ public boolean updateStock(Long productId){ //先锁定商品库存记录 “FOR UPDATE” 拿数据时上锁 ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId} FOR UPDATE", productId); if (product.getNumber() > 0) { int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId); if(updateCnt > 0){ //更新库存成功 return true; } } return false; }
加上乐观锁的代码如下:
/** * 下单减库存 * @param productId * @return */ public boolean updateStock(Long productId){ int updateCnt = 0; while (updateCnt == 0) { ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId); if (product.getNumber() > 0) { updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId} AND number=#{number}", productId, product.getNumber()); if(updateCnt > 0){ //更新库存成功 return true; } } else { //卖完啦 return false; } } return false; }
使用乐观锁更新库存的时候不加锁,当提交更新时需要判断数据是否已经被修改(AND number=#{number}),只有在 number等于上一次查询到的number时 才提交更新。
乐观锁与悲观锁的区别
乐观锁的思路一般是表中增加版本字段,更新时where语句中增加版本的判断,算是一种
CAS(Compare And Swep)操作,商品库存场景中number起到了版本控制(相当于version)的作用(
AND number=#{number})。
悲观锁之所以是悲观,在于他认为本次操作会发生并发冲突,所以一开始就对商品加上锁(
SELECT ... FOR UPDATE),然后就可以安心的做判断和更新,因为这时候不会有别人更新这条商品库存。
ber起到了版本控制(相当于version)的作用(
AND number=#{number})。
悲观锁之所以是悲观,在于他认为本次操作会发生并发冲突,所以一开始就对商品加上锁(
SELECT ... FOR UPDATE),然后就可以安心的做判断和更新,因为这时候不会有别人更新这条商品库存。
相关文章推荐
- 对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解
- 对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解
- mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解
- hibernate 乐观锁与悲观锁使用
- hibernate悲观锁与乐观锁的使用
- mysql的锁--行锁,表锁,乐观锁,悲观锁
- Hibernate 乐观锁与悲观锁使用
- mysql悲观锁以及乐观锁总结和实践
- 【数据库】mysql深入理解乐观锁与悲观锁
- MySQL学习笔记(四)悲观锁与乐观锁
- hibernate 乐观锁与悲观锁使用
- mysql悲观锁以及乐观锁总结和实践
- 01-JDBC概念--JDBC(Java Database Connectivity:Java数据库连接):使用jdbc实现Java与数据库MySQL连接
- mysql乐观锁和悲观锁
- Mysql 悲观锁与乐观锁
- 对mysql乐观锁、悲观锁、共享锁、排它锁、行锁、表锁概念的理解
- MySQL使用帮助及基本概念
- 理解MySQL字段约束的概念、基本使用。企业新闻管理系统数据库建设
- mysql乐观锁的使用
- MySQL(27):行锁、表锁、乐观锁、悲观锁