实战:MySQL之Innodb中的锁

打印 上一主题 下一主题

主题 536|帖子 536|积分 1610

概叙

科普文:数据库事务、隔离级别和并发问题(MySQL)_数据库的并发控制、事务管理和隔离级别-CSDN博客
快照读取和读取辩论检测:InnoDB通过快照读取确保事务读取到的数据同等性,并通过读取辩论检测来处理惩罚并发事务中的辩论,确保数据的正确性和同等性 。
锁的兼容性:InnoDB中的锁有一套兼容性规则,共享锁(S)和排他锁(X)可以共存,但排他锁会阻塞其他事务对同一资源的访问。意向锁是InnoDB主动添加的,不需要用户干预 。
行锁的实现方式:InnoDB行锁是通过给索引上的索引项加锁实现的。假如查询不通过索引条件,InnoDB将利用表锁而不是行锁,这可能会影响并发性能 。
锁的优化:公道利用索引,减少锁的持有时间,制止死锁等计谋可以帮助优化InnoDB行锁的性能 。
总的来说,InnoDB的行锁机制通过索引来实现对数据行的正确控制,并通过多种锁范例和兼容性规则来处理惩罚并发事务中的辩论。开发者需要注意公道利用索引和优化事务处理惩罚,以进步数据库的并发性能和稳定性。
InnoDB 的行锁实现主要基于索引,并通过多种范例的锁来确保数据的同等性和并发控制。以下是InnoDB行锁实现的几个关键点:

  • 记录锁(Record Locks):这种锁直接锁定某行记录的索引记录。它通常用于唯一索引或主键索引上,当利用正确匹配的查询条件(如id = 1)时,会利用记录锁。假如查询条件不利用索引或利用非正确匹配条件,则可能退化为临键锁 。
  • 间隙锁(Gap Locks):间隙锁锁定一段范围内的索引记录,但不包括记录本身。这种锁基于非唯一索引,并且是Next-Key Locking算法的一部门。间隙锁可以阻止其他事务在锁定的间隙中插入新的记录 。
  • 临键锁(Next-Key Locks):这是InnoDB中的一种特别锁,联合了记录锁和间隙锁的特性。每个数据行上的非唯一索引列上都可能存在临键锁,它锁定一个左开右闭的索引区间,从而办理幻读问题 。
  • 意向锁(Intention Locks):InnoDB利用意向锁来支持行级锁和表级锁的共存。意向锁包括意向共享锁(IS)和意向排他锁(IX),它们在事务需要在更高条理上加锁时主动添加,以便与行级锁兼容 。
1. 记录锁(Record Locks)

记录锁(Record Locks)是InnoDB中用于锁定特定数据行的锁。以下是一些具体的业务场景和示例,说明记录锁的利用方法和效果:
场景一:更新操作

当需要更新某行数据时,通常会利用记录锁来确保在更新过程中数据不会被其他事务修改。
示例: 假设有一个订单表orders,其中包含订单ID、订单状态等字段。当一个订单的状态需要从“待发货”更新为“已发货”时,可以利用以下SQL语句:
  1. UPDATE orders SET status = '已发货' WHERE order_id = 1;
复制代码
在这个例子中,InnoDB会在order_id索引上加一个记录锁,锁定订单ID为1的行。其他事务在该行数据被解锁之前,不能对其举行修改。
场景二:查询并锁定

在某些环境下,需要先查询某行数据,然后对其举行操作。这时可以利用SELECT ... FOR UPDATE语句来锁定查询结果中的行。
示例: 假设需要查询某个用户的具体信息,并在查询后更新其资料。可以利用以下语句:
  1. SELECT * FROM users WHERE user_id = 1 FOR UPDATE;
复制代码
这条语句不仅会返回用户ID为1的记录,还会在user_id索引上加一个记录锁。其他事务在该行数据被解锁之前,不能对其举行修改或查询。
场景三:防止数据被其他事务修改

在某些业务逻辑中,可能需要确保某行数据在一段时间内不会被其他事务修改。
示例: 假设有一个库存表inventory,需要确保在计算库存的过程中数据不会被其他事务修改。
  1. SELECT * FROM inventory WHERE product_id = 100 FOR UPDATE;
