PostgreSQL 的 LIMIT 操作相比 MySQL 性能是否有极大提高?

2017-12-19 15:00:06 +08:00
 yejinmo

手上的项目由于客户对历史记录查询的要求颇高,MySQL 的性能不太够用了

历史记录表需要在 4 个 int 字段上做筛选

以及一个时间字段上做范围筛选

数据量在千万级,现在 SELECT 一下子要几十秒,主要耗时在了 LIMIT 上

由于筛选之后数据不连续,也想不到什么可以优化的地方了

查了查资料说 PostgreSQL 对大量数据有特殊优化

特想问下 PostgreSQL 的分页是不是要比 MySQL 好些

或者有什么别的潇洒方法解决这个问题

谢谢

2622 次点击
所在节点    数据库
44 条回复
runntuu
2017-12-19 16:50:35 +08:00
@yejinmo MyISAM 只是单纯读性能好,在写并发和事务支持上跟 InnoDB 没有可比性。
另外对于索引的建立,多个独立列的索引不如一个适合大多数查询的覆盖索引。
另外还没有看到你的查询计划,能否一并提出来参考一下?
likuku
2017-12-19 16:54:08 +08:00
翻到笔记, 2013 年前做过对比测试,同一软硬件环境,非常简单的单表,180 万行:

postgres=# select count(*) from status;
count
---------
1841495
(1 row)

Time: 516.717 ms

mysql Innodb 表:

mysql> select count(*) from status;
+----------+
| count(*) |
+----------+
| 1841495 |
+----------+
1 row in set (3.53 sec)

表结构:

| status | CREATE TABLE `status` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` date NOT NULL,
`license` varchar(100) NOT NULL,
`localdomain` varchar(100) NOT NULL,
`mailing` varchar(50) NOT NULL DEFAULT 'unknown',
`isp` varchar(50) NOT NULL,
`success` int(50) NOT NULL,
`spamrelay` int(50) NOT NULL,
`overlimit` int(50) NOT NULL,
`badmailbox` int(50) NOT NULL,
`timeexpired` int(50) NOT NULL,
`unknown` int(50) NOT NULL DEFAULT '0',
`open` int(50) NOT NULL DEFAULT '0',
`click` int(50) NOT NULL DEFAULT '0',
`olapstamp` char(19) NOT NULL DEFAULT '0000-00-00 00:00:00',
`updatestamp` char(19) NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1911671 DEFAULT CHARSET=utf8 |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
yejinmo
2017-12-19 17:03:53 +08:00
@zhNaMore #19
@runntuu #21
相关信息已 Append
之所以考虑换掉 MySQL
是觉得以 MySQL 的机制对大表查询不够友好啊
yejinmo
2017-12-19 17:05:25 +08:00
@likuku #22
额。。postgres 上没有对表和查询做特殊的优化么?
likuku
2017-12-19 17:11:30 +08:00
@yejinmo

count(*) 是比较特别的需求(我那时就是需要这个),对你参考价值有限,建议你直接装一个 PG 载入数据实际测测看。

这个测试里 postgresql 没有作特殊优化,debian 下直接 apt-get 装完就测试。

MySQL 是作了优化,设置了 InnoDB MEM Pool 为内存一半,其他相关优化也做过。

对了,MyISAM 类型表,count(*) 是直接返回数值,没有实际查询耗时(因为它对这个操作设计记得就是删改记录时维护一个计数器,count()直接返回数值)。MyISAM 不支持事务,只有表级锁,没有全文索引,表特别容易坏,只适合纯查询,Innodb 这些年性能也提高很多了,一般也没人用 MyISAM 了。
likuku
2017-12-19 17:14:11 +08:00
@yejinmo 你这台机是 SSD 么?换用 SSD 对 DB 的效能提升简直就是银子弹。
yejinmo
2017-12-19 17:16:35 +08:00
@likuku #25
膜拜大手 我去跑一遍试试
另外配置使用默认会对性能造成很大影响么?
likuku
2017-12-19 17:19:42 +08:00
@yejinmo [配置使用默认] MySQL ? PostgreSQL ?

MySQL 使用默认,尤其 InnoDB 使用默认,则极大影响性能。
likuku
2017-12-19 17:22:01 +08:00
mysql, postgresql 都有内置的 benchmark 基准测试功能,可以用这个测试作对比
likuku
2017-12-19 17:25:50 +08:00
PostgreSQL 性能优化方法 - 1-博客-云栖社区-阿里云 : https://yq.aliyun.com/articles/214

发现这个博客,PG 相关还有好几篇

Greenplum 是基于 PostgreSQL 的并行计算版本,假若单机 DB 还不能满足,可以尝试用它
runntuu
2017-12-19 17:48:47 +08:00
@yejinmo mysql 的 offset 是个坑,offset 的逻辑是读取 500050 条记录,然后抛弃前面的 50w 条数据,返回 50 条记录。所以这么简单的查询,即使走了索引,你的 show profile 还是花了 46s 在 sending data 从磁盘读。
建议你考虑使用字段 event_id 来分页,然后在 limit 里仅限制返回行数。
wucancc
2017-12-19 18:18:17 +08:00
有一个思路。

题主提到了数据不是连续的,是否可以这么解决了。
offset limit 的速度很慢,但是数据表加了索引后对 between and 的查询速度极快。
比如说你的是数据是:1,2,3,4,8,9,10,需要每次取 3 条。
先用 select * from table between n and n+10; 每次多取一些。
再对结果取前 3 条。
如果结果不足 3 条,再查一次 select * from table between n and n+20; 直到结果多余 3 条。
tabris17
2017-12-19 18:25:54 +08:00
现在不是都不用分页了么,都是从当前数据加载更多啊
wucancc
2017-12-19 18:34:37 +08:00
@tabris17 对于后台来说不是一样么,都是再获取一页的数据。在前台展现的是不清空当前数据继续追加。
ke1e
2017-12-19 19:02:31 +08:00
或者可以考虑分表
likuku
2017-12-19 19:14:14 +08:00
[历史记录表需要在 4 个 int 字段上做筛选] 历史记录筛选,这个是不是交由 hadoop 来作个 mapreduce 会更好?
whx20202
2017-12-19 19:14:29 +08:00
我来说说吧
如果仅仅是为了分页的话,可以参考 openstack 的实现
第一页:
select * from instances limit 20;
第二页:
select * from instances where id> 第 20 行的 ID limit 20;

改进版:
select * from instances where id> 第 20 行的 ID and created_at>第 20 行的时间 limit 20;

仅供参考
chengs
2017-12-20 10:01:20 +08:00
@whx20202 为什么不用 pg 呢
whx20202
2017-12-20 10:20:41 +08:00
@chengs
不太懂你说的哪一个领域不用 pg 哈
我司搞 openstack 用的是 pg
开源社区 openstack 主要是 mysql
我个人项目两个都用

pg 如果单纯 limit offset 也很慢,得用一些高级特性或者技巧,德哥的博客里说了不少
我说的那个办法,pg mysql 都能用啊
alcarl
2017-12-20 13:06:20 +08:00
可以考虑用子查询搜索第一条记录 id 的方法再加个时间排序,一般都是这么优化。但数据库不是万能的,还要考虑针对业务来优化一下,几千万行在里面一页一页翻这种需求我是没想出来谁会去翻。。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/415995

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX