Postgres vs MySQL

news2024/11/22 23:25:29

主要区别及示例

简而言之,Postgres 和 MySQL 之间的主要区别实际上归结为主索引和辅助索引的实现方式以及数据的存储和更新方式。

让我们进一步探讨这个问题。

但首先... 基础知识

索引是一种数据结构(主要是 B + 树),允许通过多层节点进行键的搜索,数据库将其实现为页面。树的遍历允许消除不包含结果的页面,并缩小包含结果的页面的范围。这一过程一直持续到找到包含键的叶子页面。

叶子节点或页面包含有序键及其值的列表。当找到一个键时,可以获取其值,并且页面会被缓存在数据库的共享缓冲区中,希望未来的查询可能会请求相同页面中的键。

0b57a4af5c1ace284cf2adb76eb0eabe.jpeg

这最后一句是理解数据库工程、管理、编程和建模的基本原则。了解查询是否命中页面中相邻的键将最大程度地减少 I/O 并提高性能。

B + 树索引中的键是创建索引所在表的列(或多个列),而值在 Postgres 和 MySQL 中的实现方式有所不同。让我们探讨一下 Postgres 和 MySQL 中值的含义。

MySQL

在主索引中,值是带有所有属性 * 的完整行对象。这就是为什么主索引通常被称为聚簇索引或我更喜欢的术语 "索引组织表"。这意味着主索引就是表本身。

* 注意,对于行存储,这是正确的。数据库可能使用不同的存储模型,如列存储、图形或文档存储,从根本上讲,这些也可以作为潜在的值。

如果在主索引中查找一个键,你会找到包含该键的页面和它的值,该值是该键对应的完整行,不需要额外的 I/O 操作来获取其他列。

在二级索引中,键是你索引的列(或多个列),而值是指向实际存储完整行位置的指针。二级索引叶子页面的值通常是主键。

这就是 MySQL 的情况。在 MySQL 中,所有的表都必须有一个主索引,而所有额外的二级索引都指向主键。如果你在 MySQL 表中不创建主键,系统会为你自动创建一个。

94394ab439a0e1a705493f7e885a4029.jpeg

Postgres

在 Postgres 中,严格来说没有主索引,所有的索引都是二级索引,它们都指向加载在堆中的数据页中由系统管理的元组标识符(tuple ids)。堆中的表数据是无序的,不像主索引叶子页是有序的。因此,如果你插入了 1-100 行,并且它们都在同一页中,然后后来更新了 1-20 行,这 20 行可能会跳转到另一页,并且变得无序。而在聚簇主索引中,插入操作必须按照键的顺序插入到相应的页中。这就是为什么 Postgres 表通常被称为 " 堆有序表 "而不是" 索引组织表 "。

需要注意的是,在 Postgres 中,更新和删除实际上是插入操作。每次更新或删除都会创建一个新的元组标识符(tuple id),而旧的元组标识符则保留为了多版本并发控制(MVCC)的原因。我稍后会在本文中探讨这个问题。

事实上,仅仅使用元组标识符是不够的。实际上,我们需要同时知道元组标识符和页面编号,这被称为 c_tid。想一想,仅仅知道元组标识符是不够的,我们需要知道元组所在的页。这是在 MySQL 中不需要做的事情,因为我们实际上是通过查找来找到主键所在的页。而在 Postgres 中,我们只需要进行一次 I/O 操作就可以获取到完整的行数据。

c4ee2e1c16e4c1867588ec1a6a6355f5.jpeg

查询费用

请参考以下示例中的表格。

#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)的开销。

如果我们最终要共享内存并处理互斥锁和信号量,为什么不使用线程呢?这只是我的个人观点。

总结

你可以选择适合你的数据库系统。真正重要的是将你的使用情况和查询进行分解,了解每个数据库的功能,看看哪些适合你,哪些不适合你。

这里没有对错之分。

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

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

相关文章

DAY01_MySQL基础数据类型navicat使用DDL\DML\DQL语句练习

目录 1 数据库相关概念1.1 数据库1.2 数据库管理系统1.3 常见的数据库管理系统1.4 SQL 2 MySQL2.1 MySQL安装2.1.1 安装步骤 2.2 MySQL配置2.2.1 添加环境变量2.2.2 MySQL登录2.2.3 退出MySQL 2.3 MySQL数据模型2.4 MySQL目录结构2.5 MySQL一些命令2.5.1 修改默认账户密码2.5.2…

Linux 计划任务(at与crontab)

一次性计划任务 at Linux 中的【 at 】 命令是用来创建一次性计划任务的, at 命令有一个服务 atd 以后台的模式运行,通过检查当前的时间来决定是 否运行 " 计划 " ,默认情况下, atd 服务每 60 秒检 查一次&#x…

【Web服务应用】Nginx服务

Nginx服务 一、Nginx概述1.1Nginx特点1.2Nginx作用1.3Nginx与Apache的差异 二、Nginx进程模型三、编译安装Nginx3.1Nginx服务的检查、启动、停止,重载3.2平滑升级3.3把nginx进程加入到系统服务当中 四、Nginx服务的主配置文件nginx.conf4.1补充什么是IO多路复用4.2根…

R语言:移动平均计算及绘图

