数据库锁机制与并发控制
字数 1292 2025-11-02 08:11:07
数据库锁机制与并发控制
1. 问题描述
在高并发场景下,数据库如何保证数据的一致性?例如,当多个用户同时修改同一行数据时,可能出现更新丢失、脏读等问题。这类问题需要通过锁机制和事务隔离级别来解决。面试中常会考察锁的类型、死锁的产生与避免,以及如何通过优化锁策略提升并发性能。
2. 锁的基本类型与作用
(1)共享锁(Shared Lock)
- 作用:允许多个事务同时读取同一数据,但禁止其他事务获取排他锁(即禁止修改)。
- 场景:
SELECT ... LOCK IN SHARE MODE(MySQL)或WITH (HOLDLOCK)(SQL Server)。 - 特点:读读不阻塞,读写阻塞。
(2)排他锁(Exclusive Lock)
- 作用:仅允许一个事务修改数据,其他事务无法读取或修改。
- 场景:
UPDATE、DELETE、INSERT或SELECT ... FOR UPDATE。 - 特点:写写互斥、读写互斥。
示例:
-- 事务A
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE; -- 获取id=1的排他锁
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
-- 事务B若同时执行以下操作会被阻塞:
UPDATE accounts SET balance = balance + 200 WHERE id = 1;
3. 锁的粒度:表锁 vs 行锁
(1)表级锁
- 特点:锁住整张表,实现简单但并发度低。
- 适用场景:MyISAM引擎、批量操作。
(2)行级锁
- 特点:仅锁住需要操作的行,并发度高但开销大。
- 适用场景:InnoDB引擎、高并发事务。
- 实现依赖:需索引支持。若
WHERE条件无索引,可能退化为表锁。
示例:
-- 无索引字段更新导致行锁升级为表锁
UPDATE users SET status = 1 WHERE name = 'Alice'; -- 若name无索引,锁整张表
4. 死锁的产生与解决
(1)死锁条件
- 互斥、持有并等待、不可剥夺、循环等待。
典型场景:
-- 事务A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 锁住id=1
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 等待事务B释放id=2的锁
-- 事务B
BEGIN;
UPDATE accounts SET balance = balance - 200 WHERE id = 2; -- 锁住id=2
UPDATE accounts SET balance = balance + 200 WHERE id = 1; -- 等待事务A释放id=1的锁
此时事务A和B互相等待,形成死锁。
(2)解决方案
- 数据库自动检测:InnoDB会主动回滚其中一个事务(如回滚代价最小的事务)。
- 优化应用逻辑:按固定顺序访问资源(例如始终先操作id更小的记录)。
- 设置锁超时:
SET innodb_lock_wait_timeout = 5;(MySQL)。
5. 事务隔离级别对锁的影响
不同隔离级别通过锁机制实现数据一致性,但并发性能不同:
- 读未提交(Read Uncommitted):无读锁,可能脏读。
- 读已提交(Read Committed):仅加写锁,避免脏读,但不可重复读。
- 可重复读(Repeatable Read):读写均加锁,避免不可重复读(MySQL通过MVCC优化)。
- 串行化(Serializable):所有操作加锁,并发度最低。
示例:
-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT * FROM accounts WHERE id = 1; -- 可能加共享锁(依赖数据库实现)
6. 性能优化实践
(1)减少锁竞争
- 使用索引:避免行锁升级为表锁。
- 缩短事务时间:事务内只保留必要操作,尽快提交。
- 分解大事务:将批量更新拆分为小批次。
(2)乐观锁与悲观锁的选择
- 悲观锁:默认并发冲突频繁,直接加锁(如
SELECT ... FOR UPDATE)。 - 乐观锁:通过版本号或时间戳检测冲突,适合读多写少场景。
-- 乐观锁示例
UPDATE products SET stock = stock - 1, version = version + 1
WHERE id = 100 AND version = 5; -- 若版本号被修改,更新失败
7. 总结
- 锁是保证并发数据一致性的核心机制,但需权衡性能与安全。
- 通过合理选择锁粒度、隔离级别,以及避免死锁,可显著提升系统并发能力。
- 实际开发中应结合监控工具(如
SHOW ENGINE INNODB STATUS)分析锁争用情况。