当前位置: 首页 > news >正文

Postgresql源码(148)hash表的调试方法与技巧

处理实际问题时,经常需要调试hash表,例如需要gdb查看hash表中的数据结构等等。本篇结合实例给出一些使用方法技巧,后续遇到问题可以快速通过gdb查看hash表内容。

gdb脚本

$hashtable 修改为当前关注的hash表:

set $hashtable = PortalHashTable
set $i = 0
while ($i <= $hashtable->hctl->max_bucket)set $seg_idx = $i / $hashtable->hctl->dsizeset $seg_off = $i % $hashtable->hctl->dsizeset $bucket = $hashtable->dir[$seg_idx][$seg_off]set $cnt = 0set $b = $bucketwhile ($b != 0)printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)set $cnt = $cnt + 1set $b = $b->linkendprintf "bucket[%d] has %d keys\n", $i, $cntset $i = $i + 1
end

实例一:查看PortalHashTable

例如执行下面存储过程,会产生5个游标portal和1个匿名快执行portal,例如在执行时,我想打印每一个portal结构里面的变量,可以执行上面gdb脚本。

create table test_table(id int, name text);
insert into test_table values(1, 'abc');DO $$
DECLAREcur1 CURSOR FOR SELECT id, name FROM test_table;cur2 CURSOR FOR SELECT id, name FROM test_table;cur3 CURSOR FOR SELECT id, name FROM test_table;cur4 CURSOR FOR SELECT id, name FROM test_table;cur5 CURSOR FOR SELECT id, name FROM test_table;rec1 RECORD;rec2 RECORD;rec3 RECORD;rec4 RECORD;rec5 RECORD;
BEGINOPEN cur1;FETCH cur1 INTO rec1;RAISE NOTICE 'cur1: id=%, name=%', rec1.id, rec1.name;OPEN cur2;FETCH cur2 INTO rec2;RAISE NOTICE 'cur2: id=%, name=%', rec2.id, rec2.name;OPEN cur3;FETCH cur3 INTO rec3;RAISE NOTICE 'cur3: id=%, name=%', rec3.id, rec3.name;OPEN cur4;FETCH cur4 INTO rec4;RAISE NOTICE 'cur4: id=%, name=%', rec4.id, rec4.name;OPEN cur5;FETCH cur5 INTO rec5;RAISE NOTICE 'cur5: id=%, name=%', rec5.id, rec5.name;perform pg_sleep(30);
END
$$;

执行

set $hashtable = PortalHashTable
set $i = 0
while ($i <= $hashtable->hctl->max_bucket)set $seg_idx = $i / $hashtable->hctl->dsizeset $seg_off = $i % $hashtable->hctl->dsizeset $bucket = $hashtable->dir[$seg_idx][$seg_off]set $cnt = 0set $b = $bucketwhile ($b != 0)printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)set $cnt = $cnt + 1set $b = $b->linkendset $i = $i + 1
end

执行结果

(gdb) set $hashtable = PortalHashTable
(gdb) set $i = 0
(gdb) while ($i <= $hashtable->hctl->max_bucket)>    set $seg_idx = $i / $hashtable->hctl->dsize>    set $seg_off = $i % $hashtable->hctl->dsize>    set $bucket = $hashtable->dir[$seg_idx][$seg_off]>    set $cnt = 0>    set $b = $bucket>    while ($b != 0)>        printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)>        set $cnt = $cnt + 1>        set $b = $b->link>    end>    set $i = $i + 1>endbucket[1] key_addr[0]: 0x3751078
bucket[1] key_addr[1]: 0x3751020
bucket[2] key_addr[0]: 0x3750f70
bucket[5] key_addr[0]: 0x3750f18
bucket[6] key_addr[0]: 0x3750fc8
bucket[13] key_addr[0]: 0x37510d0

得到5个key值,结合hash表创建时使用的数据结构是PortalHashEnt

