列存表并发更新时时的锁等待问题原理
当开启transaction,执行updata的语句执行成功,不执行commit或rollback;再开启另一个窗口,执行upadate语句,会出现失败(报错:锁等待超时)的情况,但是如果对于上一个窗口执行rollback,此窗口update可以执行成功,该种情况应考虑该表是否为列存表。
【问题根因】
如果使用的是列存表,在事务中执行update操作时,是以CU为单位进行加锁的,所以在事务未提交时并发更新同一CU的其他数据时会出现锁等待的情况,等待超时时会出现报错
【机制原理】
1. CU为压缩单元(Compress Unit),列存储的最小单位,导入数据时生成,生成后数据固定不可更改,单个CU最多存储1列60000行数据。同一列的CU连续存储在一个文件中,当大于1G时,切换到新文件中。其中的Ctid字段标识列存表的一行,由cu_id和CU内行号(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场景建议使用行存表
- 点赞
- 收藏
- 关注作者
评论(0)