数据库的 IO 到底有多慢?

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 本文通过对比Java程序从Oracle、MySQL数据库读取数据与读取文本文件的性能,揭示数据库IO速度远低于文件读取的现状。在相同硬件环境下,读取3000万行记录,Oracle耗时280秒,MySQL耗时380秒,而文本文件仅需42秒。此外,通过SPL实现并行处理,可显著提升读取速度,尤其是在处理大规模数据时。实验还探讨了数据库接口慢的问题及其对性能的影响,提出在追求高性能计算时应尽量避免从数据库读取数据的建议。

有过多年应用开发经验的同学大都会体验过数据库 IO 比较慢的情况,但到底会慢到什么程度,特别是和其它读写数据的手段相比的差距,可能很多人还没有感性认识。
Java 是普遍采用的应用开发技术,我们来实际测试一下,Java 程序从 Oracle 和 MySQL 这两种典型数据库中读数的性能,并和读文本文件对比。
用国际标准 TPCH 的工具生成数据表,选用其中的 customer 表,3000 万行,8 个字段。生成的原始文本文件有 4.9G。将这些数据导入到 Oracle 和 MySQL 中。
硬件环境是单台 2CPU 共 16 核的服务器,文本文件和数据库都在 SSD 硬盘上。所有测试都在本机完成,没有实质上的网络传输时间。

Java 代码直接写起来比较麻烦,我们这里用 SPL 编写,SPL 就是简单封装了 Java 的读数动作,最后都是通过数据库的 JDBC 驱动取数,不会影响性能。这里用 SPL 游标取出数据并且全部转换成内存对象才算是完整的读数动作。
QQ_1729476956782.png

将整个 3000 万行的表全部读出,Oracle 大约耗时 280 秒,平均每秒 10 万行,MySQL 约 380 秒,平均每秒 8 万行。
读取速度和字段数量及数据类型都相关,当然也和硬件环境相关,所以这个测试结果只能作为一种参考,换了环境可能会相差很大。

但同等环境下和其它数据读取手段就有可比性了,我们还是用 SPL 直接读取 TPCH 生成的文本文件:
QQ_1729488651492.png

和数据库的测试一样,用 SPL 游标取出数据并转换成内存对象。读完 3000 万行仅用了 42 秒。比 Oracle 快了 6 倍多,比 MySQL 快了 9 倍!
我们知道,文本解析是非常麻烦的事情,非常消耗 CPU,但即使这样,从文本文件读数还是远远快于从数据库读数。

我们再来测试二进制文件,感受一下文本解析造成的性能损失。
为了对比明显以及后面还要做的并行测试,我们换了更大的 orders 表,有 3 亿行,9 个字段。从文本文件读数的代码和刚才类似,实测耗时 483 秒
将这个文本文件转换成 SPL 的组表文件,再测试读取速度:

QQ_1729488729109.png

耗时 164 秒,大概比读文本文件快 3 倍。
这是情理之中的事,因为二进制数据不再需要解析,可以直接产生对象,计算量少了很多,因而要更快。

按说数据库存储也是二进制格式,也没有文本解析的麻烦。因为要考虑写入而不能压缩,速度赶不上紧凑的 SPL 组表还算是正常的,但比文本文件还慢就有点难以理解了。
事实上,如果用 SQL 针对这个数据表做一次遍历式的聚合运算,返回很小的结果集,就会发现速度也挺快,会比基于文本文件上做同样运算快得多。这说明在数据库内部遍历数据表并不慢,也就是说这个存储格式本身的性能并不差。
慢都慢在接口上了,就是 JDBC 的驱动非常慢。这甚至会让人感觉是故意而为,就是期望甚至强迫数据不要出库,一切运算都放在数据库内实现。
这样,我们会有一个结论:追求大数据计算性能的时候,不能从数据库临时读数来计算,计算任务最好不要出库。如果某个任务一定要读出数据才能计算(因为有时 SQL 很难写甚至写不出来某些计算逻辑),那就别把数据放进数据库中了。数据继续在数据库中,而在外部无论怎样实现高性能算法,大部分情况都是无济于事的,除非数据量很小。
所以,以提升 SQL 计算性能为目标的 SPL 必须自己实现某种存储格式,不可能基于数据库的存储实现高性能。

如果场景实在需要从数据库中读出数据,又有什么办法提速呢?
仅仅是接口速度慢,也就是说这个慢并不是数据库负担重造成的,这时候可以使用并行技术来提速。
Java 实现多线程并行有点麻烦,我们用 SPL 写出并行取数的代码来测试:
QQ_1729488767903.png

