Postgres vs MySQL
主要区别及示例
简而言之,Postgres 和 MySQL 之间的主要区别实际上归结为主索引和辅助索引的实现方式以及数据的存储和更新方式。
让我们进一步探讨这个问题。
但首先... 基础知识
索引是一种数据结构(主要是 B + 树),允许通过多层节点进行键的搜索,数据库将其实现为页面。树的遍历允许消除不包含结果的页面,并缩小包含结果的页面的范围。这一过程一直持续到找到包含键的叶子页面。
叶子节点或页面包含有序键及其值的列表。当找到一个键时,可以获取其值,并且页面会被缓存在数据库的共享缓冲区中,希望未来的查询可能会请求相同页面中的键。
这最后一句是理解数据库工程、管理、编程和建模的基本原则。了解查询是否命中页面中相邻的键将最大程度地减少 I/O 并提高性能。
B + 树索引中的键是创建索引所在表的列(或多个列),而值在 Postgres 和 MySQL 中的实现方式有所不同。让我们探讨一下 Postgres 和 MySQL 中值的含义。
MySQL
在主索引中,值是带有所有属性 * 的完整行对象。这就是为什么主索引通常被称为聚簇索引或我更喜欢的术语 "索引组织表"。这意味着主索引就是表本身。
* 注意,对于行存储,这是正确的。数据库可能使用不同的存储模型,如列存储、图形或文档存储,从根本上讲,这些也可以作为潜在的值。
如果在主索引中查找一个键,你会找到包含该键的页面和它的值,该值是该键对应的完整行,不需要额外的 I/O 操作来获取其他列。
在二级索引中,键是你索引的列(或多个列),而值是指向实际存储完整行位置的指针。二级索引叶子页面的值通常是主键。
这就是 MySQL 的情况。在 MySQL 中,所有的表都必须有一个主索引,而所有额外的二级索引都指向主键。如果你在 MySQL 表中不创建主键,系统会为你自动创建一个。
Postgres
在 Postgres 中,严格来说没有主索引,所有的索引都是二级索引,它们都指向加载在堆中的数据页中由系统管理的元组标识符(tuple ids)。堆中的表数据是无序的,不像主索引叶子页是有序的。因此,如果你插入了 1-100 行,并且它们都在同一页中,然后后来更新了 1-20 行,这 20 行可能会跳转到另一页,并且变得无序。而在聚簇主索引中,插入操作必须按照键的顺序插入到相应的页中。这就是为什么 Postgres 表通常被称为 " 堆有序表 "而不是" 索引组织表 "。
需要注意的是,在 Postgres 中,更新和删除实际上是插入操作。每次更新或删除都会创建一个新的元组标识符(tuple id),而旧的元组标识符则保留为了多版本并发控制(MVCC)的原因。我稍后会在本文中探讨这个问题。
事实上,仅仅使用元组标识符是不够的。实际上,我们需要同时知道元组标识符和页面编号,这被称为 c_tid。想一想,仅仅知道元组标识符是不够的,我们需要知道元组所在的页。这是在 MySQL 中不需要做的事情,因为我们实际上是通过查找来找到主键所在的页。而在 Postgres 中,我们只需要进行一次 I/O 操作就可以获取到完整的行数据。
查询费用
请参考以下示例中的表格。
#TABLE T; #PRIMARY INDEX ON PK AND SECONDARY INDEX ON C2, NO INDEX ON C1 # C1 and C2 are text # PK is integer | PK | C1 | C2 | |----|----|----| | 1 | x1 | x2 | | 2 | y1 | y2 | | 3 | z1 | z1 |
让我们比较一下 MySQL 和 Postgres 中发生的情况。
SELECT * FROM T WHERE C2 = 'x2';
在 MySQL 中,执行这个查询将会产生两次 B + 树查找。首先,我们需要使用二级索引查找 x2 的主键,找到主键值为 1,然后再使用主索引进行另一次查找,找到完整的行数据,因此返回了所有属性(因此有 * 号)。
在 Postgres 中,查找任何二级索引只需要进行一次索引查找,然后进行一次常量的单个 I/O 操作,以获取包含完整行数据的页。一次 B + 树查找要比两次查找好。
为了使这个示例更加有趣,假设 C2 不是唯一的,并且有多个 x2 的条目,那么我们将会找到匹配 x2 的大量 tids(或在 MySQL 中的 PK)。问题是这些行标识符将位于不同的页面,导致随机读取。在 MySQL 中,这将导致索引查找(根据这些键的数量,查询优化器可能会选择索引扫描还是基于 seek 的操作),但是两个数据库都会导致许多随机 I/O。
Postgres 尝试通过使用位图索引扫描来最小化随机读取,将结果分组为页面而不是元组,并以尽可能少的 I/O 操作从堆中获取页面。然后应用额外的过滤来呈现候选行。
让我们看一个不同的查询。
SELECT * FROM T WHERE PK BETWEEN 1 AND 3;
对于对主键索引的范围查询,我认为 MySQL 在这方面是更好的选择,通过一次查找,我们可以找到第一个键,并在 B + 树链接的叶子页上遍历以找到附近的键,当我们遍历时,我们找到完整的行数据。
Postgres 在这方面可能会遇到一些困难,确实,二级索引查找将在叶子页上进行相同的 B + 树遍历,并找到键,但它只会收集 tids 和页码。它的工作并没有结束。Postgres 仍然需要在堆中进行随机读取,以获取完整的行数据,而这些行数据可能分布在堆中的各个位置,而不是紧凑地排列在一起,特别是如果这些行数据被更新过。
好的,我们来进行一次更新操作。
UPDATE T SET C1 = ‘XX1’ WHERE PK = 1;
在 MySQL 中,更新一个未建立索引的列只会导致更新包含该行的叶子页,并将其更新为新值。不需要更新其他任何二级索引,因为它们都指向的是未发生变化的主键。
在 Postgres 中,更新一个未建立索引的列将生成一个新的元组,并可能需要更新所有的二级索引以使用新的元组 ID,因为它们只知道旧的元组 ID。这会导致许多写入 I/O 操作。Uber 在 2016 年对此不太满意,这也是他们从 Postgres 切换到 MySQL 的主要原因之一。
我在这里说 “可能” 是因为在 Postgres 中有一种优化方法称为 HOT(仅堆元组),不要与(堆组织表)混淆,它会在二级索引中保留旧的元组 ID,并在堆页头上放置一个指向新元组的链接。
数据类型的重要性
在 MySQL 中,选择主键数据类型非常重要,因为该键将出现在所有的二级索引中。例如,如果使用 UUID 作为主键,会导致所有二级索引的大小膨胀,增加存储和读取 I/O 操作的开销。
在 Postgres 中,元组 ID 固定为 4 个字节,因此二级索引中不会包含 UUID 值,而只包含指向堆的元组 ID。
Undo 日志
所有现代数据库都支持多版本并发控制(MVCC)。在简单的读已提交隔离级别中,如果事务 tx1 更新了一行但尚未提交,而同时另一个并发事务 tx2 想要读取该行,它必须读取旧的行而不是更新后的行。大多数数据库(包括 MySQL)使用 undo 日志来实现此功能。
当事务对一行进行更改时,更改会被写入共享缓冲池中的页面,因此包含该行的页面始终具有最新的数据。然后,事务会在 undo 日志中记录如何撤消对行的最新更改的信息(足够构建旧状态的信息),这样基于其隔离级别仍需要旧状态的并发事务必须解析 undo 日志并构建旧行。
你可能会想知道将未提交的更改写入页面是否是一个好主意。如果后台进程在事务提交之前将页面刷新到磁盘,然后数据库崩溃会发生什么?这就是 undo 日志至关重要的地方。在崩溃后,会使用 undo 日志在数据库启动时撤消未提交的更改。
不可否认,对于长时间运行的事务,undo 日志会对其他正在运行的事务产生影响。需要更多的 I/O 操作来构建旧状态,并且 undo 日志可能会满,导致事务失败的可能性。
在某种情况下,我曾经看到一个数据库系统在运行了一个持续 3 小时的未提交长事务后,需要一个多小时才能从崩溃中恢复。是的,要尽量避免长时间的事务。
Postgres 在这方面处理方式完全不同,每次更新、插入和删除都会得到一份具有新的元组 ID 的新行副本,并附带有关创建该元组的事务 ID 和删除该元组的事务 ID 的提示。因此,Postgres 可以安全地将更改写入数据页面,并且并发事务可以根据其事务 ID 读取旧的或新的元组。聪明的设计。
当然,没有解决方案是没有问题的。我们实际上已经谈论了在二级索引上创建新元组 ID 的代价。此外,如果所有正在运行的事务 ID 都大于删除元组的事务 ID,则 Postgres 需要清除不再需要的旧元组。
进程与线程
MySQL 使用线程,Postgres 使用进程,在这两种选择中都有各自的优缺点。
在数据库系统中,我更喜欢线程而不是进程。因为线程更轻量级,并共享其父进程的虚拟内存地址。与较小的线程控制块(TCB)相比,进程带来了专用虚拟内存和更大的控制块(PCB)的开销。
如果我们最终要共享内存并处理互斥锁和信号量,为什么不使用线程呢?这只是我的个人观点。
总结
你可以选择适合你的数据库系统。真正重要的是将你的使用情况和查询进行分解,了解每个数据库的功能,看看哪些适合你,哪些不适合你。
这里没有对错之分。
相关文章:

