【问题标题】:Understanding EXPLAIN statements in similar MySQL and PostgreSQL databases了解类似 MySQL 和 PostgreSQL 数据库中的 EXPLAIN 语句
【发布时间】:2015-10-21 03:25:03
【问题描述】:

我目前正在开发支持多个数据库的 Web 服务。我正在尝试优化表并修复丢失的索引。以下是 MySQL 查询:

SELECT 'UTC' AS timezone, pak.id AS package_id, rel.unique_id AS relay, sns.unique_id AS sensor, pak.rtime AS time,
   sns.units AS sensor_units, typ.name AS sensor_type, dat.data AS sensor_data,
   loc.altitude AS altitude, Y(loc.location) AS latitude, X(loc.location) as longitude,
   loc.speed as speed, loc.climb as climb, loc.track as track,
   loc.longitude_error as longitude_error, loc.latitude_error as latitude_error, loc.altitude_error as altitude_error,
   loc.speed_error as speed_error, loc.climb_error as climb_error, loc.track_error as track_error
 FROM sensor_data dat
 LEFT OUTER JOIN package_location loc on dat.package_id = loc.package_id
 LEFT OUTER JOIN data_package pak ON dat.package_id = pak.id
 LEFT OUTER JOIN relays rel ON pak.relay_id = rel.id
 LEFT OUTER JOIN sensors sns ON dat.sensor_id = sns.id
 LEFT OUTER JOIN sensor_types typ ON sns.sensor_type = typ.id
 WHERE typ.name='Temperature'
   AND rel.unique_id='OneWireTester'
   AND pak.rtime > '2015-01-01'
   AND pak.rtime < '2016-01-01'

还有解释……

+----+-------------+-------+--------+------------------------------------------+----------------------+---------+------------------------+------+----------------------------------------------------+
| id | select_type | table | type   | possible_keys                            | key                  | key_len | ref                    | rows | Extra                                              |
+----+-------------+-------+--------+------------------------------------------+----------------------+---------+------------------------+------+----------------------------------------------------+
|  1 | SIMPLE      | rel   | ALL    | PRIMARY                                  | NULL                 | NULL    | NULL                   |    5 | Using where                                        |
|  1 | SIMPLE      | pak   | ref    | PRIMARY,fk_package_relay_id              | fk_package_relay_id  | 9       | BigSense.rel.id        |    1 | Using index condition; Using where                 |
|  1 | SIMPLE      | dat   | ref    | fk_sensor_package_id,fk_sensor_sensor_id | fk_sensor_package_id | 9       | BigSense.pak.id        |    1 | NULL                                               |
|  1 | SIMPLE      | sns   | eq_ref | PRIMARY,fk_sensors_type_id               | PRIMARY              | 8       | BigSense.dat.sensor_id |    1 | NULL                                               |
|  1 | SIMPLE      | loc   | eq_ref | PRIMARY                                  | PRIMARY              | 8       | BigSense.pak.id        |    1 | NULL                                               |
|  1 | SIMPLE      | typ   | ALL    | PRIMARY                                  | NULL                 | NULL    | NULL                   |    5 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+--------+------------------------------------------+----------------------+---------+------------------------+------+----------------------------------------------------+

...看起来很简单。我需要在relays 表和sensor_types 上添加索引来优化查询。

PostgreSQL 版本的表几乎相同。但是,当我使用以下查询时:

SELECT 'UTC' AS timezone, pak.id AS package_id, rel.unique_id AS relay, sns.unique_id AS sensor, pak.rtime AS time,
       sns.units AS sensor_units, typ.name AS sensor_type, dat.data AS sensor_data,
       loc.altitude AS altitude, ST_Y(loc.location::geometry) AS latitude, ST_X(loc.location::geometry) as longitude,
       loc.speed as speed, loc.climb as climb, loc.track as track,
       loc.longitude_error as longitude_error, loc.latitude_error as latitude_error, loc.altitude_error as altitude_error,
       loc.speed_error as speed_error, loc.climb_error as climb_error, loc.track_error as track_error
FROM sensor_data dat
LEFT OUTER JOIN package_location loc on dat.package_id = loc.package_id
LEFT OUTER JOIN data_package pak ON dat.package_id = pak.id
LEFT OUTER JOIN relays rel ON pak.relay_id = rel.id
LEFT OUTER JOIN sensors sns ON dat.sensor_id = sns.id
LEFT OUTER JOIN sensor_types typ ON sns.sensor_type = typ.id
WHERE typ.name='Temperature'
  AND rel.unique_id='OneWireTester'
  AND pak.rtime > '2015-01-01'
  AND pak.rtime < '2016-01-01';

如果我进行解释分析,我会得到以下信息:

    QUERY PLAN                                                                          
