Como ejemplo simplificado, supongamos que tengo una tabla como esta:
seq | value
----+------
102 | 11954
211 | 43292
278 | 19222
499 | 3843
La tabla puede contener cientos de millones de registros, y necesito hacer consultas como esta con frecuencia:
SELECT sum(value) WHERE seq > $a and seq < $b
Incluso si seq
está indexado, una implementación de base de datos típica recorrerá cada fila para calcular la suma en el mejor de los casos O(n)
, donde n
está el tamaño del rango.
¿Hay alguna base de datos que pueda hacer esto de manera eficiente, como en O(log(n))
cada consulta?
Me he encontrado con una estructura de datos llamada Árbol de segmentos como se describe aquí . También a veces se lo denomina árbol de rango o árbol de intervalos, aunque todos estos nombres a menudo se describen como una variación ligeramente diferente de la estructura de datos.
Sin embargo, no he encontrado ninguna base de datos que implemente dicha estructura de datos. Implementarlo desde cero es fácil para una estructura en memoria, pero se vuelve complicado si tiene que persistir o es demasiado grande para caber en la memoria. Si hay un patrón eficiente para implementar esto sobre una base de datos existente, eso también podría ayudar.
Nota al margen: esta no es una tabla de solo agregar, por lo que una solución como mantener una suma acumulativa no funcionará en este caso.
Respuestas:
Uso de índices ColumnStore de SQL Server
Bueno, está bien, solo uno: un índice CS agrupado.
Si quieres leer sobre el hardware en el que hice esto, dirígete aquí . Revelación completa, escribí esa publicación de blog en el sitio web de la empresa para la que trabajo.
¡A la prueba!
Aquí hay un código genérico para construir una tabla bastante grande. La misma advertencia que Evan, esto puede tomar un tiempo para construir e indexar.
Bueno, Evan gana por simplicidad, pero ya he hablado de eso antes.
Aquí está la definición del índice. La y dee y dah.
Mirando un conteo, cada Id tiene una distribución bastante uniforme:
Resultados:
...
Con cada Id que tiene ~ 5,005,005 filas, podemos ver un rango bastante pequeño de ID para obtener una suma de 10 millones de filas.
Resultado:
Perfil de consulta:
Por diversión, una agregación más grande:
Resultados:
Perfil de consulta:
¡Espero que esto ayude!
fuente
PostgreSQL con un índice BRIN
Eso no es cierto. Al menos, ninguna base de datos decente lo hará. PostgreSQL admite la creación de índices BRIN en este tipo de tablas. Los índices BRIN son súper pequeños y pueden caber en ram incluso en tablas tan grandes. Cientos de millones de filas no son nada.
Aquí, 300 millones de filas definidas tal como las ordenó. Advertencia: puede llevar mucho tiempo crearlo (Tiempo: 336057.807 ms + 95121.809 ms para el índice).
Y ahora...
1.4 segundos para agregar / sumar 5,889,135 filas en el rango dado.
A pesar de que la tabla tiene 10 GB, el índice BRIN es 304 kB.
Aun más rápido
Si todavía no es lo suficientemente rápido, puede almacenar en caché los agregados en 100k filas.
Ahora solo necesitará usar las
2(1e5-1)
filas brin y agregadas en lugar de 300 millones o lo que sea.Hardware
Lenovo x230, i5-3230M, 16 GB de RAM, 1 tb Samsung 840 SSD.
fuente
O(n)
, quizásO(sqrt(n))
. Depende de cómo definirá los intervalos que se utilizarán en la materialización.