具有复杂查询匹配模式的 MySQL 与 PostgreSQL 性能
Posted
技术标签:
【中文标题】具有复杂查询匹配模式的 MySQL 与 PostgreSQL 性能【英文标题】:MySQL vs PostgreSQL performance with a complex query matching patterns 【发布时间】:2011-04-16 22:34:27 【问题描述】:我有一个复杂的数据库,大约有 30 个表。一个表有超过 500,000 行,另一个超过 15,000 行,我在一个单独的数据库中使用这两个表,直到今天我决定只在一个数据库中实现。
在今天之前,500,000 行的表在 mysql 数据库中,而 15,000 行的表在 PostgreSQL 中。在一个频繁使用的页面中,这是 php 基准测试的结果:
getSimilarAvaiable - 0.0287 s
getUnavaiable - 0.27 s
ProcessDataOfUnavaiable - 1.4701 s
Process - 1.8622 s
TotalPageTime - 3.631 s
在我将所有内容迁移到 PostgreSQL 并使用相同的 SQL 代码而不做任何更改后,同一页面的结果是这样的:
getSimilarAvaiable - 2.7465 s
getUnavaiableCars - 9.0763 s
ProcesseDataOfUnavaiableCars - 1.4167 s
ProcessCars - 1.7207 s
TotalPageTime - 14.9602 s
我把所有东西都放在 MySQL 中,相同的索引,所有东西,但我不明白为什么会有这么大的差异。我应该怎么做才能优化它?
编辑:现在解释得更好了。
500.00 表由以下结构组成:
id - bigint (primary key)
plate- varchar(10) Unique key
manufacturer - varchar(30)
vin - varchar(30)
主要查询是这样的:
SELECT plate, vin, 1 as n, substr(plate,1,2) as l
FROM imtt_vin WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
UNION
SELECT plate, vin, 3 as n, substr(plate,4,2) as l
FROM imtt_vin WHERE substr(plate,4,1) >= 'A' and substr(plate,4,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
UNION
SELECT plate, vin, 2 as n, substr(plate,7,2) as l
FROM imtt_vin WHERE substr(plate,7,1) >= 'A' and substr(plate,7,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
ORDER BY n, l, plate;
EDIT2:使用复杂的单个查询进行测试,我将其从 15 秒减少到 8/9 秒。即便如此,这对我来说也太过分了。
【问题讨论】:
@David Believe me 30 表和 500,000 行远非复杂 :) 你是在 MySQL 还是 InnoDB 中使用 MyISAM? MyISAM 速度更快,但支持的功能更少。 没有看到您的任何查询或涉及的表和索引的结构,任何人都很难帮助您。 使用 EXPLAIN 查看查询是如何执行的。也可以使用 substr() 创建一些索引,这可能会改善很多:EXPLAIN 会显示给你。 您的程序的版本?索引的定义? EXPLAIN / EXPLAIN ANALYZE 的输出?表只读?或者写操作的频率如何? 【参考方案1】:您需要发布 EXPLAIN yourquery (for mysql) 和 EXPLAIN ANALYZE yourquery (for postgres) ;没有它,就不可能说任何相关的东西。
也选择 pg_relation_size('imtt_vin')
例如,“?”的值是什么?在这个查询中?
SELECT plate, vin, 1 as n, substr(plate,1,2) as l
FROM imtt_vin WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
我不知道你在哪里工作的车牌,但是这部分:
WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z'
可能会选择数据库中的所有行,因此其唯一目的是消耗 CPU 周期。你至少可以像这样重写它(和所有其他的)以避免调用 substr() :
WHERE substr(plate,1,1) BETWEEN 'A' AND 'Z'
当然,当它没有用时,删除条件。
然后我们有:
manufacturer ILIKE '%".self::$Manufacturer."%'
糟糕的数据库设计:世界上有 500.000 家汽车制造商吗?可能不是。您应该将制造商放在另一个表中并使用外键。这会将这种不可索引的条件变成可索引的条件。
对于其余部分,请发布 EXPLAIN / EXPLAIN ANALYZE。
【讨论】:
条件实际上不是“不可索引”。有一种方法。在我的回答中概述了它。【参考方案2】:如果您在 MySQL 中使用 MyISAM,理论上可以解释性能差异(因为关于您的数据库设计和执行的查询没有太多公开)。关于两个 RDBMS 之间的交叉性能,我建议您查看 this comparison page(锚定到 MyISAM 部分)。
【讨论】:
是的,我使用 MyISAM。我在第一篇文章中发布了我使用的结构和查询。 @DavidMagalhães:MyISAM 对于原始的蛮力查询非常快。我认为我为 PostgreSQL 概述的索引仍然可以解决它。【参考方案3】:MySQL 默认使用更多内存。我认为它被 def install 分配使用超过 256MB。不确定确切的数字。 PostgreSQL 默认设置为使用 32MB 之类的大小。尝试在配置文件中将每个内存增加到 1GB,然后运行基准测试并返回给我们。
【讨论】:
默认使用 16MB 或 32MB。每个已经达到 128MB,没有任何变化。 @DavidMagalhães:您可以调整很多不同的设置。 Postgres Wiki 是您入门的好地方。【参考方案4】:在我看来,您可能没有更新 Postgres 数据库的统计信息。如果统计数据不当,数据库的性能将不会很好。
【讨论】:
我激活了 autoVacuum,但什么也没发生 :( 您需要发出 VACUUM ANALYZE 语句。 Autovacuum 对这样的大批量插入没有帮助。 @Denis:Autovacuum 将分析所有表(使用默认设置),但可能需要一些时间。如果您在批量插入后立即运行查询,则只需要手动ANALYZE
。【参考方案5】:
查询
(
SELECT 1 AS n, left(plate, 2) AS l, plate, vin
FROM imtt_vin
WHERE left(plate, 1) BETWEEN 'A' AND 'Z'
AND manufacturer ILIKE '%".self::$Manufacturer."%'
AND vin LIKE ? -- You probably mean: vin = ?
ORDER BY l, plate
)
UNION ALL
(
SELECT 3 AS n, substr(plate, 4, 2) AS l, plate, vin
FROM imtt_vin
WHERE substr(plate, 4, 1) BETWEEN 'A' AND 'Z'
AND manufacturer ILIKE '%".self::$Manufacturer."%'
AND vin LIKE ?
ORDER BY l, plate
)
UNION ALL ...
使用UNION ALL
。 UNION
将用于折叠重复项,这显然不是这里的情况,而且会更昂贵。
由于您的前导 ORDER BY 项目是 n
,因此对查询的各个部分进行排序可能更有效。为此需要额外的一组括号。
left (plate, 2)
比 substr(plate, 1, 2)
快一点。仅适用于前导子字符串(您的第一个 SELECT
)。
索引
默认B-tree index only works for left-anchored LIKE
expressions。但trigram GiST 或 GIN 索引可用于非左锚定模式。您需要附加模块pg_trgm
。在 PostgreSQL 9.1 或更高版本中使用 CREATE EXTENSION
为每个数据库安装一次。查阅旧版本的手册。
CREATE EXTENSION pg_trgm;
我没有太多信息可以继续,基本的partial GIN indexes 应该可以工作奇迹:
CREATE INDEX imtt_vin_partial_gist_idx ON imtt_vin
USING gin (manufacturer gin_trgm_ops)
WHERE left(plate, 1) BETWEEN 'A' AND 'Z';
CREATE INDEX imtt_vin_partial_gist_idx ON imtt_vin
USING gin (manufacturer gin_trgm_ops)
WHERE substr(plate, 4, 1) BETWEEN 'A' AND 'Z';
-- more ...
我没有在索引中包含vin
,因为您可能希望在其中使用相等运算符=
。
部分索引上的谓词必须在查询中重复(或多或少),以便查询规划器了解该索引是适用的。
三元组索引适用于不区分大小写的匹配。
用EXPLAIN ANALYZE
测试索引是否被实际使用。如果是,查询时间应该是 毫秒,而不是秒。
索引维护的写入操作需要(少量)成本来提高速度。并且索引通常是磁盘上表大小的几倍。
你不能用 MySQL 做任何这些。
【讨论】:
【参考方案6】:您仍然没有提供足够的信息——您有哪些索引、慢查询的 EXPLAIN ANALYZE 输出等。
关于优化示例查询的一些想法:
1: UTF-8 字符串函数一般不是很快。如果要加速字符串函数,请使用bytea
类型而不是 varchar 用于此列(或将整个数据库编码更改为SQL_ASCII
,但这是不可取的)
2:根据您的查询,数据库可能必须遍历表中的所有行并为每个行计算这些字符串函数。
我不知道他们有多少匹配,所以索引可能没有用,但功能索引可能会帮助你:
CREATE INDEX imtt_vin_plate_1 ON imtt_vin (substr(plate,1,1));
CREATE INDEX imtt_vin_plate_4 ON imtt_vin (substr(plate,4,1));
CREATE INDEX imtt_vin_plate_7 ON imtt_vin (substr(plate,7,1));
3:如果您可以容忍重复输出,请在查询中使用 UNION ALL
而不是 UNION
- 这样可以为您节省一些处理较大结果集的时间。
4:尽可能避免LIKE
/ILIKE
。
【讨论】:
以上是关于具有复杂查询匹配模式的 MySQL 与 PostgreSQL 性能的主要内容,如果未能解决你的问题,请参考以下文章