Postgres vs MySQL
主要区别及示例 简而言之,Postgres 和 MySQL 之间的主要区别实际上归结为主索引和辅助索引的实现方式以及数据的存储和更新方式。 让我们进一步探讨这个问题。 但首先... 基础知识 索引是一种数据结构(主要是 B 树),允许通过…...

02.IP地址以及静态路由配置
文章目录 IP地址IP地址分类IPV4地址(32位)IPV4地址的分类特殊IP地址 VLSM --- 可变长子网掩码(子网划分)CLDR --- 无类域间路由(汇总)配置静态路由的基础配置静态路由的拓展配置 IP地址 IP地址分类 IPV4(32位二进制构成) — 点分十进制IPV6(128位二进制构成) — 冒分十六进制…...

GD32(STM32)因为中断问题,导致不能进行程序 正常运行
项目中,之前定时器中断就用了个TIM2,但后来程序优化需要再加一个计数定时器TIM6, TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 开启定时器时钟,即内部时钟CK_INT72M RCC_APB1PeriphClockCmd(RCC_APB1Perip…...

华为OD机试真题B卷 Java 实现【统计字符】,附详细解题思路
一、题目描述 输入一行字符,分别统计出包含英文字母、空格、数字和其它字符的个数。 数据范围:输入的字符串长度满足 1 \le n \le 1000 \1≤n≤1000 。 二、输入描述 输入一行字符串,可以有空格。 三、输出描述 统计其中英文字符&#…...

深入理解设计原则之开闭原则(OCP)
系列文章目录 C高性能优化编程系列 深入理解设计原则系列 深入理解设计模式系列 高级C并发线程编程 OCP:开闭原则 系列文章目录1、开闭原则的定义和解读2、如何理解“对扩展开放,对修改关闭”3、实现开闭原则的方法4、如何在团队协作中保证开闭原则的实…...

【学习随笔】
2022/11/13 HTML :讲完了 css:讲完了 作业:编写登陆界面、整理一下sql优化,对于mybatis不熟练的继续练习 关于MySQL优化的问题? 思路总结:主要考虑数据库优化与SQL语句优化。 1,数据库优化,包括存储引擎的优化&…...

【多路IO复用】select
select: 1.select:当被监听的 fd(文件描述符)就绪后会返回,但是我们无法知道具体是哪些 fd 就绪了,只能遍历所有的 fd。通常来说某一时刻,就绪的 fd 并不会很多,但是使用 select 必须要遍历所有…...

cuda编程学习——基础知识介绍!干货向(三)
本文主要内容为介绍CUDA编程前的一些基础知识 参考资料: 高升博客 《CUDA C编程权威指南》 以及 CUDA官方文档 文章、讲解视频同步更新公众《AI知识物语》,B站:出门吃三碗饭 1:并行计算 并行程序可以分为 指令并行࿱…...

30 VueComponent 事件的绑定
前言 这是最近的碰到的那个 和响应式相关的问题 特定的操作之后响应式对象不“响应“了 引起的一系列的文章 主要记录的是 vue 的相关实现机制 呵呵 理解本文需要 vue 的使用基础, js 的使用基础 测试用例 用例如下, 我们这里核心关注 事件的处理流程 问题的调试 整个…...

作用域及作用域链
作用域 隔离变量的集合 作用域最大的用处就是隔离变量,不同作用域内的同名变量不会有命名冲突。 作用域类型 全局作用域,函数作用域和块级作用域。 1)全局作用域,在整个代码文件中都可以访问的作用域。 2)函数作用域…...