注意每个线程都要独立连接数据库,不能共用同一个连接。
实测表明,在线程数不多的情况(一般 <10),能达到接近线性提速的效率,也就是有几个读数线程,读数速度就能接近快几倍,实测 6 线程能快出 5 倍。
这里要先计算出总的数据行数,然后再为每个线程拼出 WHERE 条件读取其中一部分数据,这意味着数据库多做了很多计算动作,但读取性能仍然有相当明显的提升,这进一步说明慢主要是慢在接口上,而不是数据库内部的读取和计算慢。

当然,用文件存储时,就更容易用并行提速了,SPL 实现这些并行计算都很简单:
文本并行取数:
QQ_1729488815443.png

组表并行取数:
QQ_1729488836151.png

实测结果和数据库类似,在线程数不很多的情况,也能达到线性提速。这里测试的 4 线程,文本读数速度提升了 3.6 倍,组表读数速度提升了 3.8 倍。

相关文章
阿里Java高级岗中间件二面:GC+IO+JVM+多线程+Redis+数据库+源码
虽然“钱多、事少、离家近”的工作可能离技术人比较远,但是找到一份合适的工作,其实并不像想象中那么难。但是,有些技术人确实是认真努力工作,但在面试时表现出的能力水平却不足以通过面试,或拿到高薪,其实不外乎以下 2 个原因:
|
SQL Oracle 关系型数据库
数据库连接报错之IO异常(The Network Adapter could not establish the connection)
有以下四个原因: 1. ORACLE数据库 1、oracle配置 listener.ora 和tnsnames.ora 文件里面查看是否配置正确
2789 1
|
消息中间件 NoSQL Dubbo
阿里Java高级岗中间件二面:GC+IO+JVM+多线程+Redis+数据库+源码
一转眼,都2023年了,你是否在满意的公司?拿着理想的薪水? 虽然“钱多、事少、离家近”的工作可能离技术人比较远,但是找到一份合适的工作,其实并不像想象中那么难。但是,有些技术人确实是认真努力工作,但在面试时表现出的能力水平却不足以通过面试,或拿到高薪,其实不外乎以下 2 个原因: 第一,“知其然不知其所以然”。做了多年技术,开发了很多业务应用,但似乎并未思考过种种技术选择背后的逻辑。所以,他无法向面试官展现出自己未来技术能力的成长潜力。面试官也不会放心把具有一定深度的任务交给他。 第二,知识碎片化,不成系统。在面试中,面试者似乎无法完整、清晰地描述自己所开发的系统,或者使用的相关技术。
|
SQL 前端开发 数据库
解决读写分离主从数据库之间数据不同步的问题 Slave_SQL_Running: No slave_io_running:no
解决读写分离主从数据库之间数据不同步的问题 Slave_SQL_Running: No slave_io_running:no
177 0
|
SQL 关系型数据库 数据库
在线数据库关系图设计工具 dbdiagram.io
在线数据库关系图设计工具 dbdiagram.io
3666 0
在线数据库关系图设计工具 dbdiagram.io
|
SQL JavaScript Java
面试官:为什么数据库连接池不采用 IO 多路复用?
面试官:为什么数据库连接池不采用 IO 多路复用?
|
存储 Cloud Native 测试技术
【数据库】存算分离之minio IO测试(一)
【数据库】存算分离之minio IO测试(一)
349 0
【数据库】存算分离之minio IO测试(一)
|
SQL Oracle 网络协议
SQL Developer 连接 oracle数据库 报错 Io 异常 The Network Adapter could not establish the connection的三种解决方法
SQL Developer 连接 oracle数据库 报错 Io 异常 The Network Adapter could not establish the connection的三种解决方法
1337 0
SQL Developer 连接 oracle数据库 报错 Io 异常 The Network Adapter could not establish the connection的三种解决方法
|
SQL JavaScript Java
面试官:为什么数据库连接池不采用 IO 多路复用?我懵逼了。。
面试官:为什么数据库连接池不采用 IO 多路复用?我懵逼了。。
244 0
|
SQL JavaScript Java
为何数据库连接池不采用IO多路复用?
今天我们聊一个不常见的 Java 面试题:为什么数据库连接池不采用 IO 多路复用?
367 0
为何数据库连接池不采用IO多路复用?