mysql中的锁概念
mysql已经成为大家日常数据存储的最常用平台,但随着业务量和访问量的上涨,会出现并发访问等场景,如果处理不好并发问题的话会带来严重困扰。下面介绍一下如何通过mysql的悲观锁来解决因并发访问出现的种种数据不一致问题。
为什么需要锁
当我们并发访问或更新数据库时,有可能会出现脏读(Dirty Read)、不可重复读(Unrepeatable Read)、幻读(Phantom Read)、更新丢失(Lost update)等数据不一致情况,为了解决这些问题,mysql引入了多种锁的概念。
我们在项目开发过程中,可以通过sql语句或DB配置快速使用mysql为我们提供的这些底层通用锁服务。
锁类型
MySQL InnoDB对数据行的锁定类型一共有四种:共享锁(读锁,S锁)、排他锁(写锁,X锁)、意向共享锁(IS锁)和意向排他锁(IX锁)。
我们可以根据不同的业务场景和并发特点使用不同的锁。
锁定方式
mysql支持三种锁定方式:
- 行锁(Record Lock):锁直接加在索引记录上面。
- 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。
- Next-Key Lock:行锁与间隙锁组合起来用就叫做Next-Key Lock。
默认情况下,InnoDB工作在可重复读隔离级别下,并且以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁与间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。当一个间隙被事务T加了锁,其它事务是不能在这个间隙插入记录的。
锁的实现
- 在可重复读级别下,InnoDB以Next-Key Lock的方式对索引加锁;在读已提交级别下,InnoDB以Index-Record Lock的方式对索引加锁。
- 被加锁的索引如果不是聚族索引,那被锁的索引所指向的聚族索引以及其它指向相同聚族索引的索引也会被加锁。
- SELECT * FROM ... LOCK IN SHARE MODE对索引加共享锁;SELECT * FROM ... FOR UPDATE对索引加排他锁。
- SELECT * FROM ... 是非阻塞式读,(除Serializable级别)不会对索引加锁。在读已提交级别下,总是查询记录的最新、有效的版本;在可重复读级别下,会记住第一次查询时的版本,之后的查询会基于该版本。例外的情况是在串行化级别,这时会以Next-Key Lock的方式对索引加共享锁。
- UPDATE ... WHERE 与DELETE ... WHERE对索引加排他锁。
- INSERT INTO ... 以Index-Record Lock的方式对索引加排他锁
悲观锁原理详解
什么是悲观锁
对于悲观锁的概念解释主要有两种,但本质上悲观锁主要用于数据库访问的并发控制上。
悲观锁处理流程
- 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)
- 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常
- 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了
- 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常
mysql悲观锁实现
在使用mysql悲观锁之前,我们需要关闭mysql数据库中的“自动提交”属性(set autocommit=0;),因为在mysql中默认使用的是"autocommit模式"。在默认模式下,执行完一个数据库更新操作后,mysql会立即将结果进行提交。
悲观锁使用的示例代码如下:
- 开始事务。begin;/begin work;/start transaction; (三者选一即可)
- 查询商品信息。select status from item where id=1 for update;
- 插入订单数据。insert into order (id,item_id) values (null,1);
- 修改商品状态。update item set status=2;
- 事务提交。commit;/commit work;(二选一即可)
select…for update
上面的查询语句中,我们使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在item表中,id为1的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。
使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键/索引,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。
悲观锁优点
- 悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。
- 悲观锁基于DB层面实现,对业务代码无入侵,使用方便
悲观锁缺点
- 悲观锁适用于可靠的持续性连接,诸如C/S应用。 对于Web应用的HTTP连接,先天不适用
- 锁的使用意味着性能的损耗,在高并发、锁定持续时间长的情况下,尤其严重。 Web应用的性能瓶颈多在数据库处,使用悲观锁,进一步收紧了瓶颈
- 非正常中止情况下的解锁机制,设计和实现起来很麻烦,成本还很高
- 不够严谨的设计下,可能产生莫名其妙的,不易被发现的死锁问题
- 悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好
结语
悲观锁在特定业务场景下有其独特的使用优势,但在大多数互联网场景中性能问题会严重制约我们技术方案的选择,后面再介绍一种新的数据库并发问题解决方案——乐观锁。
相关推荐
- 按加锁的方式分为:乐观锁和悲观锁。 4. 行锁的原理与算法: 行锁是指只对数据库表中某一行记录加锁,MySQL中InnoDB存储引擎支持行锁,其目的是减少锁冲突,提高并发性。行锁的算法通常有: - 记录锁(Record ...
- MySQL 的共享锁和排他锁就是悲观锁的实现。 2. 乐观锁: - 假设不会发生冲突,只有在更新数据时检查是否被其他事务修改。 - 通常通过版本号或时间戳实现,MySQL 中没有内置的乐观锁,但可以在应用层面实现。 ...
锁可以分为乐观锁和悲观锁两种,从对数据库操作的类型分,分为读锁和写锁,从对数据操作的粒度分,分为表锁和行锁。读锁是共享锁,允许多个读操作可以同时进行,而写锁是排它锁,当前写操作没有完成前,它会阻断其他...
4. **锁机制**:了解行级锁、表级锁、页级锁,以及乐观锁和悲观锁的区别。深入理解InnoDB中的Next-Key Locks和Record Locks。 5. **索引优化**:掌握B-Tree、Hash、Bitmap索引的工作原理,理解如何创建和使用合适...
* 悲观锁机制 * 间隙锁机制 * 行锁机制 事务相关知识点 事务是数据库系统中的一种机制,用来确保数据的一致性和可靠性。在MySQL中,事务有多种特性,例如: * 原子性:事务是一个原子操作,保证了数据的一致性 * ...
6. **并发控制**:了解锁的概念,包括表锁、行锁、页锁,以及乐观锁、悲观锁的策略。知道死锁产生的原因和解决方法,比如死锁检测和回滚。 7. **性能优化**:如何编写高效的SQL,避免全表扫描,合理使用索引。了解...
理解这些锁的工作原理有助于开发者充分利用InnoDB的优势。 **概念** - **锁的定义:** 锁是数据库系统中用于管理对共享资源访问的一种机制。它可以应用于不同的层次,如行记录、内存结构等,以确保数据的一致性和...
- **锁机制**:行锁、表锁、悲观锁、乐观锁等,分析其应用场景和优缺点。 - **事务处理**:事务隔离级别、分布式事务解决方案,如2PC、3PC等。 - **MySQL特性**:binlog模式、锁机制、事务处理等。 7. **缓存篇*...
了解锁定机制、乐观锁、悲观锁和多版本并发控制(MVCC)对于高并发应用至关重要。 9. **数据库性能优化**:包括索引的使用、查询优化、存储过程和视图的运用,以及数据库的参数调整。掌握这些技巧能提升数据库的...
MySQL中实现悲观锁与乐观锁** - **悲观锁**:通过行级锁或表级锁实现。 - **乐观锁**:一般通过版本号字段实现。 **20. 事务隔离级别与Spring中的事务传播行为** MySQL支持四种隔离级别:READ UNCOMMITTED、READ...
21.4.1 利用数据库系统的独占锁来实现悲观锁 21.4.2 由应用程序实现悲观锁 21.5 利用Hibernate的版本控制来实现乐观锁 21.5.1 使用元素 21.5.2 使用元素 21.5.3 对游离对象进行版本检查 21.5.4 强制...
5. **MySQL悲观锁如何实现** - MySQL中的悲观锁通常通过行级锁实现,如`SELECT ... FOR UPDATE`语句。 6. **MySQL的乐观锁机制** - MySQL中的乐观锁通常通过版本号或时间戳来实现。在更新记录时检查版本号是否...
- **悲观锁**:假设会发生数据冲突,先获取锁再进行操作。 #### 非关系型数据库与关系型数据库 **10. 非关系型数据库与关系型数据库的区别** - **数据模型**:关系型数据库使用表格结构;非关系型数据库采用文档、...
- 悲观锁假设并发高,读写都加锁,保证数据一致性。 17. **序列化**: - 序列化是将对象转化为字节流,便于存储或网络传输。通过实现Serializable接口实现。 18. **WebSocket集群同步**: - 使用消息队列、共享...
21.4.1 利用数据库系统的独占锁来实现悲观锁 21.4.2 由应用程序实现悲观锁 21.5 利用Hibernate的版本控制来实现乐观锁 21.5.1 使用元素 21.5.2 使用元素 21.5.3 对游离对象进行版本检查 21.5.4 强制...
21.4.1 利用数据库系统的独占锁来实现悲观锁 21.4.2 由应用程序实现悲观锁 21.5 利用Hibernate的版本控制来实现乐观锁 21.5.1 使用元素 21.5.2 使用元素 21.5.3 对游离对象进行版本检查 21.5.4 强制...