减少 SQL 中的提取时间(索引已经完成)
Reduce fetching time in SQL (indexing already done)
我有一个有几百万行的 table,其中经常插入行甚至更频繁地提取行。
插入行的时间并不重要,但获取时间是因为它正在为网站服务。因此,我.
查询非常简单,不包含 JOIN
s。
问题发生在 SELECT
查询中。相同的 SELECT
查询将每隔几秒 运行 在用户执行搜索后检查新的或更新的行。但是,SELECT
第一次查询 运行s 50 秒,之后这些相同的查询不到 1 秒就不足为奇了。
这让我认为问题不在于 SELECT
语句本身,而在于其他东西。
table是:
CREATE TABLE all_legs (
carrier TEXT,
dep_hub TEXT,
arr_hub TEXT,
dep_dt TIMESTAMP WITH TIME ZONE,
arr_dt TIMESTAMP WITH TIME ZONE,
price_ct INTEGER,
... 5 more cols ...,
PRIMARY KEY (carrier, dep_hub, arr_hub, dep_dt, arr_dt, ...3 other cols...)
)
INDEX 是:
CREATE INDEX IF NOT EXISTS fetch_index ON all_legs(dep_dt, LEFT(dep_hub::text, 6), LEFT(arr_hub::text, 6));
SELECT查询:
SELECT * FROM all_legs
WHERE dep_dt >= %s
AND dep_dt < %s
AND (LEFT(dep_hub::text, 6) = %s AND LEFT(arr_hub::text, 6) = %s)
这种情况并不总是发生,因此很难复制。这里有一个来自我本地数据库的 EXPLAIN
语句,它的数据比 Heroku 上的少,而且 运行 实际上相当快:
Index Scan using tz_idx on all_legs (cost=0.41..111184.33 rows=1 width=695) (actual time=128.100..136.690 rows=20 loops=1)
Index Cond: (("left"(dep_hub, 6) = 'ES-PMI'::text) AND ("left"(arr_hub, 6) = 'ES-MAD'::text))
Filter: ((dep_dt)::date = '2018-01-19'::date)
Rows Removed by Filter: 271
Planning time: 3.798 ms
Execution time: 138.525 ms
为什么第一次慢很多,如何减少第一次查询的 运行ning 时间?
查询和索引中的 LEFT
函数可能增加了不必要的复杂性;您可以将通配符与 LIKE
语句匹配以获得相同的结果。
SELECT * FROM all_legs
WHERE dep_dt >= '2018-01-19'
AND dep_dt < '2018-01-20'
AND dep_hub LIKE 'ES-PMI%'
AND arr_hub LIKE 'ES-MAD%'
在最后两个参数的末尾添加一个 %
。
有了这个,你应该也可以通过删除涉及LEFT
函数的索引来加快查询速度,并且只需正常索引列即可。
- 挤出单列的示例代码(
dep_hub
)
- 如果您的 {dep_hub,arr_hub} 都引用同一个域,则您必须稍作更改
- 您还必须重新定义主键,
- 和[也许]在挤出机上添加一些功能指标table
-- [empty] table to contain the "squeezed out" domain
CREATE TABLE dep_hub
( id SERIAL NOT NULL PRIMARY KEY
, dep_hub varchar
, UNIQUE (dep_hub)
);
-- This is done in the chained insert/update
-- INSERT INTO dep_hub(dep_hub)
-- SELECT DISTINCT dep_hub
-- FROM all_legs ;
-- an index may speedup the final update
-- (the index will be dropped automatically
-- once the column is dropped)
CREATE INDEX ON all_legs (dep_hub);
-- The original table needs a "link" to the new table
ALTER TABLE all_legs
ADD column dep_hub_id INTEGER -- NOT NULL
REFERENCES dep_hub(id)
;
-- FK constraints are helped a lot by a supportive index.
CREATE INDEX all_legs_dep_hub_fk ON all_legs (dep_hub_id);
-- Chained query to:
-- * populate the domain table
-- * initialize the FK column in the original table
WITH src AS (
INSERT INTO dep_hub(dep_hub)
SELECT DISTINCT a.dep_hub
FROM all_legs a
RETURNING *
)
UPDATE all_legs dst
SET dep_hub_id = src.id
FROM src
WHERE src.dep_hub = dst.dep_hub
;
-- Now that we have the FK pointing to the new table,
-- we can drop the redundant column.
ALTER TABLE all_legs DROP COLUMN dep_hub;
我有一个有几百万行的 table,其中经常插入行甚至更频繁地提取行。
插入行的时间并不重要,但获取时间是因为它正在为网站服务。因此,我
查询非常简单,不包含 JOIN
s。
问题发生在 SELECT
查询中。相同的 SELECT
查询将每隔几秒 运行 在用户执行搜索后检查新的或更新的行。但是,SELECT
第一次查询 运行s 50 秒,之后这些相同的查询不到 1 秒就不足为奇了。
这让我认为问题不在于 SELECT
语句本身,而在于其他东西。
table是:
CREATE TABLE all_legs (
carrier TEXT,
dep_hub TEXT,
arr_hub TEXT,
dep_dt TIMESTAMP WITH TIME ZONE,
arr_dt TIMESTAMP WITH TIME ZONE,
price_ct INTEGER,
... 5 more cols ...,
PRIMARY KEY (carrier, dep_hub, arr_hub, dep_dt, arr_dt, ...3 other cols...)
)
INDEX 是:
CREATE INDEX IF NOT EXISTS fetch_index ON all_legs(dep_dt, LEFT(dep_hub::text, 6), LEFT(arr_hub::text, 6));
SELECT查询:
SELECT * FROM all_legs
WHERE dep_dt >= %s
AND dep_dt < %s
AND (LEFT(dep_hub::text, 6) = %s AND LEFT(arr_hub::text, 6) = %s)
这种情况并不总是发生,因此很难复制。这里有一个来自我本地数据库的 EXPLAIN
语句,它的数据比 Heroku 上的少,而且 运行 实际上相当快:
Index Scan using tz_idx on all_legs (cost=0.41..111184.33 rows=1 width=695) (actual time=128.100..136.690 rows=20 loops=1)
Index Cond: (("left"(dep_hub, 6) = 'ES-PMI'::text) AND ("left"(arr_hub, 6) = 'ES-MAD'::text))
Filter: ((dep_dt)::date = '2018-01-19'::date)
Rows Removed by Filter: 271
Planning time: 3.798 ms
Execution time: 138.525 ms
为什么第一次慢很多,如何减少第一次查询的 运行ning 时间?
查询和索引中的 LEFT
函数可能增加了不必要的复杂性;您可以将通配符与 LIKE
语句匹配以获得相同的结果。
SELECT * FROM all_legs
WHERE dep_dt >= '2018-01-19'
AND dep_dt < '2018-01-20'
AND dep_hub LIKE 'ES-PMI%'
AND arr_hub LIKE 'ES-MAD%'
在最后两个参数的末尾添加一个 %
。
有了这个,你应该也可以通过删除涉及LEFT
函数的索引来加快查询速度,并且只需正常索引列即可。
- 挤出单列的示例代码(
dep_hub
) - 如果您的 {dep_hub,arr_hub} 都引用同一个域,则您必须稍作更改
- 您还必须重新定义主键,
- 和[也许]在挤出机上添加一些功能指标table
-- [empty] table to contain the "squeezed out" domain
CREATE TABLE dep_hub
( id SERIAL NOT NULL PRIMARY KEY
, dep_hub varchar
, UNIQUE (dep_hub)
);
-- This is done in the chained insert/update
-- INSERT INTO dep_hub(dep_hub)
-- SELECT DISTINCT dep_hub
-- FROM all_legs ;
-- an index may speedup the final update
-- (the index will be dropped automatically
-- once the column is dropped)
CREATE INDEX ON all_legs (dep_hub);
-- The original table needs a "link" to the new table
ALTER TABLE all_legs
ADD column dep_hub_id INTEGER -- NOT NULL
REFERENCES dep_hub(id)
;
-- FK constraints are helped a lot by a supportive index.
CREATE INDEX all_legs_dep_hub_fk ON all_legs (dep_hub_id);
-- Chained query to:
-- * populate the domain table
-- * initialize the FK column in the original table
WITH src AS (
INSERT INTO dep_hub(dep_hub)
SELECT DISTINCT a.dep_hub
FROM all_legs a
RETURNING *
)
UPDATE all_legs dst
SET dep_hub_id = src.id
FROM src
WHERE src.dep_hub = dst.dep_hub
;
-- Now that we have the FK pointing to the new table,
-- we can drop the redundant column.
ALTER TABLE all_legs DROP COLUMN dep_hub;