PostgreSQL与SQL Server:B树索引差异及去重的优势

PostgreSQL与SQL Server:B树索引差异及去重的优势

在优化查询性能方面,索引是数据库工程师可使用的最强大工具之一。PostgreSQL和Microsoft SQL Server(或Azure SQL)都将B树索引用作其默认索引结构,但每个系统实现、维护和使用这些索引的方式存在细微却重要的差异。

在这篇博文中,我们探讨了PostgreSQL和SQL Server的几个关键差异点:它们的B树索引在底层的实现方式,以及它们在磁盘上存储和访问数据的方式。我们还将对每个数据库系统中值的去重对索引大小的影响进行基准测试。

我们在文末还附上了一份全面的参考指南(参见Postgres与SQL Server索引对比表)。无论你是在优化查询,还是在规划迁移,这些差异都会对性能和索引策略产生显著影响。

PostgreSQL与SQL Server中B树索引的工作原理

从宏观层面来看,这两种数据库都使用B树索引来加快等值查询和范围查询的速度。B树保持有序状态,并且经过平衡处理,以确保稳定的读取性能。不过,尽管这两种数据库中B树的概念相似,但其实现方式却会对性能产生重要影响。

SQL Server 使用聚集索引通过索引列对表的数据进行物理排序。定义聚集索引后,表中的行将按照与索引本身相同的顺序存储。非聚集索引单独存储,并使用行定位符(RID 或聚集键)指向行。这种物理排序有利于范围扫描或分页查询,但这也意味着每个表只能有一个聚集索引。更重要的是,SQL Server 会完整存储每个索引项,即使同一页上的多个项具有相同的值。由于没有去重功能,因此包含许多重复值的索引可能会变得很大,并消耗过多的 I/O。

PostgreSQL没有SQL Server意义上的聚集索引。所有PostgreSQL表都存储为无序堆,而索引纯粹是指向堆中元组的逻辑结构。这种设计为PostgreSQL提供了一定的灵活性:它使索引维护更简单,并避免了物理重排的复杂性。

然而,这也意味着你不能依赖索引来定义表的物理布局。如果查询性能取决于按特定顺序读取数据,Postgres确实允许你运行CLUSTER命令,但这需要完整的表锁。在生产环境中,你可以使用pg_repack等工具来达到类似的效果。

因此,虽然这两种数据库都默认使用B树索引,但SQL Server的索引与物理存储之间的紧密耦合带来了一系列不同的预期和限制。PostgreSQL的索引模型存在一些性能缺陷(因为它没有聚簇索引的实现),但去重等独特功能使其在其他情况下表现更佳。

PostgreSQL的B树去重

PostgreSQL 13版本引入了去重功能,以解决传统B-Tree索引中一个常见的低效问题。当许多行共享相同的索引值(比如状态码、布尔标志或时间戳)时,标准的B-Tree会单独存储每个值及其对应的元组指针。这会导致索引页膨胀,并增加维护成本,对于写入密集型工作负载来说尤其如此。

PostgreSQL默认会对单个索引页内的重复值进行去重处理。它不会多次存储相同的键值,而是只存储一次,并维护一个紧凑的结构来跟踪所有匹配的堆指针。这能显著减小索引大小,并提高缓存性能,因为更多的索引条目可以放入内存中。

SQL Server不支持去重。即使值完全相同,每个索引项也会独立存储。在分布倾斜的数据集中,PostgreSQL的方法能生成更紧凑、更高效的索引,页面更少,磁盘I/O也更少。

在PostgreSQL与SQL Server上对B树索引进行基准测试

为了了解PostgreSQL的索引去重功能对实际性能和存储的影响,我们进行了一项基准测试,在不同的数据重复程度下比较PostgreSQL和SQL Server的B-Tree索引大小。每个测试都创建了一个包含1000万行的表,这些行的值重复程度各不相同,从完全唯一的值到重复1000倍的值不等。

