Hagamos algunas suposiciones:
Tengo una mesa que se ve así:
a | b
---+---
a | -1
a | 17
...
a | 21
c | 17
c | -3
...
c | 22
Datos sobre mi conjunto:
El tamaño de toda la tabla es de ~ 10 10 filas.
Tengo ~ 100k filas con valor
a
en la columnaa
, similar para otros valores (por ejemploc
).Eso significa ~ 100k valores distintos en la columna 'a'.
La mayoría de mis consultas leerán todos o la mayoría de los valores para un valor dado en un, por ejemplo
select sum(b) from t where a = 'c'
.La tabla está escrita de tal manera que los valores consecutivos están físicamente cerca (o bien está escrita en orden, o suponemos que
CLUSTER
se usó en esa tabla y columnaa
).La tabla rara vez se actualiza, solo nos preocupa la velocidad de lectura.
La tabla es relativamente estrecha (digamos ~ 25 bytes por tupla, + 23 bytes de sobrecarga).
Ahora la pregunta es, ¿qué tipo de índice debo usar? Mi entendimiento es:
BTree Mi problema aquí es que el índice BTree será enorme ya que, hasta donde yo sé, almacenará valores duplicados (tiene que hacerlo, ya que no puede suponer que la tabla está físicamente ordenada). Si BTree es enorme, termino teniendo que leer tanto el índice como las partes de la tabla a las que apunta el índice. (Podemos usar
fillfactor = 100
para disminuir un poco el tamaño del índice).BRIN Entiendo que puedo tener un pequeño índice aquí a expensas de leer páginas inútiles. Usar un pequeño
pages_per_range
significa que el índice es más grande (lo cual es un problema con BRIN ya que necesito leer todo el índice), y tener un granpages_per_range
medio significa que leeré muchas páginas inútiles. ¿Existe una fórmula mágica para encontrar un buen valorpages_per_range
que tenga en cuenta esas compensaciones?GIN / GiST No estoy seguro de que sean relevantes aquí, ya que se usan principalmente para la búsqueda de texto completo, pero también escuché que son buenos para manejar claves duplicadas. ¿Ayudaría
GIN
o unGiST
índice aquí?
Otra pregunta es, ¿Postgres utilizará el hecho de que se edita una tabla CLUSTER
(suponiendo que no haya actualizaciones) en el planificador de consultas (por ejemplo, mediante la búsqueda binaria de las páginas de inicio / fin relevantes)? Algo relacionado, ¿puedo almacenar todas mis columnas en un BTree y soltar la tabla por completo (o lograr algo equivalente, creo que esos son índices agrupados en el servidor SQL)? ¿Hay algún índice híbrido BTree / BRIN que ayudaría aquí?
Prefiero evitar el uso de matrices para almacenar mis valores, ya que mi consulta terminará siendo menos legible de esa manera (entiendo que esto reduciría el costo de los 23 bytes por sobrecarga de tupla al reducir el número de tuplas).
Respuestas:
No necesariamente: tener un índice btree que 'cubra' será el tiempo de lectura más rápido, y si eso es todo lo que desea (es decir, si puede permitirse el almacenamiento adicional), entonces es su mejor opción.
Si no puede permitirse la sobrecarga de almacenamiento de un índice btree de cobertura, BRIN es ideal para usted, porque ya tiene una agrupación (esto es crucial para que BRIN sea útil). Los índices BRIN son pequeños , por lo que es probable que todas las páginas estén en la memoria si elige un valor adecuado de
pages_per_range
.No hay fórmula mágica, pero comienza con
pages_per_range
algo menos que el tamaño promedio (en páginas) ocupado por ela
valor promedio . Probablemente esté intentando minimizar: (número de páginas BRIN escaneadas) + (número de páginas de montón escaneadas) para una consulta típica. BusqueHeap Blocks: lossy=n
en el plan de ejecuciónpages_per_range=1
y compare con otros valores parapages_per_range
, es decir, vea cuántos bloques de montón innecesarios se están escaneando.Puede valer la pena considerar GIN, pero probablemente no GiST; sin embargo, si el agrupamiento natural es realmente bueno, entonces BRIN probablemente será una mejor apuesta.
Aquí hay una comparación de muestra entre los diferentes tipos de índice para datos ficticios un poco como el suyo:
tabla e índices:
tamaños de relación:
cubriendo btree:
btree simple:
BRIN páginas_por_rango = 4:
BRIN páginas_por_rango = 2:
GINEBRA:
dbfiddle aquí
fuente
Bitmap Index Scan
como significa 'leer todo el índice brin' pero tal vez esa sea la lectura incorrecta. OracleCOMPRESS
parece algo útil aquí, ya que reduciría el tamaño del árbol B, ¡pero estoy atrapado con pg!Además de btree y brin, que parecen las opciones más sensatas, algunas otras opciones exóticas que vale la pena investigar, pueden ser útiles o no en su caso:
INCLUDE
índices . Con suerte, estarán en la próxima versión principal (10) de Postgres, en algún lugar alrededor de septiembre de 2017. Un índice en(a) INCLUDE (b)
tiene la misma estructura que un índice en(a)
pero incluye en las páginas de la hoja, todos los valores deb
(pero sin ordenar). Lo que significa que no puede usarlo, por ejemplo, paraSELECT * FROM t WHERE a = 'a' AND b = 2 ;
. Es posible que se use el índice, pero si bien un(a,b)
índice encontrará las filas coincidentes con una sola búsqueda, el índice de inclusión tendrá que pasar por los valores (posiblemente 100K como en su caso) que coincidena = 'a'
y verificarb
valores.Por otro lado, el índice es un poco menos ancho que el
(a,b)
índice y no necesita el ordenb
para calcular su consultaSUM(b)
. También podrías tener por ejemplo(a) INCLUDE (b,c,d)
que se puede usar para consultas similares a las suyas que se agregan en las 3 columnas.Índices filtrados (parciales) . Una sugerencia que puede sonar un poco loca * al principio:
Un índice para cada
a
valor. En su caso, alrededor de 100K índices. Si bien esto suena mucho, considere que cada índice será muy pequeño, tanto en tamaño (número de filas) como en ancho (ya que solo almacenaráb
valores). Sin embargo, en todos los demás aspectos, (los índices de 100K juntos) actuarán como un índice de árbol b(a,b)
mientras se utiliza el espacio de un(b)
índice.La desventaja es que deberá crearlos y mantenerlos usted mismo, cada vez que
a
se agregue un nuevo valor a la tabla. Dado que su tabla es bastante estable, sin muchas (o ninguna) inserción / actualización, eso no parece ser un problema.Tablas de resumen. Dado que la tabla es bastante estable, siempre puede crear y completar una tabla de resumen con los agregados más comunes que necesitará (
sum(b), sum(c), sum(d), avg(b), count(distinct b)
, etc.). Será pequeño (solo 100K filas) y solo tendrá que rellenarse una vez y actualizarse solo cuando las filas se inserten / actualicen / eliminen en la tabla principal.*: idea copiada de esta compañía que ejecuta 10 millones de índices en su sistema de producción: The Heap: Ejecución de 10 millones de índices Postgresql en producción (y contando) .
fuente
SUM
como un ejemplo, pero en la práctica mis preguntas no pueden ser calculados previamente (Son más comoselect ... from t where a = '?' and ??
wjere??
habría alguna otra condición definida por el usuario.??
es;)DO
en esta respuesta relacionada .