void
EnablePortalManager(void)
{HASHCTL		ctl;Assert(TopPortalContext == NULL);TopPortalContext = AllocSetContextCreate(TopMemoryContext,"TopPortalContext",ALLOCSET_DEFAULT_SIZES);ctl.keysize = MAX_PORTALNAME_LEN;ctl.entrysize = sizeof(PortalHashEnt);PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,&ctl, HASH_ELEM);
}

可以把每个地址转换为PortalHashEnt后查看值:

(gdb) p *(PortalHashEnt*)0x3751078
$33 = {portalname = "<UNNAMED PORTAL 11>", '\000' <repeats 44 times>, portal = 0x374d790}
(gdb) p *(PortalHashEnt*)0x3751020
$34 = {portalname = "<UNNAMED PORTAL 13>", '\000' <repeats 44 times>, portal = 0x374d8b8}
(gdb) p *(PortalHashEnt*)0x3750f70
$35 = {portalname = "<UNNAMED PORTAL 12>", '\000' <repeats 44 times>, portal = 0x374db08}
(gdb) p *(PortalHashEnt*)0x3750f18
$36 = {portalname = "<UNNAMED PORTAL 14>", '\000' <repeats 44 times>, portal = 0x374dc30}
(gdb) p *(PortalHashEnt*)0x3750fc8
$37 = {portalname = "<UNNAMED PORTAL 15>", '\000' <repeats 44 times>, portal = 0x374d9e0}
(gdb) p *(PortalHashEnt*)0x37510d0
$38 = {portalname = '\000' <repeats 63 times>, portal = 0x374d668}(gdb) p *$38.portal
$40 = {name = 0x37510d0 "", prepStmtName = 0x0, portalContext = 0x3906c10, resowner = 0x37d3230, cleanup = 0x845ed8 <PortalCleanup>, createSubid = 1, activeSubid = 1,sourceText = 0x3664978 "DO $$\nDECLARE\n    cur1 CURSOR FOR SELECT id, name FROM test_table;\n    cur2 CURSOR FOR SELECT id, name FROM test_table;\n    cur3 CURSOR FOR SELECT id, name FROM test_table;\n    cur4 CURSOR FOR SELECT "...,safeSourceText = 0x3664978 "DO $$\nDECLARE\n    cur1 CURSOR FOR SELECT id, name FROM test_table;\n    cur2 CURSOR FOR SELECT id, name FROM test_table;\n    cur3 CURSOR FOR SELECT id, name FROM test_table;\n    cur4 CURSOR FOR SELECT "..., commandTag = 0x1803fed "DO", stmts = 0x3849840, cplan = 0x0, portalParams = 0x0, queryEnv = 0x0, strategy = PORTAL_MULTI_QUERY,cursorOptions = 4, run_once = 1 '\001', status = PORTAL_ACTIVE, portalPinned = 0 '\000', autoHeld = 0 '\000', queryDesc = 0x0, tupDesc = 0x0, formats = 0x0, holdStore = 0x0,holdContext = 0x0, holdSnapshot = 0x0, atStart = 1 '\001', atEnd = 1 '\001', portalPos = 0, creation_time = 808216570922698, visible = 0 '\000', failed_in_sub = 0 '\000',up_instrument = 0, cn_penetrate_tupleDesc = 0x0, last_fetched = 0, paused = 0 '\000', cursor = 0 '\000'}

实例二:查看共享内存hash表

hashtable修改为ShmemIndex后执行脚本:

set $hashtable = ShmemIndex
set $i = 0
printf "max_bucket[%d]\n", $hashtable->hctl->max_bucket
while ($i <= $hashtable->hctl->max_bucket)set $seg_idx = $i / $hashtable->hctl->dsizeset $seg_off = $i % $hashtable->hctl->dsizeset $bucket = $hashtable->dir[$seg_idx][$seg_off]set $cnt = 0set $b = $bucketwhile ($b != 0)printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)set $cnt = $cnt + 1set $b = $b->linkendprintf "bucket[%d] has %d keys\n", $i, $cntset $i = $i + 1
end

