技术实践丨列存表并发更新时的锁等待问题原理

摘要:当开启transaction,执行updata的语句执行成功,不执行commit或rollback;再开启另外一个窗口,执行upadate语句,会出现失败(报错:锁等待超时)的状况,可是若是对于上一个窗口执行rollback,此窗口update能够执行成功,该种状况应考虑该表是否为列存表。

本文分享自华为云社区《列存表并发更新时时的锁等待问题原理》,原文做者:PFloyd 。segmentfault

当开启transaction,执行updata的语句执行成功,不执行commit或rollback;再开启另外一个窗口,执行upadate语句,会出现失败(报错:锁等待超时)的状况,可是若是对于上一个窗口执行rollback,此窗口update能够执行成功,该种状况应考虑该表是否为列存表。并发

【问题根因】

若是使用的是列存表,在事务中执行update操做时,是以CU为单位进行加锁的,因此在事务未提交时并发更新同一CU的其余数据时会出现锁等待的状况,等待超时的时候会出现报错spa

【机制原理】

1.CU为压缩单元(Compress Unit),列存储的最小单位,导入数据时生成,生成后数据固定不可更改,单个CU最多存储1列60000行数据。同一列的CU连续存储在一个文件中,当大于1G时,切换到新文件中。其中的Ctid字段标识列存表的一行,由cu_id和CU内行号(cu_id, offset)组成;一次性写入的多条的数据位于同一CU。blog

2. 为了防止页面同一个元组被两个事务同时更新,在进行update时,都会加上行级锁,对于行存来讲是对一行数据加锁,对于列存来讲就是对一个CU加锁,当一个事务update未提交时,其余事务是没法同时去更新同一CU的数据的。事务

3.进行update操做后,旧元组被标记为deleted,新元组会写到一个新的CU中get

【案例分析】

1.根据现场的报错信息,能够肯定是并发更新报错;进行update时,会申请行级锁,在申请行级锁以前会申请transactionid锁,等待超时后报错信息为:waiting for ShareLock on transaction xxx after ..msit

2.客户反应更新的并非同一条数据,id不一样,询问客户后得知出现问题的是列存表,查询更新的数据是否处于同一CU。
image.pngio

查询后发现处于同一CU,符合预期。社区

3.本地场景复现:class

起事务执行update操做:
image.png

事务未提交并发更新数据出现等待:
image.png

查询后发现两条数据位于同一cu:
image.png

【相关问题】

为何update成功一次以后,下一次update就不会互相等锁了?

这是由于update成功以后,旧数据被标记为deleted,新数据写入新的CU,这两条数据再也不是同一个CU了,也就不存在这种锁冲突

【处理方案】

列存表不适合频繁的update场景,列存频繁的update容易触发并发更新等锁超时,而且会致使小CU过多,而每一个CU都会扩展至8192字节进行对齐,从而致使磁盘空间迅速膨胀;频繁的点查或频繁update场景建议使用行存表。

点击关注,第一时间了解华为云新鲜技术~