深入解析Linux C/C++ 编程中的内存泄漏问题
深入解析Linux C/C 编程中的内存泄漏问题 I. 前言 (Introduction)1.1 文章目的与内容概述 (Purpose and Overview of the Content)1.2 重要性和实用性的说明 (Significance and Practicality Explanation)1.3 数据结构与内存泄漏的基本概念 (Basic Concepts of Data Structure …...

【爬虫第三章】 Python基础
预计更新一、 爬虫技术概述 1.1 什么是爬虫技术 1.2 爬虫技术的应用领域 1.3 爬虫技术的工作原理 二、 网络协议和HTTP协议 2.1 网络协议概述 2.2 HTTP协议介绍 2.3 HTTP请求和响应 三、 Python基础 3.1 Python语言概述 3.2 Python的基本数据类型 3.3 Python的流程控制语句 3…...

电力系统的虚假数据注入攻击和MTD系统研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【阿里云】阿里云OSS对象存储— 开通OSS服务、搭建OSS环境、快速入门
目录 一、开通OSS服务 二、搭建OSS环境 1、创建Bucket存储空间 2. 创建文件夹上传图片 3. RAM 访问控制 三、快速入门 1.下载SDK 2.创建存储空间[可选] 3.上传图片 一、开通OSS服务 二、搭建OSS环境 1、创建Bucket存储空间 选择 Bucket列表,进行Bucket创建…...

代理对象Proxy是什么
Proxy是ES6(ECMAScript 2015)引入的一个特性,它是一种用于创建代理对象的构造函数。代理对象可以用来拦截并自定义对目标对象的操作。 通过使用Proxy,您可以在目标对象上设置各种拦截器(称为"陷阱"…...

会话跟踪cookie和session
什么是会话跟踪技术 会话:用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可能包含多次请求和响应。 会话跟踪:一种维护浏览器状态的方法,服务器需…...

ACS Cent. Sci 2018 | 数据驱动的分子连续表征的自动化学设计
原文标题:Automatic Chemical Design Using a Data-Driven Continuous Representation of Molecules 代码:https://github.com/aspuru-guzik-group/chemical_vae 原文链接:https://pubs.acs.org/doi/10.1021/acscentsci.7b00572 Automatic…...

安卓Termux搭建web服务器【公网远程手机Android服务器】
文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 概述 Termux是一个Android终端仿真应用程序,用于在 Android 手机上搭建一个完整的Linux 环境,能够实现Linux下的许多基本操作,不需要root权限Termux就…...

【大数据之Hive】二、Hive安装
Hive安装部署(最小化部署) 安装部署Hive(最小化只用于本机测试环境中,不可用于生产环境),并运行。 步骤: (1)把apache-hive-3.1.3-bin.tar.gz解压到/opt/module/目录下&…...

三大特性之多态
文章目录 静态的多态动态的多态虚函数虚函数的重写(覆盖)利用虚函数重写实现多态重写的两个例外1.协变2.析构函数的函数名不同 C11的override和final 重载,重写(覆盖),重定义(隐藏)抽…...

单调队列优化dp
文章目录 单调队列优化dp烽火传递修剪草坪绿色通道琪露诺旅行问题Watching Fireworks is Fun瑰丽华尔兹股票交易 单调队列优化dp 文章首发于我的个人博客:欢迎大佬们来逛逛 单调队列优化dp的建模形式:这是窗口右滑动的情况 对于窗口左滑动的也是同理。…...

【低压配电漏电继电器660V/LLJ-100H/AC220V 中性点漏电保护 JOSEF】
LLJ-F(S)系列漏电继电器 系列型号: LLJ-10F(S)漏电继电器LLJ-15F(S)漏电继电器LLJ-16F(S)漏电继电器 LLJ-25F(S)漏电继电器LLJ-30F(S)漏电继电器LLJ-32F(S)漏电继电器 LLJ-60F(S)漏电继电器LLJ-63F(S)漏电继电器LLJ-80F(S)漏电继电器 LLJ-100F(S)漏电继电器LLJ-120…...

[数据结构习题]栈——中心对称链
[数据结构习题]栈——中心对称链 👉知识点导航💎:【数据结构】栈和队列 👉[王道数据结构]习题导航💎: p a g e 70.4 page70.4 page70.4 本节为栈和链表综合练习题 题目描述: 🎇思路…...

AMD Software Adrenalin Edition 23.5.1驱动发布,快速获取驱动
AMD新驱动赶在五月天发布!AMD Software Adrenalin Edition 23.5.1驱动 ,为部分游戏带来支持,以及为重要的软件带来修复。驱动人生带大家一览AMD WHQL 23.5.1驱动的优化内容。 游戏方面,AMD WHQL 23.5.1主要为游戏《指环王&#x…...

Visual Studio内引用Lua解释器,编译Lua源码,执行Lua脚本
前言 本篇在讲什么 在Visual Studio中引入lua的解释器 使用C调用Lua文件 本篇适合什么 适合初学Lua的小白 适合需要C/C和lua结合开发的人 本篇需要什么 对Lua语法有简单认知 对C/C语法有简单认知 依赖Lua5.1的环境 依赖VS 2017编辑器 本篇的特色 具有全流程的图文…...

【赏】C语言迷宫游戏设计如何解决屏幕严重刷屏问题同时实现运行时间的显示
要解决屏幕严重刷屏问题,可以参考以下方法: 在每次刷新前清空屏幕,使用system("cls")命令来实现清屏。 只在需要更新的地方进行刷新,而不是整个屏幕都重新绘制。在此代码中,只需要在用户输入移动指令后更新电子鼠的位置即可,不用每次循环都重新画整个迷宫。同时…...

Spring Boot如何实现接口文档自动生成
Spring Boot如何实现接口文档自动生成 在开发Web应用程序时,接口文档是非常重要的一环,它可以帮助我们快速了解API的功能和使用方法,同时也是与其他开发人员和团队协作的重要工具。然而,手动编写和维护接口文档是一项繁琐的工作&…...

二进制概述-0day漏洞利用原理(1)
二进制利用基本原理,Lord PE的使用,凡是资源性的物质且可表达的皆可利用。 往期文章: 漏洞概述-0day漏洞利用原理(0)_luozhonghua2000的博客-CSDN博客 PE 文件格式 PE (Portable Exec utable) 是 Win32 平台下可执行文件遵守的数据格式。常见的可执行文件(如“*.exe”文件…...

加密与解密 调试篇 动态调试技术 (二)-常见断点
目录 常见的断点 1.INT 3 断点 检测 绕过 2.硬件断点 原理 我们给出硬件中断的例子 删除硬件断点 3.内存断点 原理 例子 删除 区别 总结 4.内存访问一次性断点 5.消息断点 例子 删除 6.条件断点 (1)按寄存器条件中断 (2&…...

【JavaScript】拾遗(5.25)
文章目录 1. JavaScript2.HTML嵌入JS的第一种方式:行间事件3.HTML嵌入JS的第二种方式:脚本块的方式4. HTML嵌入JS的第三种方式:外部式(外链式)5. 局部变量和全局变量6. 函数7.事件8.回调函数8.1 注册事件8.2 代码的执行顺序 1. JavaScript JavaScript是一门脚本语言。…...