MySQL行锁小结

做项目时由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图书借阅系统。假设id=1的这本书库存为1,但是有2个人同时来借这本书,此处的逻辑为

SELECT  restnum FROM book WHERE id =1  ;   --如果restnum大于0,执行update
 
UPDATE  book SET restnum=restnum-1 WHERE id=1;

问题就来了,当2个人同时来借的时候,有可能第一个人执行select语句的时候,第二个人插了进来,在第一个人没来得及更新book表的时候,第二个人查到数据了,其实是脏数据,因为第一个人会把restnum值减1,因此第二个人本来应该是查到id=1的书restnum为0了,因此不会执行update,而会告诉它id=1的书没有库存 了,可是数据库哪懂这些,数据库只负责执行一条条SQL语句,它才不管中间有没有其他sql语句插进来,它也不知道要把一个session的sql语句执行完再执行另一个session的。因此会导致并发的时候restnum最后的结果为-1,显然这是不合理的,所以,才出现锁的概念,Mysql使用innodb引擎可以通过索引对数据行加锁。以上借书的语句变为:

BEGIN;
 
SELECT restnum FROM book WHERE id =1 FOR UPDATE  ; -- 给id=1的行加上排它锁且id有索引
 
UPDATE  book SET restnum=restnum-1 WHERE  ;
 
Commit;

这样,第二个人执行到select语句的时候就会处于等待状态直到第一个人执行commit。从而保证了第二个人不会读到第一个人修改前的数据。 那这样是不是万无一失了呢,答案是否定的。看下面的例子。

跟我一步一步来,先建立表

CREATE TABLE 'book' (
 
  'id' INT(11) NOT NULL AUTO_INCREMENT,
 
  'num' INT(11) DEFAULT NULL,
 
  'name' VARCHAR(0) DEFAULT NULL,
 
  PRIMARY KEY ('id'),
 
  KEY 'asd' ('num')
 
) ENGINE=InnoDB DEFAULT CHARSET=gbk

其中num字段加了索引

然后插入数据,运行,

INSERT INTO book(num) VALUES(11),(11),(11),(11),(11);
 
INSERT INTO book(num) VALUES(22),(22),(22),(22),(22);

然后打开2个mysql控制台窗口,其实就是建立2个session做并发操作

━━━━━━━━━━━━━━━━
在第一个session里运行:

BEGIN;
 
SELECT * FROM book WHERE num=11 FOR UPDATE;

出现结果:

 | id | num | name|   
 | 11 | 11   | NULL |   
 | 12 | 11   | NULL |   
 | 13 | 11   | NULL |   
 | 14 | 11   | NULL |  
 | 15 | 11   | NULL |  
 5 rows in set

然后在第二个session里运行:

BEGIN;
 
SELECT * FROM book WHERE num=22 FOR UPDATE;

出现结果:

| id| num | name | 
| 16 | 22 | NULL | 
| 17 | 22 | NULL |  
| 18 | 22 | NULL |  
| 19 | 22 | NULL |  
| 20 | 22 | NULL | 
5 rows in set

好了,到这里什么问题都没有,是吧,可是接下来问题就来了,大家请看: 回到第一个session,运行:

UPDATE book SET name='abc' WHERE num=11;

━━━━━━━━━━━━━━━━
问题来了,session竟然处于等待状态,可是num=11的行不是被第一个session自己锁住的么,为什么不能更新呢?好了,打这里大家也许有自己的答案,先别急,再请看一下操作。
把2个session都关闭,然后运行:

DELETE FROM book WHERE num=11 LIMIT 3;
 
DELETE FROM book WHERE num=22 LIMIT 3;

其实就是把num=11和22的记录各删去3行, 然后重复“━━━━━━━━”之间的操作 竟然发现,运行update book set name=’abc’ where num=11;后,有结果出现了,说明没有被锁住, 这是为什么呢,难道2行数据和5行数据,对MySQL来说,会产生锁行和锁表两种情况吗。经过跟网友讨论和翻阅资料,仔细分析后发现: 在以上实验数据作为测试数据的情况下,由于num字段重复率太高,只有2个值,分别是11和12.而数据量相对于这两个值来说却是比较大的,是10条,5倍的关系。 那么mysql在解释sql的时候,会忽略索引,因为它的优化器发现:即使使用了索引,还是要做全表扫描,故而放弃了索引,也就没有使用行锁,却使用了表锁。简单的讲,就是MYSQL无视了你的索引,它觉得与其行锁,还不如直接表锁,毕竟它觉得表锁所花的代价比行锁来的小。以上问题即便你使用了force index强制索引,结果还是一样,永远都是表锁。 所以mysql 的行锁用起来并不是那么随心所欲的,必须要考虑索引。再看下面的例子。

