尽管有索引,并且id
作为主键,下面的查询大约需要15秒才能返回数据。
select id from my_table order by insert_date offset 0 limit 1
解释分析如下
"Limit (cost=1766417.72..1766417.72 rows=1 width=12) (actual time=32479.440..32479.441 rows=1 loops=1)"
" -> Sort (cost=1766417.72..1797117.34 rows=12279848 width=12) (actual time=32479.437..32479.437 rows=1 loops=1)"
" Sort Key: insert_date"
" Sort Method: top-N heapsort Memory: 25kB"
" -> Seq Scan on my_table (cost=0.00..1705018.48 rows=12279848 width=12) (actual time=0.006..21338.401 rows=12108916 loops=1)"
"Total runtime: 32479.476 ms"
我的表几乎没有其他专栏。但insert_date
的类型是
insert_date timestamp without time zone NOT NULL DEFAULT now(),
我在该特定日期列上有一个索引
CREATE INDEX my_table_insert_date_indx
ON my_table
USING btree
(insert_date)
TABLESPACE somexyz_idx_ts;
来自postgresql.conf
档案的价值很少:
shared_buffers = more than 1GB ## just for an example
temp_buffers = more than 1GB
work_mem = more than 1GB
maintenance_work_mem = more than 1GB
dynamic_shared_memory_type = posix
default_statistics_target = 10000
autovacuum = on
random_page_cost = 2.0
cpu_index_tuple_cost = 0.0005
我现在正在使用postgres 9.3。
UPDATE ::
我刚刚运行了以下查询:
select insert_date, count(*) from my_table group by insert_date
结果中的前几位是:
"2015-04-02 00:00:00";3718104
"2015-04-03 00:00:00";6410253
"2015-04-04 00:00:00";538247
"2015-04-05 00:00:00";1228877
"2015-04-06 00:00:00";131248
我在那张桌子上有大约1200万条记录。而上述数字几乎接近总数。
不确定,但是在具有大量重复值的列上创建索引可能会出现问题吗?如果是真的,那我们有办法吗?
答案 0 :(得分:2)
使用PostgreSQL 9.3和9.4,我的机器上的查询运行速度提高了大约160000倍。我的机器没什么特别的。
-- From PostgreSQL 9.4; 9.3 is similar.
show shared_buffers; -- 128MB
show temp_buffers; -- 8MB
show work_mem; -- 4MB
show maintenance_work_mem; -- 64MB
show dynamic_shared_memory_type; -- posix
show default_statistics_target; -- 100
show autovacuum; -- on
show random_page_cost; -- 4
show cpu_index_tuple_cost; -- 0.005
<强>制备强>
让我们建立一个表格。 (你应该在你的问题中这样做。)
create table my_table (
id serial primary key,
insert_date timestamp not null
);
-- Round numbers of rows.
insert into my_table(insert_date)
select timestamp '2015-04-02 00:00:00'
from generate_series(1, 3000000) n;
insert into my_table(insert_date)
select timestamp '2015-04-03 00:00:00'
from generate_series(1, 6000000) n;
insert into my_table(insert_date)
select timestamp '2015-04-04 00:00:00'
from generate_series(1, 500000) n;
insert into my_table(insert_date)
select timestamp '2015-04-05 00:00:00'
from generate_series(1, 1200000) n;
insert into my_table(insert_date)
select timestamp '2015-04-06 00:00:00'
from generate_series(1, 131000) n;
创建索引并更新统计信息。
create index on my_table (insert_date);
analyze my_table;
PostgreSQL 9.4
现在,我们从您的第一个查询中获得了哪种执行计划?
explain analyze
select id from my_table order by insert_date offset 0 limit 1;
"Limit (cost=0.43..0.48 rows=1 width=12) (actual time=0.014..0.014 rows=1 loops=1)" " -> Index Scan using my_table_insert_date_idx on my_table (cost=0.43..540656.27 rows=11200977 width=12) (actual time=0.012..0.012 rows=1 loops=1)" "Planning time: 0.195 ms" "Execution time: 0.032 ms"
PostgreSQL 9.3
explain analyze
select id from my_table order by insert_date offset 0 limit 1;
"Limit (cost=0.43..0.47 rows=1 width=12) (actual time=0.058..0.059 rows=1 loops=1)" " -> Index Scan using my_table_insert_date_idx on my_table (cost=0.43..339814.36 rows=10830995 width=12) (actual time=0.057..0.057 rows=1 loops=1)" "Total runtime: 0.098 ms"
您的查询
select id from my_table order by insert_date offset 0 limit 1;
是不确定的。根据ORDER BY子句,有300万行具有最低的insert_date(首先出现的日期)。你挑选了300万中的一个。 PostgreSQL不保证每次都会获得相同的ID。
如果您不关心它返回的300万个ID中的哪一个,您可以用不同的方式表达查询。但我不认为以不同的方式表达会给你160k倍的加速。
您可以针对特定查询更改您包含的某些设置。所以你可以这样做。
-- Don't commit or rollback . . .
begin transaction;
set local work_mem = '8 MB';
explain analyze
select id from my_table order by insert_date offset 0 limit 1;
-- Displays the result.
手动提交或回滚。
commit;
您的work_mem设置将返回到服务器启动时设置的值。
show work_mem; -- 4MB