复制代码
这条语句会锁定产物ID为100的库存记录,直到当前事务结束。在此期间,其他事务不能修改该记录。
场景四:制止数据重复插入

在插入数据时,假如需要制止插入重复的数据,可以利用记录锁来确保唯一性。
示例: 假设有一个用户表users,需要插入一个新的用户记录,但要确保用户名是唯一的。
  1. INSERT INTO users (username, email) VALUES ('newuser', 'newuser@example.com') ON DUPLICATE KEY UPDATE email = 'newuser@example.com';
复制代码
假如username字段是唯一索引,这条语句会首先尝试插入新记录。假如用户名已存在,InnoDB会在username索引上加一个记录锁,并更新现有记录的电子邮件地址。
场景五:事务中的同等性读取

在事务中,假如需要确保读取的数据在事务执行期间不被其他事务修改,可以利用记录锁来实现。
示例: 假设在一个事务中需要读取并处理惩罚某个订单的全部相关信息。
  1. START TRANSACTION;
  2. SELECT * FROM orders WHERE order_id = 10 FOR UPDATE;
  3. -- 执行一些业务逻辑处理
  4. COMMIT;
复制代码
在这个事务中,SELECT ... FOR UPDATE语句会锁定订单ID为10的记录,确保在事务执行期间其他事务不能修改该记录。
通过这些示例,可以看到记录锁在确保数据同等性和防止数据被并发事务修改方面的重要性。
2. 间隙锁(Gap Locks)

间隙锁(Gap Locks)在InnoDB中用于锁定一个范围内的记录,但不包括记录本身。这种锁主要用于防止其他事务在这个范围内插入新的记录,从而维护数据的同等性温顺序。以下是一些具体的业务场景和示例,说明间隙锁的利用方法和效果:
场景一:防止数据插入

在某些业务逻辑中,可能需要确保某个范围内的数据不会被其他事务插入,以维护数据的完备性。
示例: 假设有一个员工表employees,包含员工ID和部门ID。假如需要防止在某个部门ID范围内插入新的员工记录,可以利用以下SQL语句:
  1. SELECT * FROM employees WHERE department_id BETWEEN 10 AND 20 FOR UPDATE;
复制代码
这条语句会锁定部门ID在10到20之间的全部记录,但不包括这些记录本身。其他事务在该范围内不能插入新的员工记录,直到当前事务结束。
场景二:范围查询并锁定

在举行范围查询时,假如需要确保查询结果中的记录不会被其他事务修改,可以利用间隙锁。
示例: 假设需要查询某个日期范围内的全部订单,并锁定这些订单记录。
  1. SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31' FOR UPDATE;
复制代码
这条语句会锁定全部订单日期在2024年1月1日到1月31日之间的订单记录。其他事务在当前事务结束之前,不能修改这些订单记录。
场景三:制止数据重复

在插入数据时,假如需要制止在某个范围内插入重复的数据,可以利用间隙锁来确保唯一性。
示例: 假设有一个产物表products,需要确保在某个价格范围内不会插入重复的产物。
  1. SELECT * FROM products WHERE price BETWEEN 100 AND 200 FOR UPDATE;
复制代码
这条语句会锁定价格在100到200之间的全部产物记录,但不包括这些记录本身。其他事务在当前事务结束之前,不能在这个价格范围内插入新的产物记录。
场景四:维护数据顺序

在某些业务逻辑中,可能需要确保数据的插入顺序,间隙锁可以用于维护这种顺序。
示例: 假设有一个任务表tasks,需要确保任务的插入顺序按照任务的优先级举行。
  1. SELECT * FROM tasks WHERE priority BETWEEN 1 AND 5 FOR UPDATE;
复制代码
这条语句会锁定优先级在1到5之间的全部任务记录,但不包括这些记录本身。其他事务在当前事务结束之前,不能在这个优先级范围内插入新的任务记录。
场景五:防止数据覆盖

