我正在使用扩展名
:ok
我有一个看起来像这样的索引...
CREATE EXTENSION btree_gin;
在我开始弄乱它之前,索引看起来像这样,结果与我要分享的结果相同,因此索引定义中的细微变化似乎没有什么不同>
create index boundaries2 on rets USING GIN(source, isonlastsync, status, (geoinfo::jsonb->'boundaries'), ctcvalidto, searchablePrice, ctcSortOrder);
我给pgsql 11这个查询:
create index boundaries on rets USING GIN((geoinfo::jsonb->'boundaries'), source, status, isonlastsync, ctcvalidto, searchablePrice, ctcSortOrder);
有结果...
explain analyze select id from rets where ((geoinfo::jsonb->'boundaries' ?| array['High School: Torrey Pines']) AND source='SDMLS'
AND searchablePrice>=800000 AND searchablePrice<=1200000 AND YrBlt>=2000 AND EstSF>=2300
AND Beds>=3 AND FB>=2 AND ctcSortOrder>'2019-07-05 16:02:54 UTC' AND Status IN ('ACTIVE','BACK ON MARKET')
AND ctcvalidto='9999-12-31 23:59:59 UTC' AND isonlastsync='true') order by LstDate desc, ctcSortOrder desc LIMIT 3000;
问题
Limit (cost=120.06..120.06 rows=1 width=23) (actual time=472.849..472.850 rows=1 loops=1)
-> Sort (cost=120.06..120.06 rows=1 width=23) (actual time=472.847..472.848 rows=1 loops=1)
Sort Key: lstdate DESC, ctcsortorder DESC
Sort Method: quicksort Memory: 25kB
-> Bitmap Heap Scan on rets (cost=116.00..120.05 rows=1 width=23) (actual time=472.748..472.841 rows=1 loops=1)
Recheck Cond: ((source = 'SDMLS'::text) AND (((geoinfo)::jsonb -> 'boundaries'::text) ?| '{"High School: Torrey Pines"}'::text[]) AND (ctcvalidto = '9999-12-31 23:59:59+00'::timestamp with time zone) AND (searchableprice >= 800000) AND (searchableprice <= 1200000) AND (ctcsortorder > '2019-07-05 16:02:54+00'::timestamp with time zone))
Rows Removed by Index Recheck: 93
Filter: (isonlastsync AND (yrblt >= 2000) AND (estsf >= 2300) AND (beds >= 3) AND (fb >= 2) AND (status = ANY ('{ACTIVE,"BACK ON MARKET"}'::text[])))
Rows Removed by Filter: 10
Heap Blocks: exact=102
-> Bitmap Index Scan on boundaries2 (cost=0.00..116.00 rows=1 width=0) (actual time=471.762..471.762 rows=104 loops=1)
Index Cond: ((source = 'SDMLS'::text) AND (((geoinfo)::jsonb -> 'boundaries'::text) ?| '{"High School: Torrey Pines"}'::text[]) AND (ctcvalidto = '9999-12-31 23:59:59+00'::timestamp with time zone) AND (searchableprice >= 800000) AND (searchableprice <= 1200000) AND (ctcsortorder > '2019-07-05 16:02:54+00'::timestamp with time zone))
Planning Time: 0.333 ms
Execution Time: 474.311 ms
(14 rows)
为什么不使用列status
和isonlastsync
?
答案 0 :(得分:1)
如果它预测过滤出这些列会更快,则可以这样做。如果列的基数非常低并且您将获取所有行的足够大的部分,通常会出现这种情况;对于像import 'package:flutter/material.dart';
import 'package:better_login/auth.dart';
import 'package:better_login/login_screen.dart';
import 'package:better_login/login_screen_presenter.dart';
import 'package:better_login/auth_provider.dart';
class HomeScreen extends StatelessWidget {
HomeScreen({this.authStateListener, this.authStateProvider});
final AuthStateListener authStateListener;
final AuthStateProvider authStateProvider;
void _signOut() async {
try{
authStateProvider.notify(AuthState.LOGGED_OUT);
authStateListener.onAuthStateChanged(AuthState.LOGGED_OUT);
}catch(e){
print(e);
}
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Home"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.exit_to_app), onPressed: (){_signOut();}),
],
),
body: new Center(
child: new Text("Welcome home!"),
),
);
}
}
这样的boolean
来说是正确的,通常对于只有几个不同值的状态列也是如此。
isonlastsync
几乎没有什么可以过滤掉的,这是因为您的表没有容纳很多行,或者它们中的大多数适合您为这两列指定的条件。您可以尝试在该表中生成更多数据,或者选择状态很少的行。
我建议至少在Rows Removed by Filter: 10
值上执行partial indexes(在WHERE条件下),并删除这两列以使该索引更轻便。
答案 1 :(得分:0)
我无法告诉您原因,但是我可以帮助您优化查询。
不要使用多列GIN索引,而只能在jsonb
表达式上使用GIN索引,而在其他列上使用b-tree索引。
列的顺序很重要:首先将等式条件中使用的oned放在首位,最开始的是最有选择性的。接下来,将列具有必选的不等式或IN
条件。对于以下各列,顺序无关紧要,因为它们仅在索引扫描中充当过滤器。
确保索引缓存在RAM中。
我希望您那样做会更快。
答案 2 :(得分:0)
我认为您在问自己一个错误的问题。正如Lukasz已经回答的那样,PostgreSQL可能会发现检查索引中所有列的效率低下。这里的问题是您的索引在磁盘上太大。
可能是通过尝试提高此SQL的速度,您向索引中添加了尽可能多的列,但它回火了。
诀窍是实现PostgreSQL必须读取多少数据才能找到您的记录。如果索引包含太多数据,则必须读取很多数据。另外,请注意,低基数列不适用于BTree和常见索引类型。通常,您要避免为它们建立索引。
要使索引尽可能小并且快速进行查找,您必须找到基数更大或更佳的列,该列将为查询返回更少的行。我的猜测是“ ctcSortOrder”。这将是索引的第一列。
现在,在按第一列进行过滤之后,现在看哪一列具有最大的基数或将过滤掉大多数行。我对您的数据一无所知,但“来源”看起来很合适。
尝试避免jsonb搜索,除非它们是基数的主要来源,并将索引保留为Btree。 BTree快了好几倍。
就像Lukasz建议的那样,看一下部分索引。例如,添加“ WHERE Status IN('ACTIVE','BACK ON MARKET')AND isonlastsync ='true'”,因为这两个对于您的所有搜索可能是通用的。
底线比起对所有列进行索引,索引更简单,更小。列的顺序非常重要。除非有充分的理由,否则请坚持使用BTree(非btree兼容类型的基数很多)。
如果表很大(> 1000万行),请考虑对表进行分区,例如通过ctcSortOrder。但是我不认为这是您的情况。