KingbaseES 串行化隔离级别引起的阻塞分析
前言
这是实际生产环境中遇到的一个问题,前端业务有如下报错:
could not serialize access due to read/write dependencies among transactions
Detail: Reason code: Canceled on coinflict out to pivot as a pivot 27892139, during read.
串行化隔离级别的含义
可串行化隔离级别保证了数据库中事务是按照顺序串行执行,而非并行。数据库可以防止所有可能的竞争条件。
串行化隔离级别可能会遇到的阻塞情况
创建测试数据
create table idx (id int,ci character varying );
create index on idx(id);
insert into idx select generate_series(1,10000);
TEST=# insert into idx select generate_series(1,10000);
INSERT 0 10000
修改 KINGBASE.CONF中
default_transaction_isolation = 'SERIALIZABLE'
重启数据库
1.Read触发报错,事务执行时间顺序如下表:
时间点 | T1 | T2 | T3 |
---|---|---|---|
t1 | begin; | ||
t2 | select * from idx where id = 1; | ||
t3 | begin; | ||
t4 | update idx set ci='x' where id =1; | ||
t5 | begin; | ||
t6 | update idx set ci='x' where id=1000; | ||
t7 | commit; | ||
t8 | select * from idx where id = 1000; |
T2在执行select时候有如下报错
TEST=# select * from idx where id = 1000;
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on conflict out to pivot 1771, during read.
HINT: The transaction might succeed if retried.
报错原因是T2事务在t4时间点还没结束,然后开启T3事务,而当T3事务结束以后,T2事务再执行select查询就违反了串行隔离级别的定义。
当一个事务处于可串行化级别, 一个select查询只能看到在事务开始之前提交的数据而永远看不到未提交的数据或事务执行中其他并行事务提交的修改。
2.Write触发报错,事务执行时间顺序如下表:
时间点 | T1 | T2 | T3 |
---|---|---|---|
t1 | begin; | ||
t2 | select * from idx where id = 1; | ||
t3 | begin; | ||
t4 | select * from idx where id = 1000; | ||
t5 | begin; | ||
t6 | update idx set ci='x' where id =1000; | ||
t7 | commit; | ||
t8 | update idx set ci ='x' where id =1; |
T2在执行update时候有如下报错
TEST=# update idx set ci ='x' where id =1;
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during write.
HINT: The transaction might succeed if retried.
从表格的时间线我们可以看出,对于serialize串行隔离级别,我们必须老老实实一个个事务按顺序进行,如不然就会出现如上报错。
3.Commit触发报错,事务执行时间顺序如下表:
时间点 | T1 | T2 |
---|---|---|
t1 | begin; | |
t2 | select * from idx where id =1; | |
t3 | update idx set ci = 'x' where id =1000; | |
t4 | begin; | |
t5 | select * from idx where id = 1000; | |
t6 | update idx set ci ='x' where id =1; | |
t7 | commit; | |
t8 | commit; |
T1在执行commit时候有如下报错
TEST=# commit;
ERROR: could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt.
HINT: The transaction might succeed if retried.
总结
一个可串行化的事务在可串行化事务开始之后不能更改被其他事务更改过的行。
当应用收到这样的错误信息时,它应该退出当前的事务然后从头开始进行整个事务。请注意只有更新事务才需要报错重试,只读事务从来没有串行化冲突。
可串行化事务隔离级别必须保证每个事务都看到一个完整的数据库的视图。如果并行更新令数据库不能维持串行执行的样子,那么应用必须准备重试事务。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?