- 专栏内容:postgresql内核源码分析
- 个人主页:senllang的主页
- 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
文章目录
前言
本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。
加速查询的方式
大的方面来说,主要是减少检索的数据量,那如何减少呢?
一方面是优化执行计划,逻辑层面减少处理的数据量,另一方面,每个tuple还要从表文件中查找位置,如何能更精确找到位置,而不是遍历整个文件。
索引主是解决第二种情况,同时索引中不会记录真实的数据,所以它的大小非常小,可以很快加载到内存进行查询,这样大大减少了查询过程中的磁盘IO;
下面我们来看索引是如何做到的?
索引记录的内容
不同索引记录的内容不一致,下面以btree索引为例:
看一下insert操作代码:
/* insert the tuple normally */
table_tuple_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
slot, estate, false,
false, NULL, NIL);
在数据insert到block后,heapam_tuple_insert中,会将tuple的tid记录到slot中,也就是tuple的位置信息;
/* Perform the insertion, and copy the resulting ItemPointer */
heap_insert(relation, tuple, cid, options, bistate);
ItemPointerCopy(&tuple->t_self, &slot->tts_tid);
在ExecInsertIndexTuples中,会将slot->tts_tid再传到索引里面,形成索引记录
ItemPointer tupleid = &slot->tts_tid;
然后下面开始索引记录的插入操作
satisfiesConstraint =
index_insert(indexRelation, /* index relation */
values, /* array of index Datums */
isnull, /* null flags */
tupleid, /* tid of heap tuple */
heapRelation, /* heap relation */
checkUnique, /* type of uniqueness check to do */
indexUnchanged, /* UPDATE without logical change? */
indexInfo); /* index AM may need this */
生成indextuple, 使用以下结构,有两部分:一是heaptuple tid, 二是flag和长度;
typedef struct IndexTupleData
{
ItemPointerData t_tid; /* reference TID to heap tuple */
/* ---------------
* t_info is laid out in the following fashion:
*
* 15th (high) bit: has nulls
* 14th bit: has var-width attributes
* 13th bit: AM-defined meaning
* 12-0 bit: size of tuple
* ---------------
*/
unsigned short t_info; /* various info about tuple */
} IndexTupleData; /* MORE DATA FOLLOWS AT END OF STRUCT */
typedef IndexTupleData *IndexTuple;
索引与数据如何对应
- 首先是HOT, heap only tuple。
理论上每个数据tuple都对应一条索引,postgresql在此处做了优化,对于update, 如果新老版本不跨page的话,索引不会新增,通过索引找到老版本,再通过版本链找到最新tuple,这类tuple就是heap only tuple。
- 对于新老版本跨page存储的,就会新增一条索引,这就会造成索引的膨胀。
索引与数据创建顺序
从代码来看,先将数据插入数据块,在table_tuple_insert操作完成后,拿到tuple的位置信息后,再进行索引操作。
/* insert the tuple normally */
table_tuple_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
/* insert index entries for tuple */
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
slot, estate, false,
false, NULL, NIL);
结尾
作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!