在某些环境下,可能需要防止在某个范围内的数据被其他事务覆盖。
示例: 假设有一个库存表inventory,需要确保在某个库存量范围内的数据不会被其他事务覆盖。
  1. SELECT * FROM inventory WHERE stock_level BETWEEN 50 AND 100 FOR UPDATE;
复制代码
这条语句会锁定库存量在50到100之间的全部库存记录,但不包括这些记录本身。其他事务在当前事务结束之前,不能在这个库存量范围内插入或修改库存记录。
通过这些示例,可以看到间隙锁在防止数据被并发事务插入和维护数据同等性方面的重要性。
3. 临键锁(Next-Key Locks)

临键锁(Next-Key Locks)是InnoDB中一种特别的锁,它联合了记录锁和间隙锁的特点,用于锁定一个记录及其后继记录之间的“间隙”。这种锁主要用于办理幻读问题,确保在可重复读(Repeatable Read)隔离级别下,事务可以看到同等的快照视图。
以下是一些具体的业务场景和示例,说明临键锁的利用方法和效果:
场景一:防止幻读

在可重复读隔离级别下,假如一个事务需要多次读取同一数据集,临键锁可以确保在事务执行期间,其他事务不能在这些数据之间插入新的记录。
示例: 假设有一个订单表orders,包含订单ID和订单状态。一个事务需要多次查抄某个订单的状态,确保在处理惩罚期间订单状态没有被其他事务修改。
  1. START TRANSACTION;
  2. SELECT * FROM orders WHERE order_id = 100 FOR UPDATE;
  3. -- 检查订单状态
  4. -- 执行一些业务逻辑
  5. SELECT * FROM orders WHERE order_id = 100 FOR UPDATE;
  6. COMMIT;
复制代码
在这个例子中,第一次SELECT ... FOR UPDATE会锁定订单ID为100的记录,同时也会锁定该记录后面的间隙,防止其他事务在这个间隙中插入新的订单记录。
场景二:范围查询并锁定

在举行范围查询时,假如需要确保查询结果中的记录不会被其他事务插入或修改,可以利用临键锁。
示例: 假设需要查询某个价格范围内的全部产物,并锁定这些产物记录。
  1. SELECT * FROM products WHERE price BETWEEN 100 AND 200 FOR UPDATE;
复制代码
这条语句会锁定价格在100到200之间的全部产物记录,同时也会锁定这些记录后面的间隙。其他事务在当前事务结束之前,不能在这个价格范围内插入新的产物记录,也不能修改这些记录。
场景三:维护数据顺序

在某些业务逻辑中,可能需要确保数据的插入顺序,临键锁可以用于维护这种顺序。
示例: 假设有一个任务表tasks,需要确保任务的插入顺序按照任务的优先级举行。
  1. SELECT * FROM tasks WHERE priority BETWEEN 1 AND 5 FOR UPDATE;
复制代码
这条语句会锁定优先级在1到5之间的全部任务记录,同时也会锁定这些记录后面的间隙。其他事务在当前事务结束之前,不能在这个优先级范围内插入新的任务记录。
场景四:防止数据覆盖

在某些环境下,可能需要防止在某个范围内的数据被其他事务覆盖。
示例: 假设有一个库存表inventory,需要确保在某个库存量范围内的数据不会被其他事务覆盖。
  1. SELECT * FROM inventory WHERE stock_level BETWEEN 50 AND 100 FOR UPDATE;
复制代码
这条语句会锁定库存量在50到100之间的全部库存记录,同时也会锁定这些记录后面的间隙。其他事务在当前事务结束之前,不能在这个库存量范围内插入新的库存记录,也不能修改这些记录。
场景五:数据同等性查抄

在某些业务逻辑中,可能需要在事务中多次查抄数据的同等性,临键锁可以确保在查抄期间数据不会被其他事务修改。
示例: 假设有一个员工表employees,需要在事务中多次查抄某个员工的薪资是否符合预期。
  1. START TRANSACTION;
  2. SELECT * FROM employees WHERE employee_id = 1 FOR UPDATE;
  3. -- 检查薪资
  4. -- 执行一些业务逻辑
  5. SELECT * FROM employees WHERE employee_id = 1 FOR UPDATE;
  6. COMMIT;