执行结果

max_bucket[439]
bucket[0] has 0 keys
bucket[1] key_addr[0]: 0x7f07221793c0
bucket[1] key_addr[1]: 0x7f075a7ac870
bucket[1] has 2 keys
bucket[2] key_addr[0]: 0x7f075a3e0d30
bucket[2] has 1 keys
bucket[3] has 0 keys
bucket[4] key_addr[0]: 0x7f075a014e10
bucket[4] key_addr[1]: 0x7f076ac262d0
bucket[4] has 2 keys
bucket[5] has 0 keys
bucket[6] key_addr[0]: 0x7f075a3dffc0
bucket[6] has 1 keys
bucket[7] has 0 keys
bucket[8] has 0 keys
bucket[9] key_addr[0]: 0x7f0759cd81b0
bucket[9] has 1 keys
bucket[10] has 0 keys

找到数据结构ShmemIndexEnt:

void
InitShmemIndex(void)
{HASHCTL		info;int			hash_flags;info.keysize = SHMEM_INDEX_KEYSIZE;info.entrysize = sizeof(ShmemIndexEnt);hash_flags = HASH_ELEM;ShmemIndex = ShmemInitHash("ShmemIndex",SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,&info, hash_flags);
}

强制转换:

...
(gdb) p *(ShmemIndexEnt*)0x7f075a7ac870
$46 = {key = "Extension group 145", '\000' <repeats 28 times>, location = 0x7f075a929900, size = 77848}
(gdb) p *(ShmemIndexEnt*)0x7f075a3e0d30
$47 = {key = "Extension group 74", '\000' <repeats 29 times>, location = 0x7f075a3e0d80, size = 77848}
...
http://www.dtcms.com/a/325188.html

相关文章:

  • Apache IoTDB 全场景部署:基于 Apache IoTDB 的跨「端-边-云」的时序数据库 DB+AI
  • ZeroNews:如何构建安全(无需 V*N!)的工业物联网连接
  • 企业高性能 Web 服务部署实践(基于 RHEL 9)
  • DNS(域名系统)
  • IP分片(IP Fragmentation)
  • NS3中的路由模型-5 OLSR路由协议
  • 疏老师-python训练营-Day42Grad-CAM与Hook函数
  • MySQL 基础操作教程
  • 学习嵌入式第二十五天
  • 机器学习——K-means聚类
  • 个人效能是一个系统
  • 【YOLO11改进 - C3k2融合】C3k2融合EBlock(Encoder Block):低光增强编码器块,利用傅里叶信息增强图像的低光条件
  • 学习嵌入式的第十六天——C语言——位运算
  • tlias智能学习辅助系统--原理篇-SpringBoot原理-自动配置-自定义starter
  • 【走进Docker的世界】深入理解Docker网络:从模式选择到实战配置
  • #Datawhale AI夏令营#第三期全球AI攻防挑战赛(AIGC技术-图像方向)
  • [Shell编程] Shell的正则表达式
  • P3232 [HNOI2013] 游走,solution
  • Python 程序设计讲义(68):Python 的文件操作——使用os模块操作文件
  • 整数规划-分支定界
  • Win10清理C盘步骤
  • 展锐平台(Android15)WLAN热点名称修改不生效问题分析
  • 最大子数组和-一维动态规划
  • C++ 黑马 引用
  • 一款轻量、免费、无广告,强大的演示工具,支持屏幕放大、涂鸦、截图、录屏
  • PostgreSQL因为A/B switch以及group表过多导致WAL full的情况讨论
  • 托福单词词组积累 2025-08-10
  • 大模型 MCP服务案例详细讲解
  • 线程池的核心线程数与最大线程数怎么设置
  • NVIDIA Jetson AGX Orin 全景解析——边缘计算的高性能选择