SQL 的锁机制用于保证多用户并发访问时的数据一致性和完整性。主要有两种锁:
• 共享锁(Shared Lock,读锁):允许多个事务同时读取同一数据,但不允许修改。
• 排他锁(Exclusive Lock,写锁):锁定的数据只能被持有锁的事务修改,其他事务无法读取或修改。
锁的使用示例
✅ 读锁(共享锁)
• 情景:事务 A 读取数据 X,加读锁后,事务 B 也可以读取 X,但不能修改。
• 例子:如果事务 A 需要读取订单表中所有未处理的订单,它可以对这些行加读锁,让其他事务知道 A 正在查看这些数据,并且在 A 完成之前不会修改它们。
❌ 写锁(排他锁) ✍️
• 情景:事务 A 对 X 加写锁后,事务 B 既不能读取也不能修改 X,直到事务 A 释放锁。
• 例子:如果事务 A 正在更新库存表,它会对需要更新的行加写锁,以确保更新过程中不会有其他事务干扰。
⚖️ 共享锁 vs. 排他锁
|
共享锁(读锁) |
排他锁(写锁) ✍️ |
---|---|---|
并发性 |
允许多个事务同时读取 |
一个事务修改时,其他事务不能读或写 |
数据一致性 ✅ |
保证数据读取的一致性 |
保证数据更新的一致性和完整性 |
互斥性 |
读锁之间不互斥,多个事务可同时读取 |
写锁之间、读锁和写锁之间互斥 |
行锁 vs. 表锁
行锁(Row Lock)是数据库管理系统在行级别上的锁定策略,允许事务并发访问不同的行,提高性能 。
• ✅ 行锁优点:锁定特定行,而不是整个表,提高并发性!
• ❌ 表锁问题:当索引失效时,行锁可能退化为表锁,影响性能 ⚠️。
示例:
✅ 使用索引的 UPDATE 操作(行锁):
UPDATE orders SET order_status = 'processed' WHERE order_date = '2024-01-01';
MySQL 使用 order_date 索引,仅锁定满足条件的行。
❌ 未使用索引的 UPDATE 操作(可能退化为表锁):
UPDATE orders SET order_status = 'processed' WHERE order_status LIKE '%pending';
MySQL 无法使用索引,可能会锁定整个表,影响并发性能!
⛓️ 死锁(Deadlock) ⚠️
死锁是指两个事务相互等待对方释放锁,导致永久等待的情况。
⚠️ 死锁示例
1. 事务 A 锁定 账户 123,等待 账户 456 的锁释放。
2. 事务 B 锁定 账户 456,等待 账户 123 的锁释放。
3. 结果:两个事务互相等待,形成死锁!
解决方案:
• 合理设计事务执行顺序,避免交叉锁定不同资源。
• 设置超时时间 ⏳,让事务在等待超过一定时间后自动回滚。
间隙锁(Gap Lock)
间隙锁是 InnoDB 防止幻读(Phantom Read)的一种机制,它不锁定具体的行,而是锁定一个范围。
例子:
1. 事务 A 执行查询:
SELECT * FROM orders WHERE order_id BETWEEN 100 AND 200 FOR UPDATE;
2. 间隙锁生效,即使 ID=150 的订单不存在,事务 B 也无法插入 ID=150 的订单 !
⚠️ 副作用:可能会不必要地阻止其他事务插入数据,降低并发性!
✅ 解决方案:调整事务隔离级别(如从 可重复读 降级到 读已提交),减少间隙锁的使用。
主从复制(Master-Slave Replication)
主从复制用于在主服务器(Master)和从服务器(Slave)之间同步数据,通常用于读写分离和高可用性。
工作流程:
1. 主服务器 记录所有数据变更到二进制日志(Binary Log)。
2. 从服务器 获取日志并应用变更,同步数据。
⚠️ 可能的问题:
• 数据延迟 ⏳:从服务器可能会比主服务器落后,影响一致性。
• 故障恢复复杂 ️:如果主服务器宕机,需要手动或自动切换到从服务器。
总结:
✅ 合理使用锁机制,平衡数据一致性和性能。
✅ 优化索引,避免行锁退化为表锁,影响并发。
✅ 防范死锁,优化事务执行顺序,降低超时风险。
✅ 调整事务隔离级别,减少间隙锁的副作用。
✅ 主从复制有利于读写分离,但需注意复制延迟。
15 | MySQL存储海量数据的最后一招:分库分表-后端存储实战课-极客时间