Skip to content

Commit 45607bc

Browse files
committed
opt: fix row count estimates for inverted filters
Previously, inverted filter row counts were set to their input's row count. This was often an over-estimation because inverted filters de-duplicate inverted index tuples from their input by primary key, and inverted indexes can contain multiple tuples for each logical row. This commit fixes this over-estimation by setting the row count of inverted filters to the distinct count of the primary key columns from the input. Fixes #120055 Release note (performance improvement): A bug has been fixed that caused the optimizer to over-estimate the cost of inverted index scans in some cases. Now, plans with inverted index scans should be selected in more cases where they are optimal.
1 parent bed2c02 commit 45607bc

File tree

7 files changed

+74
-68
lines changed

7 files changed

+74
-68
lines changed

pkg/sql/opt/memo/statistics_builder.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1252,9 +1252,15 @@ func (sb *statisticsBuilder) buildInvertedFilter(
12521252

12531253
// Calculate selectivity and row count
12541254
// -----------------------------------
1255+
// Inverted filters de-duplicate inverted index tuples by the primary key,
1256+
// so they produce no more rows than the distinct count of the PK columns.
1257+
tabID := sb.md.ColumnMeta(invFilter.InvertedColumn).Table
1258+
pkCols := sb.md.TableMeta(tabID).IndexKeyColumns(0)
1259+
keyStats := sb.colStatFromChild(pkCols, invFilter, 0 /* childIdx */)
1260+
s.RowCount = keyStats.DistinctCount
12551261
inputStats := invFilter.Input.Relational().Statistics()
1256-
s.RowCount = inputStats.RowCount
12571262
s.VirtualCols.UnionWith(inputStats.VirtualCols)
1263+
12581264
corr := sb.correlationFromMultiColDistinctCounts(constrainedCols, invFilter, s)
12591265
s.ApplySelectivity(sb.selectivityFromConstrainedCols(constrainedCols, histCols, invFilter, s, corr))
12601266
s.ApplySelectivity(sb.selectivityFromNullsRemoved(invFilter, relProps.NotNullCols, constrainedCols))

pkg/sql/opt/memo/testdata/stats/inverted-array

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ index-join t
6969
├── inverted expression: /5
7070
│ ├── tight: true, unique: false
7171
│ └── union spans: [, NULL/NULL)
72-
├── stats: [rows=1020]
72+
├── stats: [rows=1000]
7373
├── key: (1)
7474
└── scan t@a_idx,inverted
7575
├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null)
@@ -115,7 +115,7 @@ index-join t
115115
├── inverted expression: /5
116116
│ ├── tight: true, unique: false
117117
│ └── union spans: [2, 4)
118-
├── stats: [rows=20]
118+
├── stats: [rows=19.6078]
119119
├── key: (1)
120120
└── scan t@a_idx,inverted
121121
├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null)
@@ -166,7 +166,7 @@ select
166166
├── fd: (1)-->(2)
167167
├── index-join t
168168
│ ├── columns: k:1(int!null) a:2(int[])
169-
│ ├── stats: [rows=20]
169+
│ ├── stats: [rows=19.6078]
170170
│ ├── key: (1)
171171
│ ├── fd: (1)-->(2)
172172
│ └── inverted-filter
@@ -176,7 +176,7 @@ select
176176
│ │ └── union spans
177177
│ │ ├── [[], []]
178178
│ │ └── [2, 2]
179-
│ ├── stats: [rows=20]
179+
│ ├── stats: [rows=19.6078]
180180
│ ├── key: (1)
181181
│ └── scan t@a_idx,inverted
182182
│ ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null)
@@ -203,7 +203,7 @@ select
203203
├── fd: (1)-->(2)
204204
├── index-join t
205205
│ ├── columns: k:1(int!null) a:2(int[])
206-
│ ├── stats: [rows=30]
206+
│ ├── stats: [rows=29.4118]
207207
│ ├── key: (1)
208208
│ ├── fd: (1)-->(2)
209209
│ └── inverted-filter
@@ -213,7 +213,7 @@ select
213213
│ │ └── union spans
214214
│ │ ├── [[], []]
215215
│ │ └── [2, 4)
216-
│ ├── stats: [rows=30]
216+
│ ├── stats: [rows=29.4118]
217217
│ ├── key: (1)
218218
│ └── scan t@a_idx,inverted
219219
│ ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null)
@@ -241,7 +241,7 @@ select
241241
├── fd: (1)-->(2)
242242
├── index-join t
243243
│ ├── columns: k:1(int!null) a:2(int[])
244-
│ ├── stats: [rows=30]
244+
│ ├── stats: [rows=29.4118]
245245
│ ├── key: (1)
246246
│ ├── fd: (1)-->(2)
247247
│ └── inverted-filter
@@ -256,7 +256,7 @@ select
256256
│ │ └── span expression
257257
│ │ ├── tight: false, unique: false
258258
│ │ └── union spans: [3, 3]
259-
│ ├── stats: [rows=30]
259+
│ ├── stats: [rows=29.4118]
260260
│ ├── key: (1)
261261
│ └── scan t@a_idx,inverted
262262
│ ├── columns: k:1(int!null) a_inverted_key:5(encodedkey!null)

