• Postgresql源码(88)简单复习HOT更新流程


    之前的分析:
    《Postgresql源码(57)HOT更新为什么性能差距那么大?》
    相关
    《Postgresql源码(32)Btree索引分裂前后结构差异对比》

    1 概要

    复习HOT更新流程:

    内容主要分为两部分:

    • 索引查询
    • HOT更新

    关于索引查询借用之前的一张图:
    在这里插入图片描述

    关于HOT更新引用www.interdb.jp的一张图:
    在这里插入图片描述

    2 场景构造

    149369条数据刚刚好索引从两层升级为三层:

    create table t8(id int primary key, info text);
    truncate t8;
    insert into t8 select generate_series(1,149370), md5(random()::text);
    
    • 1
    • 2
    • 3

    HOT更新哪里合适:更新最后一个页面的数据有空闲位置。

    -- select relpages from pg_class where relname = 't8';   -- 1245
    
    update t8 set info='87b5696b76a041a41495da0000000000' where id = 149370;
    
    • 1
    • 2
    • 3

    数据页面结构
    在这里插入图片描述

    索引页面结构
    在这里插入图片描述

    3 hot update

    update t8 set info='87b5696b76a041a41495da0000000000' where id = 149370;

    3.1 走索引找到数据

    执行器进入索引堆栈的路径:
    在这里插入图片描述
    爬索引树参考之前的分析:https://blog.csdn.net/jackgo73/article/details/122875493

    3.2 找到数据后进入update堆栈

    找到数据后,进入ExecUpdate前必须拿到元组的ItemPointer,即元组的位置
    在这里插入图片描述

    ExecModifyTable
      context.planSlot = ExecProcNode(subplanstate);  // 递归语法树进行indexscan找到元组
      // 返回
      // TupleTableSlot
      // {type = T_TupleTableSlot, tts_flags = 16, tts_nvalid = 2, tts_ops = 0xcbb240 , 
      //    tts_tupleDescriptor = 0x23b29a0, tts_values = 0x23b2cb0,
      //    tts_isnull = 0x23b2cc0, tts_mcxt = 0x23b20f0, 
      //    tts_tid = {ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, tts_tableOid = 0}
    
    
      ExecGetJunkAttribute(slot,                         // tts封装的元组,前面索引查询的结果
                           resultRelInfo->ri_RowIdAttNo  // 2:表示第二列
                           );      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    返回
    在这里插入图片描述

    ExecModifyTable
      ...
      ExecUpdate(...,{ip_blkid = {bi_hi = 0, bi_lo = 1244}, ip_posid = 90},...)
    
    • 1
    • 2
    • 3

    3.3 开始update

    TM_Result
    heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
    			CommandId cid, Snapshot crosscheck, bool wait,
    			TM_FailureData *tmfd, LockTupleMode *lockmode)
    {
        ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第一步:计算hot_attrs标记所有索引列位置

    hot_attrs = 0100000000

    • 00000000:表示系统列站位
    • 01:表示第一列上有索引
    	hot_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_ALL);
    	key_attrs = RelationGetIndexAttrBitmap(relation, INDEX_ATTR_BITMAP_KEY);
    	id_attrs = RelationGetIndexAttrBitmap(relation,
    										  INDEX_ATTR_BITMAP_IDENTITY_KEY);
    	interesting_attrs = NULL;
    	interesting_attrs = bms_add_members(interesting_attrs, hot_attrs);
    	interesting_attrs = bms_add_members(interesting_attrs, key_attrs);
    	interesting_attrs = bms_add_members(interesting_attrs, id_attrs);
    	...
    	block = ItemPointerGetBlockNumber(otid);
    	buffer = ReadBuffer(relation, block);
    	page = BufferGetPage(buffer);
    	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第二步:逻辑位置转换为物理偏移

    逻辑位置:
    ItemPointer={ip_blkid = {bi_hi = 0, bi_lo = 1244}, ip_posid = 90}
    1244号页面,第90个item pointer。

    物理位置:
    (gdb) p ((PageHeader)page)->pd_linp[89]
    $59 = {lp_off = 2432, lp_flags = 1, lp_len = 61}

    	lp = PageGetItemId(page, ItemPointerGetOffsetNumber(otid));
    	oldtup.t_tableOid = RelationGetRelid(relation);
    	oldtup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
    	oldtup.t_len = ItemIdGetLength(lp);
    	oldtup.t_self = *otid;
    
    	newtup->t_tableOid = RelationGetRelid(relation);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第三步:找到哪些索引列被更新了

    interesting_attrs传入是为0100000000在函数内被破坏掉了。

    传出的modified_attrs=0,因为根据更新的元组,发现没有索引列被更新了。

    	modified_attrs = HeapDetermineColumnsInfo(relation, interesting_attrs,
    											  id_attrs, &oldtup,
    											  newtup, &id_has_external);
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第四步:新元组也可以放到旧页面上且没有更新任何索引列

    modified_attrs = 0:索引列没有更新的。

    hot_attrs = 0100000000:索引列位置。

    可以做HOT更新:

    use_hot_update = true;

    
    	if (newbuf == buffer)
    	{
    		if (!bms_overlap(modified_attrs, hot_attrs))
    			use_hot_update = true;
    	}
    	else
    	{
    		/* Set a hint that the old page could use prune/defrag */
    		PageSetFull(page);
    	}
    
    	if (use_hot_update)
    	{
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    第五步:标记HEAP_HOT_UPDATED完成Insert

    增加标记位HEAP_HOT_UPDATED、HEAP_ONLY_TUPLE

    		HeapTupleSetHotUpdated(&oldtup);
    		HeapTupleSetHeapOnly(heaptup);
    		HeapTupleSetHeapOnly(newtup);
    	}
    
    
    	RelationPutHeapTuple(relation, newbuf, heaptup, false); /* insert new tuple */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    stm32使用串口打印
    运维Shell脚本小试牛刀(十二):awk编程尝鲜
    4931: 二叉树遍历(先序中序还原树+后续遍历)
    TPA4045-ASEMI光伏防回流二极管TPA4045
    Acwing_98
    如何使用递归,递归使用的技巧详解
    小程序源码:王者战力查询,游戏扫码登录,王者巅峰信息查询等等支持流量主收益和CPS收益-多玩法安装简单
    软考高级五大证书,哪个更值得考?
    c# 容器变换
    Go 语言变量
  • 原文地址:https://blog.csdn.net/jackgo73/article/details/127731139