Postgres 数据库中 ULID 和 UUID 的性能

news2024/12/28 22:26:43

大家好!在本文中,我想分享我对经常用作标识符的数据类型的知识和看法。今天我们将同时讨论两个主题。这些是数据库端按键和键的数据类型衡量的搜索速度。

我将使用PostgreSQL数据库和演示Java服务来比较查询速度。

UUID 和 ULID

为什么我们需要某种难以理解的 ID 类型?我不会谈论分布式系统、服务的连接性、敏感数据等。如果有人对此感兴趣,他们可以谷歌它 - 目前我们对性能感兴趣。顾名思义,我们将讨论两种类型的键:UUID 和 ULID。

UUID早已为大家所熟知,但ULID可能有些人感到陌生。ULID 的主要优点是它是单调递增的并且是可排序的类型。当然,这些还不是全部差异。就我个人而言,我也喜欢其中没有特殊字符的事实。

一个小题外话,我很早之前注意到很多团队varchar(36)在PostgreSQL数据库中使用数据类型来存储UUID,而我不喜欢这样,因为这个数据库有UUID对应的数据类型。稍后,我们将看到哪种类型在数据库方面更可取。因此,我们不仅会在后端比较两种数据类型,还会在数据库端以不同格式存储 UUID 时的差异。

比较

那么让我们开始比较一下吧。

  • UUID长度为36个字符,占用128位内存。

  • ULID 长度为 26 个字符,也占用 128 位内存。

对于我的示例,我在数据库中创建了两个包含三个字段的表:

CREATE TABLE test.speed_ulid(    id      varchar(26) PRIMARY KEY,    name    varchar(50),    created timestamp);CREATE TABLE test.speed_uuid(    id       varchar(36) PRIMARY KEY,    name    varchar(50),    created timestamp);

varchar(36)对于第一次比较,我按照通常的做法以格式存储了 UUID 。在数据库中,我在每个表中记录了 1,000,000。

测试用例将包含 100 个使用先前从数据库中提取的标识符的请求;也就是说,当调用测试方法时,我们将访问数据库100次并通过键检索实体。连接将在测量前创建并预热。我们将进行两次测试运行,然后进行 10 次有效迭代。为了您的方便,我将在文章末尾提供 Java 代码的链接。

抱歉,这些测量是在标准 MacBook Pro 笔记本电脑上进行的,而不是在专用服务器上进行的,但我认为除了数据库和后端之间的网络流量花费的时间增加之外,结果不会有显着差异。

以下是一些背景信息:

  • # CPU I9-9980HK

  • # CPU 数量:16

  • # 内存:32GB

  • # JMH版本:1.37

  • # 虚拟机版本:JDK 11.0.12、Java HotSpot(TM) 64 位服务器虚拟机、11.0.12+8-LTS-237

  • # DB:PostgreSQL 13.4,版本 1914,64 位

将用于通过键获取实体的查询:​​​​​​​

SELECT * FROM test.speed_ulid where id = ?SELECT * FROM test.speed_uuid where id = ?

测量结果

我们来看看测量结果。让我提醒您,每个表有 1,000,000 行。

  • 两种类型的标识符都以 varchar 形式存储在数据库中

图片

我多次运行此测试,结果大致相同:要么是 ULID 快一点,要么是 UUID。从百分比来看,差异几乎为零。

好吧,你可以不同意这些类型之间没有区别。我想说的是,不可能在数据库端使用其他数据类型。

  • UUID 为 uuid,ULID 为数据库中的 varchar

对于下一个测试,我将test.speed_uuid表中的数据类型从 更改varchar(36)为uuid。

图片

在这种情况下,差异很明显:4.5% 支持 UUID。

uuid正如您所看到的,在服务端有同名类型的情况下,使用数据库端的数据类型是有意义的。这种格式的索引在 PostgreSQL 中得到了很好的优化,并显示出良好的结果。

好吧,现在我们绝对可以分道扬镳了。或不?

如果您查看索引搜索查询计划,您可以((id)::text = '01HEE5PD6HPWMBNF7ZZRF8CD9R'::text)在我们使用 varchar 的情况下看到以下内容。

一般来说,比较两个文本变量是一个相当慢的操作,因此也许不需要以这种格式存储 ID。或者还有其他方法可以加快密钥比较速度吗?hash首先,我们为具有 ULID 的表创建另一个“ ”类型的索引。​​​​​​​

create index speed_ulid_id_index    on test.speed_ulid using hash (id);

让我们看看查询的执行计划:

图片

我们将看到数据库使用哈希索引,而不是本例中的 B 树。让我们运行测试,看看会发生什么。

  • varchar + index(hash) 表示 ULID,uuid 表示 UUID

图片

uuid这种组合相对于其作弊指数增加了 2.3% 。