SELECT id FROM items WHERE id IN (SELECT id FROM items WHERE id < 6) FOR UPDATE; --id字段加了索引
 
SELECT id FROM items WHERE id IN (1,2,3,4,5) FOR UPDATE;

大部分会认为结果一样没什么区别,其实差别大了,区别就是第一条sql语句会产生表锁,而第二个sql语句是行锁,为什么呢?因为第一个sql语句用了子查询外围查询故而没使用索引,导致表锁。

好了,回到借书的例子,由于id是唯一的,所以没什么问题,但是如果有些表出现了索引有重复值,并且mysql会强制使用表锁的情况,那怎么办呢?一般来说只有重新设计表结构和用新的SQL语句实现业务逻辑,但是其实上面借书的例子还有一种办法。请看下面代码:

 SET sql_mode=
 
'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
 
BEGIN;
 
SELECT restnum FROM book WHERE id =1   ; --取消排它锁, 设置restnum为unsigned
 
UPDATE  book SET restnum=restnum-1 WHERE  ;
 
IF(UPDATE执行成功) commit;
 
ELSE  ROLLBACK;

上面是个小技巧,通过把数据库模式临时设置为严格模式,当restnum被更新为-1的时候,由于restnum是unsigned类型的,因此update会执行失败,无论第二个session做了什么数据库操作,都会被回滚,从而确保了数据的正确性,这个目的只是为了防止并发的时候极小概率出现的2个session的sql语句嵌套执行导致数据脏读。当然最好的办法还是修改表结构和sql语句,让MYSQL通过索引来加行锁。 MySQL测试版本为5.0.75-log和5.1.36-community.

