0

我有一个复杂的数据库,大约有 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 秒。即便如此,这对我来说也太过分了。

4

6 回答 6

4

您需要发布 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 家汽车制造商吗?可能不是。您应该将制造商放在另一个表中并使用外键。这会将这种不可索引的条件变成可索引的条件。

其余的,发布解释/解释分析。

于 2011-05-02T17:14:30.410 回答
3

如果您在 MySQL 中使用 MyISAM,理论上可以解释性能差异(因为您的数据库设计和执行的查询方面没有太多暴露)。关于两个 RDBMS 之间的交叉性能,我建议您查看此比较页面(锚定到 MyISAM 部分)。

于 2011-04-16T22:42:40.060 回答
3

MySQL 默认使用更多内存。我认为它被 def install 分配使用超过 256MB。不确定确切的数字。PostgreSQL 默认设置为使用 32MB 之类的大小。尝试在配置文件中将每个内存增加到 1GB,然后运行基准测试并返回给我们。

于 2011-04-17T15:08:40.673 回答
2

在我看来,您可能没有更新 Postgres 数据库的统计信息。使用不当的统计数据,数据库的性能将不会很好。

于 2011-04-16T22:47:59.947 回答
2

询问

(
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 树索引仅适用于左锚定LIKE表达式。但是三元组GiST 或 GIN 索引可用于非左锚定模式。您需要额外的模块pg_trgmCREATE EXTENSION在 PostgreSQL 9.1 或更高版本中为每个数据库安装一次。查阅旧版本的手册。

CREATE EXTENSION pg_trgm;

我没有太多信息可以继续,基本的部分 GIN 索引应该可以创造奇迹

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 做任何这些。
于 2012-10-18T23:57:24.733 回答
1

您仍然没有提供足够的信息——您有哪些索引、慢查询的 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尽可能。

于 2011-04-17T22:55:23.523 回答