Skip to content

DB2 事务与锁机制

事务概述

事务是数据库操作的基本单位,它将多个数据库操作组合成一个逻辑单元,要么全部成功执行,要么全部失败回滚。DB2严格遵循ACID特性:

  • 原子性(Atomicity):事务要么全部执行,要么全部不执行
  • 一致性(Consistency):事务执行前后数据库状态保持一致
  • 隔离性(Isolation):多个事务并发执行时互不干扰
  • 持久性(Durability):事务提交后,修改永久保存到数据库

事务控制语句

1. 开始事务

在DB2中,事务默认自动开始,也可以显式开始:

sql
-- 显式开始事务
START TRANSACTION;

-- 或者使用BEGIN原子块
BEGIN ATOMIC
  -- 事务操作
END;

2. 提交事务

提交事务将所有修改永久保存到数据库:

sql
-- 提交事务
COMMIT;

-- 提交并释放锁
COMMIT RELEASE;

3. 回滚事务

回滚事务将撤销所有未提交的修改:

sql
-- 回滚事务
ROLLBACK;

-- 回滚到保存点
ROLLBACK TO SAVEPOINT <savepoint_name>;

4. 保存点

保存点允许在事务中设置标记点,以便部分回滚:

sql
-- 设置保存点
SAVEPOINT <savepoint_name>;

-- 回滚到保存点
ROLLBACK TO SAVEPOINT <savepoint_name>;

-- 释放保存点
RELEASE SAVEPOINT <savepoint_name>;

锁机制

1. 锁类型

DB2使用多种类型的锁来控制并发访问:

锁类型描述适用对象
共享锁(S)允许读取但不允许修改表、行、页
排他锁(X)不允许其他事务读取或修改表、行、页
更新锁(U)用于更新操作的中间状态行、页
意向共享锁(IS)表示对下层对象持有共享锁的意向
意向排他锁(IX)表示对下层对象持有排他锁的意向
共享意向排他锁(SIX)表示对表持有共享锁,对下层对象持有排他锁的意向

2. 锁粒度

DB2支持多种锁粒度,从行级到表级:

  • 行级锁:最细粒度的锁,允许多个事务同时访问同一表的不同行
  • 页级锁:对数据页或索引页加锁,适合批量操作
  • 表级锁:对整个表加锁,适合大规模数据操作

3. 锁升级

当锁数量达到一定阈值时,DB2会将多个细粒度锁升级为粗粒度锁,以减少锁管理开销:

sql
-- 查看锁升级配置
db2 get db cfg for <database_name> | grep -i lock

-- 修改锁升级阈值
db2 update db cfg for <database_name> using locklist <value>
db2 update db cfg for <database_name> using maxlocks <percentage>

事务隔离级别

DB2支持四种事务隔离级别,用于控制并发事务之间的可见性:

1. 未提交读(Uncommitted Read)

  • 允许事务读取其他事务未提交的修改
  • 可能导致脏读、不可重复读和幻读
  • 隔离级别最低,并发性能最高
sql
-- 设置未提交读隔离级别
SET TRANSACTION ISOLATION LEVEL UNCOMMITTED READ;

2. 游标稳定性(Cursor Stability)

  • 保证正在读取的行被锁定
  • 防止脏读,但可能导致不可重复读和幻读
  • DB2默认的隔离级别
sql
-- 设置游标稳定性隔离级别
SET TRANSACTION ISOLATION LEVEL CURSOR STABILITY;

3. 读稳定性(Read Stability)

  • 保证事务期间读取的所有行被锁定
  • 防止脏读和不可重复读,但可能导致幻读
sql
-- 设置读稳定性隔离级别
SET TRANSACTION ISOLATION LEVEL READ STABILITY;

4. 可重复读(Repeatable Read)

  • 保证事务期间读取的所有数据被锁定
  • 防止脏读、不可重复读和幻读
  • 隔离级别最高,并发性能最低
sql
-- 设置可重复读隔离级别
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

锁监控与管理

1. 查看锁信息

sql
-- 查看当前锁信息
db2 get snapshot for locks on <database_name>

-- 使用db2pd查看锁
db2pd -db <database_name> -locks

-- 查看应用程序持有的锁
db2pd -db <database_name> -applications -locks

2. 查看死锁

sql
-- 查看死锁信息
db2 get snapshot for database on <database_name> | grep -i deadlock

-- 查看死锁历史
db2pd -db <database_name> -deadlock

-- 启用死锁监控
db2 update dbm cfg using dft_mon_lock on

3. 配置锁超时

sql
-- 查看锁超时配置
db2 get db cfg for <database_name> | grep -i locktimeout

-- 修改锁超时时间(秒)
db2 update db cfg for <database_name> using locktimeout 30

并发控制策略

1. 乐观并发控制

乐观并发控制假设冲突很少发生,只在提交时检查冲突:

sql
-- 使用乐观并发控制
SELECT * FROM <table_name> WHERE id = ? FOR UPDATE OPTIMISTIC;

2. 悲观并发控制

悲观并发控制假设冲突经常发生,在读取时就加锁:

sql
-- 使用悲观并发控制
SELECT * FROM <table_name> WHERE id = ? FOR UPDATE;

3. 行版本控制

DB2使用行版本控制来提高并发性能:

  • 为每行数据维护多个版本
  • 读取操作不需要加锁,只读取快照数据
  • 写入操作只需要锁定当前版本

锁机制最佳实践

1. 选择合适的隔离级别

  • 大多数应用使用默认的游标稳定性(CS)隔离级别
  • 只在必要时使用更高的隔离级别
  • 考虑使用未提交读(UR)提高只读查询性能

2. 优化锁粒度

  • 使用行级锁提高并发性能
  • 避免长时间持有锁
  • 尽量缩小事务范围
  • 避免在事务中执行非数据库操作

3. 防止死锁

  • 以相同顺序访问数据库对象
  • 避免事务之间的循环依赖
  • 保持事务简短
  • 使用适当的锁超时设置

4. 监控锁使用情况

  • 定期监控锁等待情况
  • 识别锁热点和瓶颈
  • 优化频繁发生锁冲突的查询
  • 考虑使用分区表减少锁竞争

事务日志管理

1. 事务日志配置

sql
-- 查看事务日志配置
db2 get db cfg for <database_name> | grep -i log

-- 修改日志文件大小(4KB页)
db2 update db cfg for <database_name> using logfilsiz 1000

-- 修改主日志文件数量
db2 update db cfg for <database_name> using logprimary 10

-- 修改辅助日志文件数量
db2 update db cfg for <database_name> using logsecond 20

2. 日志模式

DB2支持两种日志模式:

  • 循环日志:日志文件循环使用,不支持前滚恢复
  • 归档日志:日志文件归档保存,支持前滚恢复
sql
-- 启用归档日志模式
db2 update db cfg for <database_name> using logarchmeth1 "DISK:/path/to/archive"

版本差异

版本事务与锁机制特性变化
DB2 9.x引入乐观并发控制
DB2 10.x增强了锁监控和死锁检测
DB2 11.1引入行版本控制增强
DB2 11.5优化了锁管理器性能
Db2 12.x引入智能锁管理和自动优化

常见问题(FAQ)

Q1: 如何选择合适的事务隔离级别?

A1: 选择隔离级别应考虑:

  • 应用程序的并发需求
  • 数据一致性要求
  • 性能影响
  • 大多数应用使用游标稳定性(CS)隔离级别

Q2: 什么是死锁?如何避免?

A2: 死锁是指两个或多个事务互相等待对方释放锁的状态。避免方法:

  • 以相同顺序访问数据库对象
  • 保持事务简短
  • 使用适当的锁超时设置
  • 避免在事务中执行复杂操作

Q3: 锁升级会带来什么问题?如何避免?

A3: 锁升级可能导致并发性能下降。避免方法:

  • 增大锁列表大小(locklist)
  • 提高锁升级阈值(maxlocks)
  • 优化查询,减少锁持有时间
  • 考虑使用行级锁提示

Q4: 如何监控和分析锁等待?

A4: 监控方法:

  • 使用db2pd工具查看锁信息
  • 查看锁快照
  • 启用锁监控
  • 分析死锁历史

Q5: 事务日志满了怎么办?

A5: 处理方法:

  • 增大日志文件大小或数量
  • 启用归档日志模式
  • 检查是否有长事务
  • 优化事务设计,减少事务长度

Q6: 如何提高事务并发性能?

A6: 提高并发性能的方法:

  • 使用合适的隔离级别
  • 优化锁粒度
  • 保持事务简短
  • 避免长时间持有锁
  • 使用乐观并发控制
  • 优化查询性能

Q7: 什么是脏读、不可重复读和幻读?

A7: 定义如下:

  • 脏读:读取到其他事务未提交的修改
  • 不可重复读:同一事务中多次读取同一数据,结果不一致
  • 幻读:同一事务中多次查询,结果集行数不一致

Q8: 如何配置事务日志?

A8: 配置事务日志的步骤:

  • 选择合适的日志模式(循环或归档)
  • 设置适当的日志文件大小和数量
  • 配置日志归档路径
  • 定期监控日志使用情况

Q9: 如何处理长事务?

A9: 处理长事务的方法:

  • 分割长事务为多个短事务
  • 避免在事务中执行非数据库操作
  • 优化查询性能,减少事务执行时间
  • 考虑使用异步处理

Q10: DB2的默认隔离级别是什么?

A10: DB2的默认隔离级别是游标稳定性(Cursor Stability),它在保证一定一致性的同时提供较好的并发性能,适合大多数应用场景。