pkg/sql/opt/memo/testdata/stats/inverted-geo

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ memo (optimized, ~15KB, required=[presentation: i:1])
106106
│ │ ├── best: (select G6="[ordering: +1] [limit hint: 9.00]" G7)
107107
│ │ └── cost: 2656.90
108108
│ └── []
109-
│ ├── best: (select G6 G7)
110-
│ └── cost: 4148.55
109+
│ ├── best: (select G8 G7)
110+
│ └── cost: 3671.93
111111
├── G5: (const 1)
112112
├── G6: (scan t,cols=(1,2))
113113
│ ├── [ordering: +1] [limit hint: 9.00]
@@ -118,12 +118,12 @@ memo (optimized, ~15KB, required=[presentation: i:1])
118118
│ └── cost: 2128.52
119119
├── G7: (filters G9)
120120
├── G8: (index-join G10 t,cols=(1,2))
121-
│ ├── [ordering: +1] [limit hint: 5.34]
121+
│ ├── [ordering: +1] [limit hint: 1.53]
122122
│ │ ├── best: (sort G8)
123-
│ │ └── cost: 8759.99
123+
│ │ └── cost: 3399.85
124124
│ └── []
125125
│ ├── best: (index-join G10 t,cols=(1,2))
126-
│ └── cost: 8469.67
126+
│ └── cost: 3329.20
127127
├── G9: (function G11 st_intersects)
128128
├── G10: (inverted-filter G12 g_inverted_key)
129129
│ └── []
@@ -165,20 +165,20 @@ project
165165
│ ├── limit hint: 1.00
166166
│ ├── sort
167167
│ │ ├── columns: i:1(int) g:2(geometry)
168-
│ │ ├── stats: [rows=1.4e-06]
168+
│ │ ├── stats: [rows=4e-07]
169169
│ │ ├── ordering: +1
170170
│ │ ├── limit hint: 0.00
171171
│ │ └── index-join t
172172
│ │ ├── columns: i:1(int) g:2(geometry)
173-
│ │ ├── stats: [rows=1.4e-06]
173+
│ │ ├── stats: [rows=4e-07]
174174
│ │ └── inverted-filter
175175
│ │ ├── columns: rowid:3(int!null)
176176
│ │ ├── inverted expression: /6
177177
│ │ │ ├── tight: false, unique: false
178178
│ │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"]
179179
│ │ ├── pre-filterer expression
180180
│ │ │ └── st_intersects('010200000002000000000000000000594000000000000059400000000000C062400000000000C06240', g:2) [type=bool]
181-
│ │ ├── stats: [rows=1.4e-06]
181+
│ │ ├── stats: [rows=4e-07]
182182
│ │ ├── key: (3)
183183
│ │ └── scan t@t_g_idx,inverted
184184
│ │ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null)
@@ -385,20 +385,20 @@ project
385385
│ ├── limit hint: 1.00
386386
│ ├── sort
387387
│ │ ├── columns: i:1(int) g:2(geometry)
388-
│ │ ├── stats: [rows=1.42e-06]
388+
│ │ ├── stats: [rows=4e-07]
389389
│ │ ├── ordering: +1
390390
│ │ ├── limit hint: 0.00
391391
│ │ └── index-join t
392392
│ │ ├── columns: i:1(int) g:2(geometry)
393-
│ │ ├── stats: [rows=1.42e-06]
393+
│ │ ├── stats: [rows=4e-07]
394394
│ │ └── inverted-filter
395395
│ │ ├── columns: rowid:3(int!null)
396396
│ │ ├── inverted expression: /6
397397
│ │ │ ├── tight: false, unique: false
398398
│ │ │ └── union spans: ["B\xfd\xff\xff\xff\xff\xff\xff\xff\xff", "B\xfd\xff\xff\xff\xff\xff\xff\xff\xff"]
399399
│ │ ├── pre-filterer expression
400400
│ │ │ └── st_intersects('010200000002000000000000000000594000000000000059400000000000C062400000000000C06240', g:2) [type=bool]
401-
│ │ ├── stats: [rows=1.42e-06]
401+
│ │ ├── stats: [rows=4e-07]
402402
│ │ ├── key: (3)
403403
│ │ └── scan t@t_g_idx,inverted
404404
│ │ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null)
@@ -466,7 +466,7 @@ select
466466
├── stats: [rows=11.1111, distinct(1)=10.5717, null(1)=10, distinct(2)=4, null(2)=0]
467467
├── index-join t
468468
│ ├── columns: i:1(int) g:2(geometry)
469-
│ ├── stats: [rows=100]
469+
│ ├── stats: [rows=76.9231]
470470
│ └── inverted-filter
471471
│ ├── columns: rowid:3(int!null)
472472
│ ├── inverted expression: /6
@@ -477,7 +477,7 @@ select
477477
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
478478
│ ├── pre-filterer expression
479479
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
480-
│ ├── stats: [rows=100]
480+
│ ├── stats: [rows=76.9231]
481481
│ ├── key: (3)
482482
│ └── scan t@t_g_idx,inverted
483483
│ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null)
@@ -506,7 +506,7 @@ project
506506
├── stats: [rows=11.1111, distinct(2)=4, null(2)=0]
507507
├── index-join t
508508
│ ├── columns: i:1(int) g:2(geometry)
509-
│ ├── stats: [rows=100]
509+
│ ├── stats: [rows=76.9231]
510510
│ └── inverted-filter
511511
│ ├── columns: rowid:3(int!null)
512512
│ ├── inverted expression: /6
@@ -517,7 +517,7 @@ project
517517
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
518518
│ ├── pre-filterer expression
519519
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
520-
│ ├── stats: [rows=100]
520+
│ ├── stats: [rows=76.9231]
521521
│ ├── key: (3)
522522
│ └── scan t@t_g_idx,inverted
523523
│ ├── columns: rowid:3(int!null) g_inverted_key:6(encodedkey!null)