以下是我们在两个数据库中构建测试的方式,以便您可以自行复现该测试。

PostgreSQL测试设置

CREATE TABLE factor_1(col int);  
CREATE TABLE factor_10(col int);  
CREATE TABLE factor_100(col int);  
CREATE TABLE factor_1000(col int);INSERT INTO factor_1 SELECT * FROM GENERATE_SERIES(1, 10000000);  
INSERT INTO factor_10 SELECT val / 10 FROM GENERATE_SERIES(1, 10000000) x(val);  
INSERT INTO factor_100 SELECT val / 100 FROM GENERATE_SERIES(1, 10000000) x(val);  
INSERT INTO factor_1000 SELECT val / 1000 FROM GENERATE_SERIES(1, 10000000) x(val);CREATE INDEX factor_1_idx ON factor_1(col);  
CREATE INDEX factor_10_idx ON factor_10(col);  
CREATE INDEX factor_100_idx ON factor_100(col);  
CREATE INDEX factor_1000_idx ON factor_1000(col);CREATE INDEX factor_1_idx_no_dup_fill100 ON factor_1(col) WITH (deduplicate_items = off, fillfactor = 100);  
CREATE INDEX factor_10_idx_no_dup_fill100 ON factor_10(col) WITH (deduplicate_items = off, fillfactor = 100);  
CREATE INDEX factor_100_idx_no_dup_fill100 ON factor_100(col) WITH (deduplicate_items = off, fillfactor = 100);  
CREATE INDEX factor_1000_idx_no_dup_fill100 ON factor_1000(col) WITH (deduplicate_items = off, fillfactor = 100);  

SQL Server 测试设置

CREATE TABLE factor_1(col int);  
CREATE TABLE factor_10(col int);  
CREATE TABLE factor_100(col int);  
CREATE TABLE factor_1000(col int);INSERT INTO factor_1 SELECT * FROM GENERATE_SERIES(1, 10000000);  
INSERT INTO factor_10 SELECT value / 10 FROM GENERATE_SERIES(1, 10000000);  
INSERT INTO factor_100 SELECT value / 100 FROM GENERATE_SERIES(1, 10000000);  
INSERT INTO factor_1000 SELECT value / 1000 FROM GENERATE_SERIES(1, 10000000);CREATE INDEX factor_1_idx ON factor_1(col);  
CREATE INDEX factor_10_idx ON factor_10(col);  
CREATE INDEX factor_100_idx ON factor_100(col);  
CREATE INDEX factor_1000_idx ON factor_1000(col); 

基准测试结果:PostgreSQL的去重功能减小了索引大小

当我们对比PostgreSQL和SQL Server的索引大小时,发现随着数据重复率的增加,两者的差异显著扩大。当值重复1000次时,启用去重功能的PostgreSQL索引比关闭去重功能的相同索引小3倍。而SQL Server不支持去重功能,会完整存储每个重复值,相比之下,PostgreSQL始终能生成更小、更高效的索引。

这种差异至关重要。在生产系统中,状态标志、时间戳和分类字段等基数较高的列很常见。当这些值在数百万行中重复出现时,大型索引会迅速成为性能瓶颈,导致扫描速度变慢、I/O 增加以及内存使用量膨胀。

PostgreSQL的去重功能显著减小了索引大小,这使得索引更易于保存在内存中,并减轻了磁盘压力。对于从SQL Server迁移到PostgreSQL的团队,或者只是通过频繁使用的索引来扩展工作负载的团队而言,这种优化并非只是理论层面的。它对资源使用、查询性能和整体运营效率都有着直接影响。

对比表:PostgreSQL 与 SQL Server 的索引

PostgreSQL和SQL Server在B树及其他索引类型的索引实现上存在显著差异。我们整理了一份全面的索引对比表,供您在从SQL Server迁移到PostgreSQL时参考。