问题描述 现在有一个分日期记录DAU的数据,现在需要绘制其360,180,90,30,7日移动平均值,来观测消除了波动干扰的DAU趋势 (实际移动在股价趋势图上非常常见) 原始数据格式如下: day (character) dau (int…

Docker+Jenkins+Gitee自动化部署maven单模块项目

1.简介 各位看官老爷,本文为Jenkins实战,注重实际过程,阅读完会有以下收获: 了解如何使用Docker安装Jenkins了解如何使用Jenkins部署maven项目了解如何使用JenkinsGitee实现自动化部署 2.Jenkins介绍 相信,正在读这…

2023年上半年软件设计师上午真题及答案解析

1.计算机中,系统总线用于( )连接 A.接口和外设 B.运算器,控制器和寄存器 C.主存、外设部件 D.DMA控制器和中断控制器 2.在由高速缓存、主存和硬盘构成的三级存储体系中,CPU执行指令时需要读取数据,那么DMA控制…

深入理解Linux虚拟内存管理(一)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核(一) 深入理解 Linux 内核(二) Linux 设备驱动程序(一) Linux 设备驱动程序(二) Linux 设备驱动程序(三&#xf…

ubantu换配置源

文章目录 1.配置镜像源位置2.进入终端,切换到/home/user/etc/apt/3.默认这个文件是只读的,我们修改一下权限4.修改之前,我们先备份一下系统原来配置的源5.开始修改,打开/etc/apt/sources.list文件,将原来的内容删除&am…

chatgpt赋能python:Python代码怎么打包-全面介绍

Python 代码怎么打包 - 全面介绍 Python 是一种高效、易学易用、灵活多变的编程语言。对于 Python 开发者来说,如何将其编写的程序打包是一个必须掌握的技能。本文将着重介绍 Python 代码打包的方法及其优势,并提供一些实用的工具和技巧。 什么是打包?…

六级备考17天|2017年12月三套真题|翻译与写作|20:45~21:00

目录 第一套 翻译:太湖 中文 英文 词汇 作文 谚语题:respect others, and you will be respected 第二套 翻译:青海湖 中文 英文 词汇 第一套 翻译:太湖 中文 英文 词汇 太湖 Lake Tai 淡水湖 fre…

python+vue学生选课学习成绩分析及可视化分析系统

但目前国内的学习成绩分析及可视化分析信息仍然都使用人工管理,随着学校规模越来越大,同时课程信息量也越来越庞大,人工管理显然已无法应对时代的变化,而学习成绩分析及可视化分析能很好地解决这一问题,轻松应对学习成…

2020第十一届蓝桥杯Python组国赛【真题+解析+代码】

🎁2020第十一届蓝桥杯python组国赛真题 🚀 真题练习,冲刺国赛 🚀 2020年第十一届蓝桥python组国赛真题解析代码 博观而约取,厚积而薄发 🏆国赛真题目录 文章目录 🎁2020第十一届蓝桥杯python组国…

本地部署Jellyfin影音服务器【公网远程影音库】

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 转载自cpolar极点云的文章:零基础搭建私人影音媒体平台【远程访问Jelly…

华为OD机试真题 Java 实现【数组的中心位置】【2023Q1 100分】

一、题目描述 给你一个整数数组nums,请计算数组的中心位置,数组的中心位置是数组的一个下标,其左侧所有元素相乘的积等于右侧所有元素相乘的积。数组第一个元素的左侧积为1,最后一个元素的右侧积为1。如果数组有多个中心位置,应该返回最靠近左边的那一个,如果数组不存在…

chatgpt赋能python:Python代码转为C语言——提高效率的必经之路

Python代码转为C语言——提高效率的必经之路 Python是一种高级编程语言,具有易学易用的优点,因此越来越多的程序员选择使用Python来开发应用程序和脚本。但是,在开发高性能应用程序时,Python的效率问题会成为拦路虎。因此&#x…

每日一题——逆波兰表达式求值(前缀、中缀、后缀表达式的说明,库函数atoi()的解析)

文章目录 每日一题逆波兰表达式求值中缀,前缀(波兰),后缀(逆波兰)表达式的基本概念逆波兰表达式的优点和计算方法优点计算方法 思路函数原型如何将数字入栈库函数atoi() 实现代码 每日一题 逆波兰表达式求…

chatgpt赋能python:Python代码转换:如何将代码从Python2转换为Python3

Python 代码转换:如何将代码从 Python 2 转换为 Python 3 作为一位有10年 Python 编程经验的工程师,我们都知道 Python 的两个主要版本:Python 2 和 Python 3。不过,Python 2 已于2020年正式停止支持,因此&#xff0c…

MySQL-7-权限与密码

一、用户授权与权限撤销 1.1、用户授权: 赋予权限:grant all on *.* to root192.168.4.% identified by "123456";从网络访问本地数据库时:只有 增删改查权限,本地登录则有所有权限。 新建用户,并赋予权限:格式:grant 权限列表 on 库名.表名 to 用户…

图数据库的一些概览

图数据库 图数据库是一种根据节点和边存储数据的数据库。数据以非常灵活的方式存储,无需遵循预定义的模型。该图形成了两个节点之间的关系,这种关系可以是有向的也可以是无向的。这些数据库旨在处理数据/节点之间的复杂关系。 节点用于存储数据。每个节…

chatgpt赋能python:Python-高效的SEO工具

Python - 高效的SEO工具 Python作为一种高效且易于学习的编程语言,广泛应用于各种领域,包括机器学习、数据分析、网站开发等。Python的灵活性和可扩展性也使它成为一种优秀的SEO工具。 Python在SEO中的应用 Python可用于SEO行业中的多个方面。例如&am…