pkg/sql/opt/memo/testdata/stats/inverted-geo-multi-column

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ project
8383
├── fd: ()-->(3), (1)-->(2)
8484
├── index-join t
8585
│ ├── columns: k:1(int!null) g:2(geometry) s:3(string)
86-
│ ├── stats: [rows=59.3784]
86+
│ ├── stats: [rows=16.9653]
8787
│ ├── key: (1)
8888
│ ├── fd: (1)-->(2,3)
8989
│ └── inverted-filter
@@ -96,7 +96,7 @@ project
9696
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
9797
│ ├── pre-filterer expression
9898
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
99-
│ ├── stats: [rows=59.3784]
99+
│ ├── stats: [rows=16.9653]
100100
│ ├── key: (1)
101101
│ └── scan t@m,inverted
102102
│ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null)
@@ -133,7 +133,7 @@ project
133133
├── fd: ()-->(3), (1)-->(2)
134134
├── index-join t
135135
│ ├── columns: k:1(int!null) g:2(geometry) s:3(string)
136-
│ ├── stats: [rows=59.3784]
136+
│ ├── stats: [rows=16.9653]
137137
│ ├── key: (1)
138138
│ ├── fd: (1)-->(2,3)
139139
│ └── inverted-filter
@@ -146,7 +146,7 @@ project
146146
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
147147
│ ├── pre-filterer expression
148148
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
149-
│ ├── stats: [rows=59.3784]
149+
│ ├── stats: [rows=16.9653]
150150
│ ├── key: (1)
151151
│ └── scan t@p,inverted,partial
152152
│ ├── columns: k:1(int!null) g_inverted_key:8(encodedkey!null)
@@ -195,7 +195,7 @@ project
195195
├── fd: (1)-->(2,3)
196196
├── index-join t
197197
│ ├── columns: k:1(int!null) g:2(geometry) s:3(string)
198-
│ ├── stats: [rows=118.757]
198+
│ ├── stats: [rows=33.9305]
199199
│ ├── key: (1)
200200
│ ├── fd: (1)-->(2,3)
201201
│ └── inverted-filter
@@ -208,7 +208,7 @@ project
208208
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
209209
│ ├── pre-filterer expression
210210
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
211-
│ ├── stats: [rows=118.757]
211+
│ ├── stats: [rows=33.9305]
212212
│ ├── key: (1)
213213
│ └── scan t@m,inverted
214214
│ ├── columns: k:1(int!null) g_inverted_key:7(encodedkey!null)
@@ -248,7 +248,7 @@ project
248248
├── fd: (1)-->(2,3)
249249
├── index-join t
250250
│ ├── columns: k:1(int!null) g:2(geometry) s:3(string)
251-
│ ├── stats: [rows=121.57]
251+
│ ├── stats: [rows=34.7341]
252252
│ ├── key: (1)
253253
│ ├── fd: (1)-->(2,3)
254254
│ └── inverted-filter
@@ -261,7 +261,7 @@ project
261261
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
262262
│ ├── pre-filterer expression
263263
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
264-
│ ├── stats: [rows=121.57]
264+
│ ├── stats: [rows=34.7341]
265265
│ ├── key: (1)
266266
│ └── scan t@p,inverted,partial
267267
│ ├── columns: k:1(int!null) g_inverted_key:9(encodedkey!null)
@@ -310,7 +310,7 @@ project
310310
├── fd: ()-->(4), (1)-->(2,3)
311311
├── index-join t
312312
│ ├── columns: k:1(int!null) g:2(geometry) s:3(string) i:4(int)
313-
│ ├── stats: [rows=2.50393]
313+
│ ├── stats: [rows=0.715409]
314314
│ ├── key: (1)
315315
│ ├── fd: (1)-->(2-4)
316316
│ └── inverted-filter
@@ -323,7 +323,7 @@ project
323323
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
324324
│ ├── pre-filterer expression
325325
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
326-
│ ├── stats: [rows=2.50393]
326+
│ ├── stats: [rows=0.715409]
327327
│ ├── key: (1)
328328
│ └── scan t@mp,inverted,partial
329329
│ ├── columns: k:1(int!null) g_inverted_key:10(encodedkey!null)
@@ -365,7 +365,7 @@ project
365365
├── fd: (1)-->(2-4)
366366
├── index-join t
367367
│ ├── columns: k:1(int!null) g:2(geometry) s:3(string) i:4(int)
368-
│ ├── stats: [rows=10.1111]
368+
│ ├── stats: [rows=2.88887]
369369
│ ├── key: (1)
370370
│ ├── fd: (1)-->(2-4)
371371
│ └── inverted-filter
@@ -378,7 +378,7 @@ project
378378
│ │ └── ["B\xfd\x14\x00\x00\x00\x00\x00\x00\x00", "B\xfd\x14\x00\x00\x00\x00\x00\x00\x00"]
379379
│ ├── pre-filterer expression
380380
│ │ └── st_intersects('010200000002000000000000000000E03F000000000000E03F666666666666E63F666666666666E63F', g:2) [type=bool]
381-
│ ├── stats: [rows=10.1111]
381+
│ ├── stats: [rows=2.88887]
382382
│ ├── key: (1)
383383
│ └── scan t@mp,inverted,partial
384384
│ ├── columns: k:1(int!null) g_inverted_key:10(encodedkey!null)

0 commit comments

Comments
 (0)