数据库领域为了解决ACID的平衡,尝试了各种锁、各种模式,
引擎技术特性、厂家实现方式各放异彩,被各种锁折磨的小伙伴,是不是感同身受。
一、数据库锁
1. 锁的类型与特点
- 全局锁:锁定整个数据库实例,备份期间所有写操作阻塞,业务停滞
- 表级锁:锁定整张表,批量操作时并发性差(如MySQL/PostgreSQL万级TPS瓶颈)
- 行级锁:细粒度锁定单行,但高并发下仍存在三大致命缺陷:
- 阻塞等待:事务A未提交时,事务B需排队(如库存扣减场景)
- 死锁风险:循环等待资源(如转账场景中A等B、B等A)
- 优先级反转:低优先级事务持有锁时,高优先级事务被阻塞
2. 锁的本质矛盾
- 悲观锁 →悲观锁假设并发冲突频繁发生,因此在操作数据前立即加锁,阻止其他线程访问资源,直到当前操作完成
- 乐观锁 → 乐观锁假设并发冲突较少,操作数据时不加锁,仅在提交时检查数据是否被修改(冲突检测),高并发但需重试逻辑
3. 锁的分类
共享锁(S锁)
原理:允许多事务并发读取同一资源,禁止写入。
场景:高并发查询(如报表系统),配合读已提交隔离级别避免脏读。
排他锁(X锁)
原理:事务修改数据时独占资源,禁止其他事务读写。
场景:数据更新操作(如账户扣款),保障原子性。
间隙锁(Gap Lock)
原理:锁定索引记录间的空白区间,防止新数据插入。
场景:可重复读隔离级别下解决幻读(如范围查询)。
更新锁(U锁)
原理:预锁定待修改资源,避免共享锁升级为排他锁时的死锁。
场景:先读后写的复合操作(如“查询库存后扣减”)。
4. 事务的隔离级别对锁的诉求又不一样
隔离级别 | 锁机制 | 解决痛点 | 遗留问题 |
读未提交 | 写操作加X锁,读操作无锁 | 无 | 脏读 |
读已提交 | 写操作加X锁,读操作依赖MVCC | 脏读 | 不可重复读 |
可重复读 | 写操作加X锁,读操作MVCC+间隙锁 | 不可重复读、部分幻读 | 未完全解决幻读 |
串行化 | 读写均加锁(表级或范围锁) | 所有并发问题 | 并发性能极差 |
5. 技术特性对比
维度 | Lock-Free Reservations | 传统行锁 | 乐观锁 |
锁持有时间 | 仅提交瞬间(毫秒级) | 整个事务周期 | 无锁但需重试 |
并发吞吐量 | 100万+ TPS(无锁竞争) | 1万~10万TPS(锁竞争瓶颈) | 高但重试开销大 |
开发复杂度 | 声明列属性即可 | 需处理死锁 | 需实现重试逻辑 |
二、Oracle 23 ai Lock-Free Reservations(无锁列值保留)
1. 原理创新:日志缓冲-原子合并
-- 声明可保留列
ALTER TABLE inv_test ADD (qty NUMBER RESERVABLE CHECK (qty >= 0));
-- 退回传统锁模式
ALTER TABLE inv_test MODIFY (qty NOT RESERVABLE);
- 无锁操作:对RESERVABLE列(如库存qty)的加减操作(qty = qty ± N)不锁行,而是将增量值(如-50)写入内部日志表SYS.RESV$JOURNAL
- 原子提交:事务提交时,日志值合并到原表,并自动校验约束(如qty >= 0),违反则回滚
- 数据类型:仅支持NUMBER/INTEGER/FLOAT(非数值列报错ORA-55748)
- 语法限制:必须使用±操作(直接赋值报错ORA-55746)。
- 非保留列:更新产品名称等非RESERVABLE列时仍触发行锁。
三、实战验证:高并发库存扣减测试
1. 验证脚本
SYS@CDB$ROOT> GRANT CREATE TABLESPACE TO QC;
Grant succeeded.
SYS@CDB$ROOT> GRANT CREATE SESSION TO QC;
Grant succeeded.
SYS@CDB$ROOT> GRANT ALTER TABLESPACE, DROP TABLESPACE TO QC;
Grant succeeded.
SYS@CDB$ROOT> commit;
Commit complete.
SYS@CDB$ROOT> ALTER USER QC QUOTA UNLIMITED ON users;
User QC altered.
SYS@CDB$ROOT> commit;
QC@localhost:1521/FREEPDB1> CREATE TABLESPACE inv_ts1 DATAFILE 'inv_ts1.dbf' SIZE 100M;
TABLESPACE INV_TS1 created.
SYS@CDB$ROOT> create user QC identified by Oracle_4U;
User QC created.
SYS@CDB$ROOT> GRANT CREATE TABLE, ALTER ANY TABLE TO QC;
Grant succeeded.
[oracle@OL96 ~]$ sql QC/Oracle_4U@localhost:1521/FREEPDB1;
-- --1:启用无锁列(Oracle 23ai)
CREATE TABLE inv_test (item_id NUMBER PRIMARY KEY,qty NUMBER RESERVABLE CHECK (qty >= 0) -- 关键声明
);
INSERT INTO inv_test VALUES (1, 100);
COMMIT;-- --2:并发扣减(两个会话)
-- 会话1:扣减50(不提交)
UPDATE inv_test SET qty = qty - 50 WHERE item_id=1;-- 会话2:扣减30(立即执行,无阻塞!)
UPDATE inv_test SET qty = qty - 30 WHERE item_id=1;
COMMIT;
-- 会话2先提交-- 会话1提交
COMMIT; -- 合并日志:100-50-30=20,校验通过-- --3:结果验证
SELECT * FROM inv_test;
QC@localhost:1521/FREEPDB1> SELECT * FROM inv_test;ITEM_ID QTY
__________ ______1 20-- --4:触发约束保护(故意超扣)
UPDATE inv_test SET qty = qty - 150 WHERE item_id=1;
-- 提交时报错ORA-02290(违反CHECK约束)
QC@localhost:1521/FREEPDB1> UPDATE inv_test SET qty = qty - 150 WHERE item_id=1;Error starting at line : 1 in command -
UPDATE inv_test SET qty = qty - 150 WHERE item_id=1
Error report -
ORA-02290: check constraint (QC.SYS_C008612) violated
Help: https://docs.oracle.com/error-help/db/ora-02290/
2. 工作流程解析
3. 验证要点
- 无阻塞:会话2无需等待会话1提交即可执行更新
- 约束保护:扣减后qty<0时自动回滚。
- 日志追溯(DBA权限):
-- 查看-50、-30日志,Oracle 23 ai free中无此视图,DBA_EXTENDED_TXN_DETAILS,也不存在
SELECT * FROM SYS.RESV$JOURNAL WHERE table_name='inv_test';
四、适用场景和特性
1. 最佳场景
场景类型 | 案例 | 性能提升 |
OLTP高频更新 | 库存扣减(秒杀) | 并发提升10倍+ |
金融交易 | 账户余额实时更新 | 延迟降至毫秒级 |
计数系统 | 点赞/投票计数 | 无锁写入 |
2. 并发控制的新范式
- OLTP系统:彻底消除热点数据锁竞争,吞吐量提升10倍+。
- 开发者体验:无需重写应用代码,声明列属性即获性能飞跃。
- 未来生态:与AI Vector Search协同处理结构化+非结构化混合负载