(某些索引类型存在于SQL Server中,但不存在于PostgreSQL中,反之亦然。我们已按如下方式标注支持情况:🟢 支持的索引类型 🔴 不支持的索引类型。)

索引类型使用案例示例PostgreSQLSQL Server
B-Tree最适合通用索引、等值查询和范围查询(例如,按年龄或日期筛选用户)。🟢 默认索引类型,支持等值查询和范围查询、排序以及带前缀的模式匹配。🟢 在SQL Server上,聚集索引和非聚集索引的默认结构是B-Tree。
Clustered按索引键自动对表行进行排序;最适合频繁排序的查询。🔴 PostgreSQL 没有聚簇索引;相反,您可以使用 CLUSTER 命令根据非聚簇索引对表进行排序;但是,当插入新数据时,这种顺序不会被保留。🟢 相当于PostgreSQL的B树;根据键对数据进行排序和存储。
Nonclustered适用于可加快搜索速度且不影响物理存储顺序的索引。🟢 在PostgreSQL中,所有索引都是非聚集索引。🟢 可以在堆或聚集索引上创建;数据存储与表分开。
Hash针对精确匹配查找进行了优化,例如按用户ID或电子邮件地址搜索。🟢 在PostgreSQL中,哈希索引只能为单个列建立索引。虽然你可以创建多个索引来支持查询,但通常多列B-Tree索引更为有效。🟢 用于内存优化表;需要固定的桶数量。
Filtered / Partial对数据子集(例如仅活跃用户)进行索引时效率很高。🟢 PostgreSQL 可以使用部分索引仅对行的一个子集进行索引。🟢 筛选索引是一种非聚集索引,仅对表中的一部分行进行索引。
BRIN最适合数据自然有序的超大型表格,例如时间序列数据。🟢 存储块范围的摘要;最适合大型、顺序存储的数据。🔴 N/A 🔴 不适用
Full-text用于自然语言搜索,例如搜索文章或产品评论中的文本。🟢 PostgreSQL支持通过在tsvector列上使用GIN索引来进行全文搜索。🟢 SQL Server 对基于文本的查询使用倒排索引,类似于 PostgreSQL 的 GIN。
GIN非常适合为JSONB、数组和全文搜索建立索引(例如,搜索产品描述)。🟢 倒排索引;最适用于JSON、全文搜索和数组。🔴 通过全文索引实现部分功能。
Vector在高维数据中高效执行相似性搜索或最近邻搜索,这在人工智能和机器学习应用中最为常见。🟢 PostgreSQL本身不包含向量支持,但开源扩展pgvector支持向量存储和索引。🔴 SQL Server本身不支持向量索引或搜索。微软建议改用其Azure AI搜索。
XML针对查询和存储XML文档进行了优化。🔴 PostgreSQL 不直接支持在 XML 类型上创建索引;但是,可以在 XML 数据的子集上使用表达式索引。对于非结构化文档,JSONB 是推荐的数据类型。🟢 SQL Server 对 XML 数据类型有专用索引。
Spatial用于地理查询,例如查找半径范围内的位置。🟢 在PostgreSQL中,空间索引查询由开源的PostGIS扩展提供。🟢 SQL Server 具有内置的空间数据类型。
SP-GiST用于层级数据结构,如基于树的搜索(例如路由网络)。🟢 支持非平衡树结构,如四叉树和k-d树,适用于分层数据。🔴 N/A 🔴 不适用
GiST适用于几何和全文搜索查询,例如查找附近的位置。🟢 专用索引的基础架构;用于几何和全文搜索。🔴 N/A 🔴 不适用
Columnstore最适合OLAP工作负载和分析查询(例如,数据仓库)。🔴 虽然PostgreSQL有不同的扩展提供列式存储,如Citus和Timescale,但这是一个相对较新的实现,可能会受使用场景的限制。🟢 自SQL Server 2012起,SQL Server就内置了作为索引类型实现的列存储。

为你的工作选择合适的索引

