zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

流程postgresql源码 简单 更新 复习 88 hot
2023-06-13 09:15:14 时间

之前的分析: 《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);

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

-- select relpages from pg_class where relname = 't8';   -- 1245

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

数据页面结构

索引页面结构

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 <TTSOpsVirtual>, 
  //    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:表示第二列
                       );      

返回

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

3.3 开始update

TM_Result
heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
			CommandId cid, Snapshot crosscheck, bool wait,
			TM_FailureData *tmfd, LockTupleMode *lockmode)
{
    ...

第一步:计算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);

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

逻辑位置: 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);

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

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

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

	modified_attrs = HeapDetermineColumnsInfo(relation, interesting_attrs,
											  id_attrs, &oldtup,
											  newtup, &id_has_external);

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

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)
	{

第五步:标记HEAP_HOT_UPDATED完成Insert

增加标记位HEAP_HOT_UPDATED、HEAP_ONLY_TUPLE

		HeapTupleSetHotUpdated(&oldtup);
		HeapTupleSetHeapOnly(heaptup);
		HeapTupleSetHeapOnly(newtup);
	}


	RelationPutHeapTuple(relation, newbuf, heaptup, false); /* insert new tuple */