列存表并发更新时时的锁等待问题原理

当开启transaction,执行updata的语句执行成功,不执行commitrollback;再开启另一个窗口,执行upadate语句,会出现失败(报错:锁等待超时)的情况,但是如果对于上一个窗口执行rollback,此窗口update可以执行成功,该种情况应考虑该表是否为列存表。

【问题根因】

如果使用的是列存表,在事务中执行update操作时,是以CU为单位进行加锁的,所以在事务未提交时并发更新同一CU的其他数据时会出现锁等待的情况,等待超时时会出现报错

【机制原理】

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

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

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

【案例分析】

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

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


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

3.   本地场景复现:

起事务执行update操作:


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


查询后发现两条数据位于同一cu

【相关问题】

为什么update成功一次之后,下一次update就不会互相等锁了?

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

【处理方案】

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

(完)