<think>首先,用户的问题是关于MySQL机制及其使用的。他们想知道实现原理和应用场景。我需要基于系统级指令来构建我的回答。 系统级指令要求: - 所有内数学表达式使用 `$...$` 格式,例如 `$x^2$`。 - 独立公式使用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如 `[^1]`。 - 回答构清晰,帮助用户逐步解决问题,保证真实可靠。 - 参考提供的引用。 提供的引用: - [^1]: MySQL深入研究。概述了MySQL机制,包括类型、实现原理等。 - [^2]: 什么是mysql和表。讨论了基本概念、工作原理和应用场景。 - [^3]: Mysql和表。包括类型、加索引的影响、注意事项和常见面试题。 - [^4]: Innodb 实现原理。强调是通过索引实现的,否则使用表。 用户的问题是:“我想了解MySQL中的机制及其使用 请问MySQL 实现原理及应用场景” 所以,我需要覆盖: 1. 实现原理:解释MySQL是如何工作的。 2. 应用场景:讨论在什么情况下使用。 从引用中提取关键点: - 实现原理:InnoDB的是通过给索引的索引项加来实现的(引用[^4])。如果通过索引检索数据,则使用;否则使用表。 - 类型:有不同类型,如共享(S)和排他(X)。 - 应用场景:在高并发环境中,可以提高并发性能,允许同时访问不同的,减少冲突。 回答构: - 逐步解决问题:先介绍基本概念,然后详细解释原理,最后讨论应用场景。 - 构清晰:使用标题或分段。 - 添加引用:在相关段落末尾添加引用标识。 - 生成相关问题:在回答末尾添加相关问题。 确保使用中文。 现在,构建回答: 1. **引言**:简要介绍MySQL的重要性。 2. **实现原理**:详细解释如何通过索引实现。 3. **类型**:讨论不同类型的。 4. **应用场景**:描述在什么情况下使用。 5. **注意事项**:基于引用,提到一些注意事项,如索引的影响。 6. **论**:总。 7. **相关问题**:生成3-5个相关问题。 添加数学表达式:虽然问题不直接涉及数学,但如果合适,可以添加。例如,在讨论的冲突时,可以用不等式表示。但不要强迫使用;只在自然时使用。用户的问题是关于数据库的,可能不需要数学表达式,除非涉及算法。但指令说必须使用,所以找机会。例如: - 在解释的兼容性时:共享和排他的兼容性可以用矩阵表示,如 `$S \times X = 0$` 表示冲突,但这可能过度。最好保持相关。 指令说“所有内数学表达式”,所以需要在回答中至少包含一个。例如,在讨论性能时,可以说“并发性能提升可表示为 $P = k \cdot n$”,但这可能不准确。或许在解释的粒度时:的粒度更细,冲突概率更低,公式表示为冲突概率 $P_{conflict} \propto \frac{1}{n}$ 其中n是数。但这可能不必要。 为了遵守指令,我会在适当的地方添加一个简单的数学表达式。例如,在描述的类型数量时:MySQL支持多种类型,包括共享(S)和排他(X),其兼容性可表示为矩阵。 但最好保持简单。既然指令有示例,我可以添加一个。例如,在讨论索引时:索引键值 $k$ 被加。 决定:在解释实现原理时,提到索引项被加,使用 `$index\_key$`。 添加引用:在段落末尾,如“是通过索引实现的[^4]”。 生成相关问题:类似示例,3个问题。 完整回答构: - 开头:概述MySQL。 - 实现原理部分。 - 应用场景部分。 - 注意事项。 - 相关问题。</think>### MySQL 机制及其使用详解 MySQL机制是 InnoDB 存储引擎的核心特性之一,用于在高并发环境中确保数据的一致性和完整性,同时提升系统的并发性能。通过定数据库中的单个(而非整个表)来减少冲突,适用于需要精细控制并发访问的场景。下面我将逐步解释的实现原理、应用场景及相关注意事项,帮助您全面理解这一机制。 #### 1. **的实现原理** MySQL在 InnoDB 存储引擎中实现,其核心机制依赖于数据库索引。具体来说: - **基于索引的定**:InnoDB 的是通过给索引的索引项(即索引键值 $k$)加来实现的。例如,当执一个 `SELECT ... FOR UPDATE` 语句时,InnoDB 会定查询涉及的索引项,而不是物理数据[^4]。这意味着如果操作基于索引(如主键索引或辅助索引),系统只会定相关的索引项,从而实现级隔离。 - **类型与兼容性**:主要分为共享(S )和排他(X )。共享允许多个事务同时读取同一,但禁止写入;排他则禁止其他事务读取或写入该的兼容性可简化为矩阵形式:S 与 S 兼容,但 S 与 X 冲突,表示为 $S \times X = 0$(冲突)。这种机制确保了事务的 ACID 特性[^1][^3]。 - **索引依赖性与回退机制**:如果查询未使用索引(例如全表扫描),InnoDB 将自动升级为表级(表),以避免性能瓶颈。这是因为的实现依赖于索引构;辅助索引操作时,主键索引也会被定,以防止数据不一致[^4]。例如,一个更新操作 `UPDATE table SET column=value WHERE id=1` 会定 id 索引项,但如果 WHERE 子句无索引,则整个表被定。 在实际运中,的加过程是原子性的,通过内存中的构管理。每个事务在访问时,会检查状态,并在提交或回滚后释放。这减少了死风险,但需注意索引设计(如避免全表扫描)以最大化的效益[^1][^4]。 #### 2. **的应用场景** 适用于高并发、写密集型应用,通过精细控制粒度来提升性能。以下是常见场景: - **在线事务处理(OLTP)系统**:在电商或金融系统中,多个用户可能同时更新同一表的不同(如订单状态修改)。允许这些操作并发执,而不会相互阻塞,显著提高吞吐量。例如,银转账事务只需定涉及的两个账户,其他事务可继续访问其他账户[^2][^3]。 - **读写分离环境**:在读写分离架构中,读操作(S )可并发执,而写操作(X )仅定特定。这避免了表导致的读阻塞,适用于内容管理系统或社交媒体平台[^1][^3]。 - **高并发更新场景**:当应用涉及频繁的插入、更新或删除(如库存扣减)时,减少竞争。例如,在秒杀活动中,确保每个库存项只被一个事务独占修改,防止超卖问题[^2][^4]。 然而,并非万能。在以下情况下需谨慎: - **索引缺失时**:如果查询未命中索引,会退化为表,导致性能下降。因此,优化索引设计是前提。 - **死风险**:多个事务循环等待可能引发死。InnoDB 会自动检测并回滚一个事务,但应用中需添加重试机制[^3][^4]。 - **小表或低并发场景**:对于小表或只读为主的系统,表可能更高效,因为有额外的管理开销。 #### 3. **注意事项与最佳实践** 基于引用内容,使用时应注意: - **索引优化**:确保所有写操作使用索引,避免全表扫描。例如,为 WHERE 子句的列添加索引,以维持的粒度[^4]。 - **超时设置**:通过 `innodb_lock_wait_timeout` 参数控制等待时间,防止事务长时间阻塞。 - **监控与调优**:使用 `SHOW ENGINE INNODB STATUS` 监控争用,并合性能指标(如等待时间)优化 SQL[^1][^3]。 - **避免过度定**:尽量减少事务范围,例如使用精确的 WHERE 条件,缩短持有时间。 总之,MySQL机制通过索引实现高效并发控制,适用于 OLTP 等场景,但需合索引设计和事务管理来规避风险。正确使用时,它能显著提升数据库的并发性能和数据一致性[^1][^2][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值