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

2022-11-16 19:11:40 浏览数 (1)

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

1 概要

复习HOT更新流程:

内容主要分为两部分:

  • 索引查询
  • HOT更新

关于索引查询借用之前的一张图:

关于HOT更新引用www.interdb.jp的一张图:

2 场景构造

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

代码语言:javascript复制
create table t8(id int primary key, info text);
truncate t8;
insert into t8 select generate_series(1,149370), md5(random()::text);

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

代码语言:javascript复制
-- 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://cloud.tencent.com/developer/article/2000739

3.2 找到数据后进入update堆栈

找到数据后,进入ExecUpdate前必须拿到元组的ItemPointer,即元组的位置

代码语言:javascript复制
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:表示第二列
                       );      

返回

代码语言:javascript复制
ExecModifyTable
  ...
  ExecUpdate(...,{ip_blkid = {bi_hi = 0, bi_lo = 1244}, ip_posid = 90},...)

3.3 开始update

代码语言:javascript复制
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:表示第一列上有索引
代码语言:javascript复制
	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_linp89

$59 = {lp_off = 2432, lp_flags = 1, lp_len = 61}

代码语言:javascript复制
	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,因为根据更新的元组,发现没有索引列被更新了。

代码语言:javascript复制
	modified_attrs = HeapDetermineColumnsInfo(relation, interesting_attrs,
											  id_attrs, &oldtup,
											  newtup, &id_has_external);

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

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

hot_attrs = 0100000000:索引列位置。

可以做HOT更新:

use_hot_update = true;

代码语言:javascript复制
	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

代码语言:javascript复制
		HeapTupleSetHotUpdated(&oldtup);
		HeapTupleSetHeapOnly(heaptup);
		HeapTupleSetHeapOnly(newtup);
	}


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

0 人点赞