复制代码
在这个例子中,SELECT ... FOR UPDATE会锁定员工ID为1的记录,同时也会锁定该记录后面的间隙,确保在事务执行期间其他事务不能在这个间隙中插入新的员工记录或修改该员工的薪资。
通过这些示例,可以看到临键锁在防止幻读、维护数据同等性温顺序方面的重要性。
4. 意向锁(Intention Locks)

意向锁(Intention Locks)是InnoDB存储引擎中的一种内部利用的锁,用于表现事务将要请求的锁范例,并帮助事务在不同级别的锁(行锁和表锁)之间实现兼容性。意向锁主要有以下两种范例:

  • 意向共享锁(Intention Shared Lock,IS):事务在请求多个行的共享锁之前,首先在表级别加上意向共享锁。
  • 意向排他锁(Intention Exclusive Lock,IX):事务在请求多个行的排他锁之前,首先在表级别加上意向排他锁。
下面是业务场景和示例:
场景一:多行数据的更新

当需要更新表中的多行数据时,事务会在表级别加上意向排他锁,以表明它打算在表中放置排他锁。
示例: 假设有一个在线购物平台的订单表orders,需要批量更新多个订单的状态为“已发货”。
  1. START TRANSACTION;
  2. UPDATE orders SET status = 'Shipped' WHERE order_id IN (101, 102, 103);
  3. COMMIT;
复制代码
在这个事务中,InnoDB会在orders表上主动加上意向排他锁(IX),然后在每条选定的订单记录上加上排他锁(X)。这表明事务打算修改这些行,并且其他事务不能同时修改这些行或在表上加上共享锁。
场景二:多行数据的读取

假如一个查询需要读取多行数据,并且事务需要确保这些数据在读取期间不被修改,事务会在表级别加上意向共享锁。
示例: 假设需要为报表生成读取特定条件的订单数据,以确保在生成报表期间这些订单数据不被修改。
  1. START TRANSACTION;
  2. SELECT * FROM orders WHERE customer_id = 100 FOR UPDATE;
  3. COMMIT;
复制代码
在这个事务中,InnoDB会在orders表上主动加上意向排他锁(IX),然后在满足条件的每一行上加上排他锁(X)。这确保了在事务期间,其他事务不能修改这些订单记录。
场景三:制止死锁

在复杂的业务逻辑中,多个事务可能需要在不同的表或同一表的不同行上请求锁。意向锁有助于制止死锁,由于它允许事务在请求行锁之前表明其锁意图。
示例: 假设有两个事务,事务A需要更新orders表和customers表,事务B也需要更新这两个表,但顺序相反。
事务A:
  1. START TRANSACTION;
  2. UPDATE orders SET ... WHERE order_id = 101;
  3. UPDATE customers SET ... WHERE customer_id = 100;
  4. COMMIT;
复制代码
事务B:
  1. START TRANSACTION;
  2. UPDATE customers SET ... WHERE customer_id = 100;
  3. UPDATE orders SET ... WHERE order_id = 101;
  4. COMMIT;
复制代码
即使两个事务请求锁的顺序不同,意向锁的存在可以确保它们在请求行锁之前在表级别请求相应的意向锁,从而降低死锁的风险。
场景四:表结构变更时的兼容性

当数据库管理员需要对表结构举行变更,如添加索引,而表中已有行锁时,意向锁提供了一种机制来确保结构变更不会与现有的行级锁辩论。
示例: 数据库管理员需要为orders表添加一个新索引,但表中已有多个行被锁。
  1. ALTER TABLE orders ADD INDEX (new_column);
复制代码
在这个操作中,InnoDB会在表级别查抄意向锁,以确保没有其他事务正在修改表中的数据,从而安全地举行索引的添加。
意向锁是InnoDB内部主动处理惩罚的,不需要用户手动请求。它们在事务需要在多行上请求共享锁或排他锁时,提供了一种高效的协调机制,以确保数据库的并发控制和数据同等性。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表