我不确定在一个字段上保留两个索引是否合理。因此,值得考虑是否还有更多事情可以做。在这里,值得回顾过去并记住uuid过去如何存储一些其他字符串标识符。没错:文本或字节数组。

因此,让我们尝试这个选项:我删除了 ULID 的所有索引,将其转换为bytea,然后重新创建主键。

  • bytea 代表 ULID,uuid 代表 UUID

图片

结果,我们得到了与上次运行时使用附加索引大致相同的结果,但我个人更喜欢这个选项。

数据库中2,000,000行的测量结果:

图片

数据库中3,000,000行的测量结果:

图片

我认为继续进一步测量是没有意义的。模式仍然是:保存为 ULID 的性能bytea略优于保存为数据库中的 UUID uuid。

如果我们从第一次测量中获取数据,很明显,在小操作的帮助下,如果使用varchar.

因此,如果您已经读到这里,我认为您对这篇文章很感兴趣,并且您已经自己得出了一些结论。

值得注意的是,测量是在后端部分和数据库的理想条件下进行的。我们没有运行任何并行进程来向数据库写入数据、更改记录或在后端执行复杂的计算。

结论

让我们回顾一下材料。你学到了什么有用的东西?

  1. 不要忽视 PostgreSQL 端的 uuid 数据类型。也许有一天这个数据库会出现 ULID 的扩展,但现在我们只能用现有的。 

  2. 有时候,手动创建一个额外的所需类型的索引是值得的,但也要考虑到额外的开销。 

  3. 如果你不怕多余的工作 - 即编写自己的类型转换器 - 那么如果数据库端没有对应的标识符类型,你应该尝试使用 bytea。 

主键应该使用什么类型的数据,以及应该以什么格式存储?对这些问题我没有一个明确的答案:这一切都取决于许多因素。同样值得注意的是,对 ID 的数据类型的合理选择,不仅仅是它,有时在你的项目中可以起到重要作用。


作者:Artem Artemev 

更多技术干货请关注公号【云原生数据库

squids.cn,云数据库RDS,迁移工具DBMotion,云备份DBTwin等数据库生态工具。

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

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

相关文章

MyBatis使用教程详解<下>

回顾上一篇博文,我们讲了如何使用注解/XML的方式来操作数据库,实际上,一个Mapper接口的实现,这两种方式是可以并存的. 上一篇博文中,我们演示的都是比较简单的SQL语句,没有设计到复杂的逻辑,本篇博文会讲解复杂SQL的实现及一些细节处理.话不多说,让我们开始吧. 一. #{}和${} …

基于LNMP快速搭建WordPress平台

目录 1 LNMP简介 2 WordPress简介 3 安装MySQL环境 3.1 安装MySQL 3.1.1 下载wget工具 3.1.2 下载MySQL官方yum源安装包 3.1.3 安装MySQL官方yum源 3.1.4 mysql安装 3.2 启动MySQL 3.3 获取默认密码 3.4 登录MySQL ​ 3.5 修改密码 3.6 创建WordPress数据库并授权 3.6.1 创…

5 时间序列预测入门:LSTM+Transformer

0 引言 论文地址:https://arxiv.org/abs/1706.03762 1 Transformer Transformer 模型是一种用于处理序列数据的深度学习模型,主要用于解决自然语言处理(NLP)任务。它在许多 NLP 任务中取得了重大突破,如机器翻译、文本…

微服务实战系列之Cache

前言 欢迎来到Cache(缓存)的世界! 自从世界第一台计算机诞生之日起,人们对效率的渴望逐步增强。从CPU到存储,从芯片到内存,一批又一批的先驱以一种孜孜不倦的“工匠”精神,为计算机运行效率的提…

【数据结构 —— 二叉树的链式结构实现】

数据结构 —— 二叉树的链式结构实现 1.树的概念及其结构1.1.树概念1.2.树的结构1.3树的相关概念1.4.树的表示1.5. 树在实际中的运用(表示文件系统的目录树结构) 2.二叉树的概念及其结构2.1二叉树的概念2.2.现实中的二叉树:2.3. 特殊的二叉树…

Flutter开发type ‘Future<int>‘ is not a subtype of type ‘int‘ in type cast错误

文章目录 问题描述错误源码 问题分析解决方法修改后的代码 问题描述 今天有个同事调试flutter程序时报错,问我怎么解决,程序运行时报如下错误: type ‘Future’ is not a subtype of type ‘int’ in type cast 错误源码 int order Databas…

FFmpeg介绍

官方网站:http://www.ffmpeg.org/ 项目组成 libavformat 封装模块,封装了Protocol层和Demuxer、Muxer层,使得协议和格式对于开发者来说是透明的。FFmpeg能否支持一种封装格式的视频的封装与解封装,完全取决于这个库&#xff0c…

《微信小程序开发从入门到实战》学习三十五

