【问题标题】:MySQL vs PostgreSQL performance with a complex query matching patterns具有复杂查询匹配模式的 MySQL 与 PostgreSQL 性能
【发布时间】: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 的输出?表只读?或者写操作的频率如何?

标签: mysql sql postgresql query-optimization database-performance


【解决方案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 ALLUNION 将用于折叠重复项,这显然不是这里的情况,而且会更昂贵。
  • 由于您的前导 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-23
      • 2019-06-17
      • 2016-07-02
      • 2019-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多