-------------------------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=36.23..131.80 rows=1 width=477) (actual time=0.074..3.933 rows=76 loops=1)
   ->  Nested Loop  (cost=36.09..131.60 rows=1 width=349) (actual time=0.068..3.782 rows=76 loops=1)
         ->  Nested Loop  (cost=35.94..130.58 rows=4 width=267) (actual time=0.062..2.472 rows=620 loops=1)
               ->  Hash Join  (cost=35.67..128.73 rows=4 width=247) (actual time=0.053..0.611 rows=620 loops=1)
                     Hash Cond: (dat.sensor_id = sns.id)
                     ->  Seq Scan on sensor_data dat  (cost=0.00..89.46 rows=946 width=21) (actual time=0.007..0.178 rows=1006 loops=1)
                     ->  Hash  (cost=35.64..35.64 rows=2 width=238) (actual time=0.037..0.037 rows=11 loops=1)
                           Buckets: 1024  Batches: 1  Memory Usage: 1kB
                           ->  Hash Join  (cost=20.68..35.64 rows=2 width=238) (actual time=0.019..0.035 rows=11 loops=1)
                                 Hash Cond: (sns.sensor_type = typ.id)
                                 ->  Seq Scan on sensors sns  (cost=0.00..13.60 rows=360 width=188) (actual time=0.002..0.005 rows=31 loops=1)
                                 ->  Hash  (cost=20.62..20.62 rows=4 width=66) (actual time=0.010..0.010 rows=1 loops=1)
                                       Buckets: 1024  Batches: 1  Memory Usage: 1kB
                                       ->  Seq Scan on sensor_types typ  (cost=0.00..20.62 rows=4 width=66) (actual time=0.006..0.008 rows=1 loops=1)
                                             Filter: ((name)::text = 'Temperature'::text)
                                             Rows Removed by Filter: 4
               ->  Index Scan using data_package_pkey on data_package pak  (cost=0.28..0.45 rows=1 width=20) (actual time=0.002..0.002 rows=1 loops=620)
                     Index Cond: (id = dat.package_id)
                     Filter: ((rtime > '2015-01-01 00:00:00'::timestamp without time zone) AND (rtime < '2016-01-01 00:00:00'::timestamp without time zone))
         ->  Index Scan using relays_pkey on relays rel  (cost=0.14..0.24 rows=1 width=94) (actual time=0.002..0.002 rows=0 loops=620)
               Index Cond: (id = pak.relay_id)
               Filter: ((unique_id)::text = 'OneWireTester'::text)
               Rows Removed by Filter: 1
   ->  Index Scan using package_location_pkey on package_location loc  (cost=0.14..0.18 rows=1 width=140) (actual time=0.001..0.001 rows=0 loops=76)
         Index Cond: (dat.package_id = package_id)
 Planning time: 0.959 ms
 Execution time: 4.030 ms
(27 rows)

表架构具有相同的外键和一般结构,因此我希望看到所需的相同索引。但是,我一直在查看有关 pgsql 的检查语句的几个指南,从我收集的内容来看,Seq Scan 语句是缺少索引的指标,这意味着我在sensorssensor_datasensor_type 上缺少索引。

我是否正确解释了这些检查语句的结果?为了优化这两个数据库,我应该寻找什么?

【问题讨论】:

  • and from what I've gathered, the Seq Scan statements are indicators of missing indexes, 错误。
  • ... 你不应该尝试优化 small 查询。结果总时间为 4 毫秒,小于一次磁盘寻道。 一切顺利……
  • 请阅读stackoverflow.com/q/15474812/398670。简短版本:seqscan 在小表上通常更快,或者在您想要获取大部分行的地方。

标签: mysql database postgresql query-optimization explain


【解决方案1】:

在 PostgreSQL(可能还有 MySQL)中,索引并不仅仅因为它们被定义而被使用,而是在加快查询速度时使用。

EXPLAIN ANALYZE 输出中,括号之间是cost 的部分,后面是actual time 的类似部分。查询计划器查看cost,它由配置文件中列出的许多参数定义。这些成本是诸如 IO 和 CPU 时间之类的东西,前者通常比后者具有更高的价值(通常相差 100 倍)。这意味着查询计划器试图最小化需要从磁盘读取的数据量,这些数据按预定大小(通常为 4kB)的页面而不是单个行(这是因为这允许更快的访问由于硬盘驱动器的物理特性)。表本身和索引都存储在磁盘上。如果表格很小,它可以容纳几页,甚至可能只有一页。由于 CPU 时间比 IO 时间便宜,因此顺序扫描几个页面比使用索引读取磁盘页面的额外 IO 快得多。

EXPLAIN ANALYZE 输出中可以看出,您的大多数表格都很小,可以放在几页上。如果您真的想测试索引的功能,您应该在表中加载大约一百万行随机数据,然后进行测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-18
    • 1970-01-01
    • 2012-08-06
    • 2018-12-22
    • 1970-01-01
    • 2016-11-30
    • 2021-11-16
    相关资源
    最近更新 更多