• KingbaseESV8R6 snapshot too old的配置和测试


    背景

    书接上文,我们很好的理解了xmin和xid的区别。我们继续上文《KingbaseESV8R6不同隔离级下xmin的区别》来讨论 snapshot too old 的功能。

    当kingbaseES中有事务长时间持有backend_xmin,就会通过参数old_snapshot_threshold把快照中的lsn和当前事务数据块中的lsn做对比判断快照是否过旧。

    它并不关心是否存在backend_xid。

    [

    1. test=# show old_snapshot_threshold ;
    2. old_snapshot_threshold
    3. ------------------------
    4. -1
    5. (1 row)
    6. test=# alter system set old_snapshot_threshold= 1;
    7. ALTER SYSTEM

    [

    修改后重启数据库生效

    第一种情况: 包含xmin,没有申请xid的只读事务

    当持有xmin的query执行时间超过old_snapshot_threshold设置的阈值,并且读取到数据块的LSN大于快照存储的LSN时,报snapshot too old错误。

    [

    1. test=# create table e1(id int);
    2. CREATE TABLE
    3. test=# insert into e1 select generate_series(1,10000);
    4. INSERT 0 10000
    5. test=# create index idx_e1 on e1(id);
    6. CREATE INDEX
    7. session A:
    8. test=# begin transaction isolation level repeatable read;
    9. BEGIN
    10. 1号数据块的数据
    11. test=# select ctid,* from e1 where id=2;
    12. ctid | id
    13. -------+----
    14. (0,1) | 2
    15. (1 row)
    16. 另一个数据块的数据
    17. test=# select ctid,* from e1 where id=800;
    18. ctid | id
    19. --------+------
    20. (3,122) | 800
    21. (1 row)
    22. session B:
    23. 更新1号数据块的某条记录
    24. test=# update e1 set id=0 where id=2 returning ctid,*;
    25. ctid | id
    26. ---------+----
    27. (44,57) | 0
    28. (1 row)
    29. UPDATE 1
    30. 1分钟后
    31. session A:
    32. 访问未发生变化的数据块正常(因为id=1000走索引,所以不会扫描变更的数据块,这也是前面测试要建立索引的原因)
    33. test=# select ctid,* from e1 where id=800;
    34. ctid | id
    35. --------+------
    36. (3,122) | 800
    37. (1 row)
    38. 访问发生变化的数据块, 报错snapshot too old
    39. test=# select ctid,* from e1 where id=6;
    40. ERROR: snapshot too old
    41. test=# end;
    42. ROLLBACK

    [

    第二种情况:已申请xid的写,在隔离级别repeatable read/serializable事务,由于持有了xmin,并且repeatable read隔离级别,事务只要不结束,xmin不会变化。这种情况一样可能出现snapshot too old。

    [

    1. session A:
    2. test=# begin transaction isolation level repeatable read;
    3. BEGIN
    4. test=# insert into e1 values (1) returning ctid,*;
    5. ctid | id
    6. ---------+----
    7. (44,60) | 1
    8. (1 row)
    9. session B:
    10. 修改10号数据块的记录,导致10号数据块LSN变大
    11. test=# update e1 set id=1 where ctid::text ~ '^\(10,' returning ctid,*;
    12. ctid | id
    13. ----------+----
    14. (44,60) | 1
    15. (44,61) | 1
    16. (44,62) | 1
    17. (44,63) | 1
    18. ......
    19. UPDATE 226
    20. 1分钟后
    21. session A:
    22. 访问变更的数据块,报错
    23. test=# select * from e1 where ctid::text ~ '^\(10,';
    24. ERROR: snapshot too old
    25. test=# end;
    26. ROLLBACK

    [

    第三种情况:已申请xid的,在隔离级别read committed写事务,由于query开始时会重新生成快照,所以通常query持有的快照lsn大于或等于访问到的PAGE的LSN,则不会出现snapshot too old。因为这种情况不用去读取快照

    [

    1. session A:
    2. test=# begin transaction isolation level read committed;
    3. BEGIN
    4. test=# insert into e1 values (1) returning ctid,*;
    5. ctid | id
    6. ----------+----
    7. (46,1) | 1
    8. (1 row)
    9. session B:
    10. 修改44号数据块的记录,导致44号数据块LSN变大
    11. test=# update e1 set id=2 where ctid::text ~ '^\(44,' returning ctid,*;
    12. ctid | id
    13. ----------+----
    14. (45,60) | 2
    15. (45,61) | 2
    16. (45,62) | 2
    17. (45,63) | 2
    18. ......
    19. UPDATE 225
    20. 1分钟后
    21. session A:
    22. 访问变更的数据块,不会报错
    23. test=# select * from e1 where ctid::text ~ '^\(44,';
    24. id
    25. ----
    26. 0
    27. 0
    28. 0
    29. 0
    30. 0
    31. ......
    32. 但是如果QUERY本身访问时间长,并且访问到了快照创建以后被修改的页,还是会报错的。也就是访问到被修改的块的时候发现快照号lsn大于数据块lsn。这时候再去找以前的快照lsn已经发现超过1min过于旧。
    33. 模拟长SQL
    34. session A:
    35. with t as (select pg_sleep(100) ) select * from e1,t;
    36. 立即执行如下
    37. session B:
    38. test=# update e1 set id=7 where ctid::text ~ '^\(4,' returning ctid,*;
    39. ctid | id
    40. ----------+----
    41. (47,59) | 7
    42. (47,60) | 7
    43. (47,61) | 7
    44. ........
    45. UPDATE 226
    46. SQL报错
    47. session A:
    48. ERROR: snapshot too old

    [

    总结

    哪些情况可能导致snapshot too old

    包含了backend_xmin的事务,SQL的执行时间超过old_snapshot_threshold阈值,并且该SQL读取到了LSN超过快照存储的LSN的数据块时。

    \1. snapshot too old报错通常出现在非常耗时的SQL,同时读取的数据块在不断的变化。当读取时间在10点,但10:03分另外的事务更改了查询中的某个数据块,这时候,查询进行到10:04分时候发现这个数据块中的lsn大于快照中的lsn,就要去快照中读取过去版本,这是为了保证一致性读,如果查询开始到这个时刻超过snapshot too old就会报snapshot too old报错。

    \2. snapshot too old也可能出现在sys_dump备份数据库时,因为sys_dump使用的是repeatable read隔离级别,快照是在事务启动后的第一条SQL创建的,备份时间长的话,极有可能在备份过程中读取到LSN大于快照LSN的数据块,导致snapshot too old报错。

    快照与隔离级别

    • 已提交读:在该事务的每条SQL执行之前都会重新获取一次快照
    • 可重复读和可串行化:该事务只在第一条SQL执行之前获取一次快照

    img

    最后我们比对理解oracle中undo机制和kingbaseES中的snapshot too old。

    undo表空间的其中一个功能就是实现一致性读,当我在15:00开始查询,15:00的SCN号记录假设为100。那么在15:00这个时刻100就是最大的SCN,

    这里需要引入一个ITL的概念,ITL全称为 Interested Transaction List,是Oracle中数据块的组成部分,用来记录在这个数据块上发生的所有事务,一个ITL可以记录一个事务不论这个事务是否已经提交,一个数据块可以有多个ITL。如果这个事务已经提交了那么这个ITL的位置就可以被反复使用了,因为ITL类似记录,所以,有的时候也叫ITL槽位。ITL槽中会记录对应undo块的地址。可以说上面记录的100SCN号在15:00的时候大于所有的数据块上ITL记录的SCN(多个ITL取最大SCN)

    执行查询时,服务器进程扫描这个表中的数据块时,会把每个数据块ITL槽中最大的SCN与100进行比较,如果比100小则说明这个数据块没有被修改服务器进程直接进行数据读取即可。如果数据块ITL槽中的SCN大于100那么说明这个数据块在发起查询后被修改了,需要借助undo去获取15:00那个时刻数据块的数据。

    根据上面的例子,我是在15:00开始的查询,而数据是在15:03的时候被修改(这里不用考虑有没有提交,因为ITL只要数据块被修改就会有记录,那么这个查询就会去读undo数据块)。我们假设这个被修改的数据块是n号数据块,修改后n号数据块的ITL中记录的SCN是120,当服务器进程扫描到这个数据块进行SCN比较时发现这个数据块的SCN要大于100,服务器进程就知道了这个数据块在发起查询后被修改了,于是服务器进程到n号数据块的头部找到120对应的ITL槽,然后找到对应undo块的位置。将undo块中所存放的n号块修改前的数据取出再结合n号块里的当前数据行进而构建15:00这个时间点未被修改的数据块,这个被新构建的数据块被称为CR块(Consistant Read)。然后服务器进程扫描这个块,得到15:00一致性的数据,返回正确的数据。这就是一致性读

    因为我们的修改操作是delete,那么undo中对应的信息就是insert,insert将被删除的数据插入到CR块中,实现一致性读。undo记录的是buffer_cache中对应修改的前镜像,所以undo记录buffer修改的反向操作。

    好了,我们把undo中记录的事务槽中scn号比作snapshot中的记录lsn号。结合案例3,第三种情况最符合oracle中的场景应用,因为我们数据库默认的隔离级别也是read committed。如果QUERY时间很长,也就是访问到被修改的块的时候发现快照号lsn大于数据块lsn。也就是相当于oracle中buffer_cache中数据块的scn大于undo中对应这条记录的scn,就需要读取前镜像,在我们数据库就需要读取snapshot。而snapshot保存多久靠old_snapshot_threshold这个参数设置,在oracle中undo表空间保留策略靠undo_retention参数,默认15分钟。

    下文我们讨论垃圾回收受到参数old_snapshot_threshold参数的影响。

  • 相关阅读:
    EFK架构部署
    Tekton 设计简介 及 实践
    【Spring框架学习2】DI 依赖注入
    用Python做了几个爬虫私活,赚了
    java毕业设计选题基于SSM项目源代码实现的家政服务系统成品
    章节十六:复习与反爬虫
    机器学习-Kmeans
    户外耳机品牌哪个好、最新的户外耳机品牌排行
    05 CNN 猴子类别检测
    新版TCGAbiolinks包学习02:提取新版TCGA表达矩阵(tpm/count/fpkm)
  • 原文地址:https://blog.csdn.net/lyu1026/article/details/126074043