登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
博客
发1篇日志+1圆
记录
发1条记录+2圆币
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
VIP网盘
VIP申请
网盘
联系我们
道具
勋章
任务
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
安全
›
表访问方法:PostgreSQL 中数据更新的处理方式 ...
表访问方法:PostgreSQL 中数据更新的处理方式
[ 复制链接 ]
蒙飘
2025-6-11 10:41:27
作者:Cary
前言
本文将详细探讨 PostgreSQL 如何处理更新操作。在 PostgreSQL 中,成功的更新可以被视为“插入一条新记录”,同时“标记旧记录为不可见”,这是因为 PostgreSQL 使用了 MVCC 技术。这个过程听起来简单,但实际上有许多因素需要考虑,以确保更新的成功。
涉及的 API
在执行更新操作时,主要涉及两种表访问方法的 API:
tuplefetchrow_version():此函数用于查找给定 TID 的元组的最新版本。我们需要使用给定的 TID 查找特定的元组进行更新。此外,如果适用,可以提供一个快照结构来执行可见性检查。该函数会提取元组并将其转换为 Tuple Table Slot,如果成功提取元组,则返回 true,否则返回 false。
tuple_update():这是处理元组更新请求的主要处理函数。此函数会接收多个参数来执行更新操作:
旧元组的 TID:待更新的旧元组的位置
新元组(以 Tuple Table Slot 表示):PostgreSQL 将其转换为 HeapTuple 进行更新
命令 ID 和快照:用于对待更新的旧元组执行可见性检查
更新流程
在执行更新操作之前,PostgreSQL 会进行一系列的检查和考量。此过程如下图所示:
1. 确定更新的列
主更新流程执行的第一个检查是确定需要更新的列,特别是要确认是否更新了标识键列(例如主键、索引键或分区键)。因为如果标识键列未被更新,可能无需为了执行更新操作而获取独占锁。此外,对于逻辑复制中的副本标识列,还需要进行额外的处理,以确保在 WAL 文件中记录足够的信息,以便逻辑订阅者能够识别出哪一行已被更新。
2. 确定元组是否可更新
这是关于并发控制的关键步骤。PostgreSQL 是一个多进程系统,一个元组可能会同时被多个客户端连接更新。当然,PostgreSQL 不允许一个元组同时被多个客户端更新,因此在继续操作之前,我们需要确保没有其他客户端正在更新同一个元组。这可以通过查看当前旧元组头部的 xmax 值(修改该元组的事务 ID)来检查,并查找 CLOG 或其提示位(hintbit)标志来确定 xmax 值是否已提交。在调用 HeapTupleSatisfiesUpdate() 后,有以下几种情况需要考虑:
元组不可见:这意味着另一个客户端后端已经更新了该元组(将其标记为不可见)并且已提交了事务。如果是这种情况,我们无法进行更新,因为根据快照,已经没有可更新的内容了。因此,系统将在此处报错。
元组正在被更新:这意味着另一个客户端后端已经更新了该元组(将其标记为不可见),但尚未提交或回滚事务。如果是这种情况,我们必须在此处等待,直到其他客户端的事务提交或中止。系统将进入等待循环,同时执行死锁检测,以确保我们正在等待的事务 ID 没有在等待我们完成(交叉等待,即死锁)。
元组可更新:这意味着该元组未被其他客户端后端更新,我们可以继续对其进行更新。
3. 准备新的元组头部
一旦我们确认可以更新旧元组,PostgreSQL 将开始准备新元组,将其从元组表槽(Tuple Table Slot)格式转换为堆元组(HeapTuple)格式。然后填写必要的头部信息,并准备插入操作。
4. TOAST 检查
一旦我们准备好了新的堆元组(HeapTuple)结构,就需要检查其大小是否适合更新期间给定的缓冲页剩余空间。我们需要确认新元组是否可以放入给定缓冲页的剩余空间中。如果可以,则无需执行额外操作,直接进入下一阶段处理。如果给定的缓冲页空间不足以容纳这个新的堆元组,那么我们需要执行 TOAST 操作。TOAST 是 PostgreSQL 中的一种技术,用于将大型数据分解为较小的块并以分布式方式存储。系统将调用 heap_toast_insert_or_update() 来完成 TOAST 操作,并生成一条 WAL 记录,表明在此处执行了 TOAST 操作,且在使用该值时需要进行 DE-TOAST 操作。
5. 从旧元组中提取副本标识
此操作仅仅是从旧元组中提取副本标识(如主键、索引键等),以便将这些附加信息包含在 WAL 段中,从而准确告知逻辑复制订阅者具体是哪一行被更新了。如果没有这个副本标识,逻辑复制订阅者只会收到某一行被更新为新值的通知,但无法确定具体是哪一行被更新了。
6. 页面设置为可修剪
此操作主要是对缓冲页的头部进行修改,以表明该页面将包含一个死元组,因为我们正在对其执行更新操作。这一标记主要是供 VACUUM 进程查看,用于判断一个页面是否完全可见(即页面内的所有元组都可见)或可修剪(即页面内存在需要清理的死元组)。
7. HOT(Heap Only Tuple) 更新
这是 PostgreSQL 采用的一种优化手段,用于在条件合适的情况下“节省索引元组”。之所以称为仅元组堆更新,是因为它字面上意味着只有堆元组,而没有直接关联的索引元组,即使在该表上创建了索引。
触发 HOT 更新以“节省索引元组”的条件如下:
没有索引列被更新。
新的元组可以插入到与旧元组相同的页面中。
如果满足上述条件,PostgreSQL 会将新的元组插入页面,并在页面内将旧元组与新元组“链接”在一起。与旧元组关联的索引元组仍然指向旧元组,但由于我们已将旧元组和新元组链接在同一页面内,我们仍然可以通过先定位到旧元组,然后沿着链接找到新元组。这样,PostgreSQL 就不需要创建一个新的索引元组来直接指向新元组,从而节省了一些潜在的元组空间。
8. 关系放入堆元组
在完成上述所有检查后,我们现在可以将新的元组放入指定的页面,并正确设置元组头部信息和 HOT 标志。
9. 将旧元组标记为不可见
新元组插入后,我们现在可以通过将旧元组的 xmax 值设置为当前事务 ID,设置其提示位(hintbit)和适当的 HOT 标志,将旧元组标记为不可见。
10. 标记缓冲区为脏页
这是一个缓冲区管理器例程,用于通知管理器当前页面内容已被修改。因此,在需要置换缓冲区页面前,必须先将该页面的修改内容刷新到磁盘。周期性的检查点进程会主动尝试优先将此脏页刷新到磁盘。
11. 缓存使堆元组无效
该机制用于通知所有活跃的后端进程,如果它们在其本地缓存中存储了刚刚被标记为不可见(已删除)的堆元组,则使这些元组失效。
12. 更新索引标志
在更新结束时,我们需要设置一个标志,告知执行器是否需要为新元组创建索引元组。在 HOT 的情况下,或者当表上没有创建索引时,我们通常会将其设置为 false。
总结
与插入和顺序扫描不同,更新操作在完成时需要更多的考虑。它不仅需要考虑并发控制,还必须考虑一系列优化措施、超大元组以及用于逻辑复制的副本标识。这些因素使得更新操作通常成为执行起来更为昂贵的操作。
关于 IvorySQL
lvorySQL 是由瀚高股份主导研发的一款开源的兼容 Oracle 的 PostgreSQL。IvorySQL 与 PostgreSQL 国际社区紧密合作,保持与最新 PG 版本内核同步,为用户提供便捷的升级体验。基于双 Parser 架构设计,100% 与原生 PostgreSQL 兼容,支持丰富的 PostgreSQL 周边工具和扩展,并根据用户需求提供定制化工具。同时,IvorySQL 4.0 提供更全面灵活的 Oracle 兼容功能,具备高度的 SQL 和 PL/SQL 兼容性能够为企业构建更加高效、稳定和灵活的数据库解决方案。
官网:https://www.ivorysql.org
GitHub(欢迎点击 star 收藏哦):https://github.com/IvorySQL/IvorySQL
社群:VX 搜索“ivorysql_official” 添加小助理进群
本文由博客一文多发平台 OpenWrite 发布!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
业界
签约作者
程序园优秀签约作者
发帖
蒙飘
2025-6-11 10:41:27
关注
0
粉丝关注
14
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
敖可
9984
黎瑞芝
9990
杭环
9988
4
猷咎
9988
5
凶契帽
9988
6
接快背
9988
7
氛疵
9988
8
恐肩
9986
9
虽裘侪
9986
10
里豳朝
9986
查看更多