南大通用GBase8sV8.8隔离级别的介绍
在高并发的数据库生产环境中,多个事务同时读写数据时,如何平衡数据一致性与系统并发性能,是每个DBA和开发者都必须面对的核心问题。GBase 8s V8.8 提供了丰富的隔离级别机制,让我们能够根据业务场景灵活控制事务间的可见性与锁行为。
本文将系统梳理GBase 8s支持的五种隔离级别,从原理、优缺点到双会话实验示例,带你彻底搞懂脏读、不可重复读、幻读等经典问题在GBase 8s中的表现,并给出生产环境的选型建议。
一、隔离级别概述
多个同时运行的事物之间不会互相影响彼此的执行
每个用户感觉自己的事务都是排他的执行
同时发生的其他事务应该出现在当前事务的前或者后
类似一个事务中的“序列”
1.脏读
设置方式:SET ISOLATION TO DIRTY READ;
- 采用这种隔离级别,数据库服务器不会分配任何锁
- 查询过程中,可以查询到任何数据row, 甚至那些被修改但尚未提交的记录
- Dirty-read隔离级别将产生幻影读,不可重复读的问题
- 非日志数据库中只有Dirty-read一种隔离级别
Dirty-read 隔离级别应用场景
静态表 (no updates, read-only tables)
速度比100% 准确更重要的情况
不能等待锁的释放
2.可提交读
设置方式:SET ISOLATION TO COMMITTED READ;
- 确保读取的所有记录都是提交到数据库
- 能避免读到脏数据
- 读到的所有的记录都是已提交的
- 当一个进程读完记录后,其他进程就可以修改
在读取数据前,数据库server尝试在记录上加共享锁
加锁前,需要先检查是否可以对对象加共享锁,但是不加
如果可以加锁,则要保证要加锁的记录没有其他进程正在更新
- 当记录正在更新时,记录上有排他锁,此时我们不能对这些记录加共享锁
3.游标读
设置方式:SET ISOLATION TO CURSOR STABILITY;
- 非更新CURSOR, 游标读的所有记录加上共享锁
- 共享锁一致将保持,直到下一行记录被读取
- 通过游标检索数据, 共享锁将一直保持到执行下一个 FETCH语句
- 不仅可以看到提交的记录,也可以保证看到的记录不会被更新
- 其他进程不能更新 (UPDATE or DELETE) 你所看到的记录
- 当移动到下一行时,锁才会释放,记录就可以进行修改
4.可重复读
设置方式:SET ISOLATION TO REPEATABLE READ;
- 数据库在读取的记录上加共享锁,验证是否可以读取数据
- 直到事务提交,锁才能释放
- 其他用户可以读取数据,但是不能修改
- 可以保证在同一事务中前后两次读取记录是一致的
当你必须要信任所有读取的记录,保证记录不被修改。我们可以采用repeatable read.
举例:
关键的, 统计数据 (如:银行账号的余额情况)
关联查询多个表
5.最后可提交读
设置方式: SET ISOLATION TO COMMITTED READ LAST COMMITTED
- 解决Committed Read的不足: 记录被锁,其他进程需要等待;更新的记录不能访问,直到提交,除非采用脏读;由于需要等待更新记录的提交,应用程序性能较低;如果采用脏读则会得到非预期的结果;死锁的发生,也会消耗很多时间
解决办法: Last Committed Read - Committed Read 隔离级别, 由于其他session在记录上加了排他锁(行级锁),将导致读取数据失败SET ISOLATION COMMITTED READ 的LAST COMMITTED 可以降低读取数据的锁冲突
6.监控session的隔离级别指令
使用:onstat –g sql 或者 onstat –g ses
查看sid的详细信息:onstat –g sql sid
7.关于隔离级别的示例
(1)脏读
优点:并发性高,不需要锁资源
缺点:脏读、幻影读、不可重复读
Session1 | Session2 | Session2 output |
Begin work; | Begin work; |
|
| Select * from lock_tb where c1<=3; | 1 abc |
Update lock_tb set c2='new' where c1=1; | Select * from lock_tb where c1<=3; | 1 new |
Insert into lock_tb values(3, 'new'); | Insert into lock_tb values(3, 'new'); Select * from lock_tb where c1<=3; | 1 new |
Rollback; |
|
|
| Select * from lock_tb where c1<=3; | 1 abc |
--或者 |
|
|
| Select * from lock_tb where c1<=3; | 1 new |
(2)可提交读
优点:相对dirty read能避免脏读的问题
缺点:并发性较dirty read有所降低,容易出现锁等待引起的问题。不能解决幻影读、不可重复读
Session1 | Session2 | Session2 output |
| Begin work; --With committed read default set lock mode to wait 5; | Begin work; set isolation committed read; set lock mode to wait 5; | |
| Select * from lock_tb where c1<=3; | 1 abc 2 abc | |
| Update lock_tb set c2='new' where c1=1; | ||
| Select * from lock_tb where c1<=3; | SQL Error -244 锁错误 |
以上现象解释如下:
- 因为 session2采用committed read对读取的记录不加S-LOCK,所以session1可以对该记录进行更新,并在该记录上加X-LOCK
- session2采用committed read对读取的记录试图加S-LOCK,但检测到c1=1的记录上有X-LOCK,出现锁等待事件,超过锁等待时长5秒后报告锁等待超时错误:Error -244
Session1 | Session2 | Session2 output |
| Begin work; --With committed read default Set lock mode to wait 5; | Begin work; Set isolation committed read; Set lock mode to wait 5; | |
| Select * from lock_tb; | 1 abc 2 abc | |
| update lock_tb set c2='new' where c1=1; insert into lock_tb values(3, 'new'); | ||
| Commit work; | ||
| Select * from lock_tb; 当session 1结束后执行 | 1 new 2 abc 3 new | |
| --或者 Roll back; | ||
| Select * from lock_tb; 当session 1结束后执行 | 1 abc 2 abc |
(3)可重复读
优点:能够解决脏读、幻影读和不可重复读的问题
缺点:只能串行执行,并发性低,需要对读取的记录加s-lock,容易引起锁相关的问题
Session1 | Session2 | Session2 output |
| Begin work; --With committed read default set lock mode to wait 5; Begin work; set isolation repeatable read; Set lock mode to wait 5; | Begin work; set isolation repeatable read; Set lock mode to wait 5; | |
| Update lock_tb set c2='new' where c1=1; | ||
| Select * from lock_tb where c1<=3; | SQL Error -244 锁错误 | |
| Insert into lock_tb values(3, 'new'); | ||
| Commit work; |
以上原因如下:
- Session1对修改的的记录(c1=1)加X-LOCK
- session2采用repeatable read需要对读取的记录(c1<=3)加S-LOCK,但是记录c1=1已经被加上了x-lock,故出现锁等待事件,当锁等待超时,则报告锁错误:Error -244
Session1 | Session2 | Session2 output |
| Begin work; --With committed read default Set lock mode to wait 5; | Begin work; Set isolation repeatable read; Set lock mode to wait 5; | |
| Update lock_tb set c2='new‘ where c1=1; | ||
| Insert into lock_tb values(3, 'new'); | ||
| Commit work; | ||
| Select * from lock_tb ; | 1 new 2 abc 3 new | |
| --或者: Rollback; | Select * from lock_tb ; | 1 abc 2 abc |
(4)最后可提交读
优点:能解决脏读问题,并发性高;不需要锁资源,能避免committed read带来的锁问题,
缺点:幻影读、不可重复读
Session1 | Session2 | Session2 output |
| Begin work; --With committed read default set lock mode to wait 5; | Begin work; set isolation to committed read last committed; Set lock mode to wait 5; | |
| Select * from lock_tb where c1>=3; | 1 abc 2 abc | |
| update lock_tb set c2='new' where c1=1; | ||
| select * from lock_tb where c1>=3; | 1 abc 2 abc | |
| Update lock_tb set c2='new' where c1=1; | ||
| Select * from lock_tb where c1>=3; | 1 abc 2 abc | |
| rollback; | ||
| Select * from lock_tb where c1>=3; | 1 abc 2 abc | |
| --或者 Commit work; | ||
| Select * from lock_tb where c1>=3; | 1 new 2 abc 3 new |
以上。如果你在GBase 8s的实际应用中遇到了棘手的锁冲突或隔离级别相关的问题,欢迎在南大通用技术社区分享讨论,我们一起寻找最优解!
热门帖子
- 12025-12-01浏览数:183737
- 22023-05-09浏览数:26443
- 42023-09-25浏览数:20111
- 52020-05-11浏览数:18840