理解PostgreSQL和SQL Server索引之间的差异,在优化查询性能、规划迁移或设计高性能数据库时至关重要。选择合适的索引策略需要深入了解查询执行模式和性能权衡。许多团队会手动尝试不同的索引策略,这可能导致过度索引、冗余索引或错失优化机会。

与反复试验不同,pganalyze 索引顾问通过针对真实查询执行数据应用约束编程模型,自动检测缺失的索引、冗余的索引以及多列索引的最佳列顺序。这消除了猜测工作,确保PostgreSQL数据库的索引设置能实现最佳性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95647.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【微实验】使用MATLAB制作一张赛博古琴?

当一个理工音乐人没钱去买古琴,我直接用代码画一个古琴!目录 零、总脚本: 一、核心功能:交互模块拆解 二、核心价值 一、初始化脚本:参数配置与启动界面 ①废话不说,直接上代码 ②代码模块拆解与详细解…

毕业项目推荐:67-基于yolov8/yolov5/yolo11的大棚黄瓜检测识别系统(Python+卷积神经网络)

文章目录 项目介绍大全(可点击查看,不定时更新中)概要一、整体资源介绍技术要点功能展示:功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出(xls格式…

无人机小尺寸RFSOC ZU47DR板卡

整板尺寸:120*120mmFPGA: XCZU47DR-2FFVE1156I;DDR:PS侧8GB 2400Mhz*64bit / PL侧 4GB 2400Mhz*32bit;2路(QSP0QSPI1)/单片512Mb、共计1Gb;千兆以太网:1路(PS侧);主要接口资源如下&a…

LangGraph(一):入门从0到1(零基础)

文章目录LangGraph入门从0到10️⃣ 安装 & 确认环境1️⃣ 把 LangGraph 想象成「自动化的做菜流水线」2️⃣ 最小可运行例子:一句话复读机3️⃣ 加一个小节点:把用户输入变大写4️⃣ 条件边:如果用户说 quit 就结束,否则复读5…

学习数据结构(16)快速排序

快速排序的基本思想:快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该基准值将待排序集合分割成两子序列,左子序列中所有元素均小于基准值&am…

uni-app iOS 上架常见问题与解决方案,实战经验全解析

uni-app 让开发者能够“一套代码,多端运行”,极大降低了开发成本。 但当应用进入 iOS 上架阶段 时,不少团队发现流程并没有想象中那么顺利:证书问题、打包失败、上传出错、审核被拒……这些都可能让项目卡壳。 本文结合实际案例&a…

洗衣机的智能升级集成方案WT2606B屏幕驱动+AI语音控制

2025,洗衣机市场正从功能满足转向体验升级,企业正面临哪些转型难点?一文为您解读洗衣机行业智能化升级之路。传统洗衣机就像是一个"沉默的工人",只能通过简单的LED指示灯告诉你它在工作,却无法让你真正了解它在干嘛。用…

机器学习进阶,梯度提升机(GBM)与XGBoost

梯度提升机(Gradient Boosting Machine, GBM),特别是其现代高效实现——XGBoost。这是继随机森林后自然进阶的方向,也是当前结构化数据竞赛和工业界应用中最强大、最受欢迎的算法之一。为什么推荐XGBoost? 与随机森林互…

【ARMv7】开篇:掌握ARMv7架构Soc开发技能

本专栏,开始与大家共同总结使用ARMv7系列CPU的Soc开发技能。大概汇总了一下,后面再逐步完善下面的思维导图。简单说说:与通用的ARMv7-A/R相比,以STM32F为代表的ARMv7-M架构有以下关键区别和重点:无MMU,有MP…

【学术会议论文投稿】JavaScript在数据可视化领域的探索与实践

【ACM出版 | EI快检索 | 高录用】2024年智能医疗与可穿戴智能设备国际学术会议(SHWID 2024)_艾思科蓝_学术一站式服务平台 更多学术会议请看 学术会议-学术交流征稿-学术会议在线-艾思科蓝 目录 引言 JavaScript可视化库概览 D3.js基础入门 1. 引入…

CSS基础学习步骤

好的,这是一份为零基础初学者量身定制的 **CSS 学习基础详细步骤**。我们将从最根本的概念开始,通过一步一步的实践,带你稳稳地入门。 第一步:建立核心认知 - CSS 是做什么的? 1. 理解角色: HTML&…

MTK Linux DRM分析(三十七)- MTK phy-mtk-hdmi.c 和 phy-mtk-hdmi-mt8173.c

一、简介 HDMI PHY驱动 HDMI 的物理层接口主要就是 HDMI Type-A 接口(19 pin),除此之外还有 Type-B、Type-C(Mini HDMI)、Type-D(Micro HDMI)、Type-E(车载专用)。 1. HDMI Type-A(常见 19-pin 标准接口) HDMI Type-A Connector Pinout ========================…

【人工智能学习之MMdeploy部署踩坑总结】

【人工智能学习之MMdeploy部署踩坑总结】报错1:TRTNet: device must be a GPU!报错2:Failed to create Net backend: tensorrt报错3:Failed to load library libonnxruntime_providers_shared.so1. 确认库文件是否存在2. 重新安装 ONNX Runti…

力扣516 代码随想录Day16 第一题

找二叉树左下角的值class Solution { public:int maxd0;int result;void traversal(TreeNode* root,int depth){if(root->leftNULL&&root->rightNULL){if(depth>maxd){maxddepth;resultroot->val;}}if(root->left){depth;traversal(root->left,depth…

网格图--Day07--网格图DFS--LCP 63. 弹珠游戏,305. 岛屿数量 II,2061. 扫地机器人清扫过的空间个数,489. 扫地机器人,2852. 所有单元格的远离程度之和

网格图–Day07–网格图DFS–LCP 63. 弹珠游戏,305. 岛屿数量 II,2061. 扫地机器人清扫过的空间个数,489. 扫地机器人,2852. 所有单元格的远离程度之和 今天要训练的题目类型是:【网格图DFS】,题单来自灵茶山…

多功能修改电脑机器码序列号工具 绿色版

多功能修改电脑机器码序列号工具 绿色版电脑机器码序列号修改软件是一款非常使用的数据化虚拟修改工具。机器码修改软件可以虚拟的定制您电脑上的硬件信息,软件不会对您的电脑造成伤害。软件不需要您有专业的知识,就可以模拟一份硬件信息。机器码修改软…

React Hooks深度解析:useState、useEffect及自定义Hook最佳实践

React Hooks自16.8版本引入以来,彻底改变了我们编写React组件的方式。它们让函数组件拥有了状态管理和生命周期方法的能力,使代码更加简洁、可复用且易于测试。本文将深入探讨三个最重要的Hooks:useState、useEffect,以及如何创建…

期权平仓后权利金去哪了?

本文主要介绍期权平仓后权利金去哪了?期权平仓后权利金的去向需结合交易角色(买方/卖方)、平仓方式及市场价格变动综合分析,具体可拆解为以下逻辑链条。期权平仓后权利金去哪了?1. 买方平仓:权利金的“差价…

2025国赛C题题目及最新思路公布!

C 题 NIPT 的时点选择与胎儿的异常判 问题 1 试分析胎儿 Y 染色体浓度与孕妇的孕周数和 BMI 等指标的相关特性,给出相应的关系模 型,并检验其显著性。 思路1:针对附件中孕妇的 NIPT 数据,首先对数据进行预处理,并对多…

NLP技术爬取

“NLP技术爬取”这个词组并不指代一种单独的爬虫技术,而是指将自然语言处理(NLP)技术应用于网络爬虫的各个环节,以解决传统爬虫难以处理的问题,并从中挖掘出更深层次的价值。简单来说,它不是指“用NLP去爬”…