4.2 云开发JSON数据库 4.2.3 权限控制 在云开发控制台可以对数据库中的数据进行操作, 在小程序端和云函数可以分别使用小程序API和服务端API对数据中的数据进行操作。 以上操作受到权限控制。 对数据库进行查询属于读操作,增删改操作属于写操作。 …

Python自动化办公:PDF文件的加密与解密

在本篇文章中,我们将介绍如何使用PyPDF2库对PDF文件进行加密和解密操作。 包括如何给PDF文件添加密码,以及如何从受密码保护的PDF文件中删除密码。 注:删除密码的操作,前提是需要知道密码哦 1. 安装PyPDF2库 首先,…

【小黑嵌入式系统第十课】μC/OS-III概况——实时操作系统的特点、基本概念(内核任务中断)、与硬件的关系实现

文章目录 一、为什么要学习μC/OS-III二、嵌入式操作系统的发展历史三、实时操作系统的特点四、基本概念1. 前后台系统2. 操作系统3. 实时操作系统(RTOS)4. 内核5. 任务6. 任务优先级7. 任务切换8. 调度9. 非抢占式(合作式)内核10…

el-table,列表合并,根据名称列名称相同的品名将其它列值相同的进行合并

el-table,列表合并,根据名称列名称相同的品名讲其它列值相同的进行合并,并且不能垮品名合并 如图 用到el-table合并行的方法合并 tableSpanMethod({ row, column, rowIndex, columnIndex }) {if (column.property "materielName") {//合并商品名const _row this…

CI/CD 构建中能保护好 SSHKEY吗?

目录 背景 方案 编码存储 逐行存储 合并存储 打马赛克 结论 背景 使用极狐GitLab CI/CD,在部署方面,主要有两种方式: 部署到K8S集群 Push模式:流水线通过kubectl执行命令部署,这需要把K8S的权限给流水线&#xf…

【python程序】把小于10的数值都变成1

【python程序】把小于10的数值都变成1 import numpy as np import xarray as xra xr.DataArray(np.arange(25).reshape(5, 5)) a[np.where(a < 10)] 1 print(a)

微信小程序+中草药分类+爬虫+torch

1 介绍 本项目提供中草药数据集&#xff0c;使用gpu、cpu版本的torch版本进行训练&#xff0c;将模型部署到后端flask&#xff0c;最后使用微信小程序进行展示出来。 数据爬虫可以参考&#xff1a;http://t.csdnimg.cn/7Olus 项目中的爬虫代码&#xff0c;并且本项目提供相同的…

拆解按摩器:有意思的按键与LED控制电路,学习借鉴一下!

拆解 外观和配色个人感觉还行,比较青春 拉开拉链&#xff0c;拆开外面的布面&#xff0c;里面还有一层纱面 按键部分使用魔术贴固定 拆开纱面后&#xff0c;看到里面的结构&#xff0c;整体是一个海绵 可以看到如下&#xff0c;电池&#xff0c;按键板&#xff0c;充电线的三条…

匿名内部类(内部类) - Java

匿名内部类 1、理解2、语法3、使用&#xff08;1&#xff09;基于接口的内部类&#xff08;2&#xff09;基于类的内部类&#xff08;3&#xff09;基于抽象类的匿名内部类 4、细节&注意事项5、最佳应用场景&#xff08;1&#xff09;当作实参直接传递&#xff0c;简洁高效…

Alibaba Java诊断工具Arthas查看Dubbo动态代理类

原创/朱季谦 阅读Dubbo源码过程中&#xff0c;会发现&#xff0c;Dubbo消费端在做远程调用时&#xff0c;默认通过 Javassist 框架为服务接口生成动态代理类&#xff0c;调用javassist框架下的JavassistProxyFactory类的getProxy(Invoker invoker, Class<?>[] interfac…

GO 集成Prometheus

一、Prometheus介绍 Prometheus&#xff08;普罗米修斯&#xff09;是一套开源的监控&报警&时间序列数据库的组合&#xff0c;起始是由SoundCloud公司开发的。随着发展&#xff0c;越来越多公司和组织接受采用Prometheus&#xff0c;社会也十分活跃&#xff0c;他们便…

GAN:GAN论文学习

论文&#xff1a;https://arxiv.org/pdf/1406.2661.pdf 发表&#xff1a;2014 一、GAN简介&#xff1a;Generative Adversarial Network GAN是由Ian Goodfellow于2014年提出&#xff0c;GAN&#xff1a;全名叫做生成对抗网络。GAN的目的就是无中生有&#xff0c;以假乱真。 …

day64 django中间件的复习使用

django中间件 django中间件是django的门户 1.请求来的时候需要先经过中间件才能达到真正的django后端 2.响应走的时候也需要经过中间件 ​ djangp自带七个中间件MIDDLEWARE [django.middleware.security.SecurityMiddleware,django.contrib.sessions.middleware.SessionMiddle…