GH GambleHub

Indexación y optimización de consultas

1) Objetivos de indexación y optimización

Latencia: reducción de P50/P95/P99.
Ancho de banda: crecimiento de QPS sin escala horizontal.
Previsibilidad: planes estables y sin «saltos» de tiempo de respuesta.
Ahorro: menos IO/CPU, menos factura por nube.
Fiabilidad: reduzca los bloqueos y los desbloqueos a través de los accesos correctos.

Invariantes:
  • Cualquier optimización debe mantener la corrección y la consistencia.
  • Realizar un seguimiento del efecto en métricas y logotipos de planes.

2) Estructuras básicas de los índices y cuándo aplicarlos

2. 1 B-Tree (default)

Igual a/rangos, ordenaciones, 'ORDER BY'.
Es bueno para la mayoría de los filtros de tiempo/ID/estado.

2. 2 Hash

Pure Equality ('='), más barato de memoria, pero sin orden (PG: se eliminan las restricciones, pero sigue siendo una opción de nicho).

2. 3 GIN / GiST (PostgreSQL)

GIN: matrices/claves JSONB, texto completo (tsvector), contenido ('@>').
GiST: geo, rangos, kNN.

2. 4 BRIN (PostgreSQL)

Índice súper barato por mesas «naturalmente ordenadas» (append-only por tiempo). Bueno para series de tiempo con tablas grandes.

2. 5 Bitmap (MySQL/InnoDB: no nativo; DW-SUBD/OLAP)

Son eficaces para la baja cardinalidad y facetas, más a menudo en bóvedas de columnas.

2. 6 Índices de columna (ClickHouse)

Primary key + data skipping (minmax), secondary через `skip indexes` (bloom, set).
Solicitudes OLAP con agregaciones y rangos.

2. 7 Índices invertidos (Elasticsearch/OpenSearch)

Texto completo, facetas, búsqueda híbrida. Para filtros precisos, utilice los campos clave y doc values.

2. 8 MongoDB

Single, compound, multikey (matrices), partial, TTL, text, hashed (para charding en clave uniforme).

3) Diseño de llaves e índices compuestos

3. 1 Regla «prefijo izquierdo»

El orden de los campos del índice determina la usabilidad.
La consulta 'WHERE tenant_id =? AND created_at >=? ORDER BY created_at DESC` → индекс `(tenant_id, created_at DESC, id DESC)`.

3. 2 Tie-breaker

Añade una cola única (normalmente 'id') para ordenar y ver la paginación de forma estable.

3. 3 Índices parciales/filtrados

Indexe sólo los subconjuntos «calientes»:
sql
CREATE INDEX idx_orders_paid_recent
ON orders (created_at DESC, id DESC)
WHERE status = 'paid' AND created_at > now() - interval '90 days';

3. 4 Índices de cobertura

Incluya campos «legibles» en el índice (MySQL: 'INCLUDE' no; PG 11+: `INCLUDE`):
sql
CREATE INDEX idx_user_lastseen_inc ON users (tenant_id, last_seen DESC) INCLUDE (email, plan);

3. 5 Funcional/Computable

Normalice las claves del índice:
sql
CREATE INDEX idx_norm_email ON users (lower(email));

4) Partición y chardeo

4. 1 Partición (herencia nativa de PG/tabla; MySQL RANGE/LIST)

La rotación de lotes por tiempo ('daily/weekly') simplifica 'VACUUM/DELETE'.
Los índices son lotes locales → menos B-Tree, plan más rápido.

sql
CREATE TABLE events (
tenant_id bigint,
ts timestamptz,
...
) PARTITION BY RANGE (ts);

4. 2 Clave de partición

En OLTP, por 'tenant _ id' (localización de carga).
En series de tiempo/OLAP - por 'ts' (consultas de rango).
Híbrido: '(tenant_id, ts)' + subparticiones.

4. 3 Charding

Consistent hashing/range-shard por 'tenant _ id' o por tiempo.
Consulta cruzada → scatter-gather y k-way merge; Mantenga el cursor per-shard.

5) Estadísticas, cardinalidad y planes

5. 1 Estadísticas actualizadas

Habilite el análisis automático ('autovacuum/autoanalyze'), aumente 'default _ statistics _ target' para las distribuciones 'sucias'.

5. 2 Estadísticas avanzadas (PG)

Columnas correlacionadas:
sql
CREATE STATISTICS stat_user_country_city (dependencies) ON country, city FROM users;
ANALYZE users;

5. 3 Plan de ejecución

Ver 'EXPLAIN (ANALYZE, BUFFERS, VERBOSE)'; campos clave:
  • `Rows`, `Loops`, `Actual time`, `Shared Read/Hit`, `Recheck Cond`.
  • Типы join: Nested Loop, Hash Join, Merge Join.
  • Seq Scan vs Index Scan/Only Scan/Bitmap Heap Scan.

5. 4 Estabilidad de planes

La parametrización (estados preestablecidos) puede «verter» en un mal plan. Utilice el plan cache guardrails (PG: 'plan _ cache _ mode = force_custom_plan' para consultas problemáticas) o el «probador» de constantes.

6) Optimización de join-in y ordenaciones

6. 1 Estrategias

Nested Loop: pequeño externo, índice rápido en el interior.
Hash Join: conjuntos grandes, agarra la memoria debajo de la mesa de hash.
Merge Join: entradas ordenadas, ventajosas con el orden ya disponible.

6. 2 Índices bajo join

Para 'A JOIN B ON B.a_id = A.id' → el índice en 'B (a_id)'.
Para el filtro post-join: índice en las columnas del filtro de la tabla interna.

6. 3 Ordenaciones

Evite 'ORDER BY' sin el índice correspondiente; ordenar en conjuntos grandes el camino de memoria/disco.

7) Reescritura de solicitudes (query rewrite)

Deshacerse de los «copos de nieve» de las sub-estaciones; desplegar en JOIN.
Utilice CTE-inline (PG ≥12 inlines CTE por defecto, pero 'MATERIALIZED' puede confirmar el resultado intermedio si es necesario).
Retire 'SELECT' → enumere los campos (ahorro de IO/red).
Transfiera los cálculos de 'WHERE' a la forma indexada (columnas anticipadas).
Agregaciones: tablas resumidas preliminares/representaciones materializadas con actualización incremental.

8) Batcheo, limitación y paginación

Batch-insert/update: paquetes 500-5000 en lugar de piezas.
Seek-paginación por '(sort_key, id)' en lugar de 'OFFSET' profundo.
Limite el conjunto antes de ordenar/joyne (push-down 'LIMIT').

9) Almacenamiento en caché y denormalización

Query-cache de nivel de aplicación (clave = SQL + bind-vars + versión de derechos).
Vistas materializadas para unidades pesadas; plan de rotación/refresco.
Denormalización: almacena campos computables de lectura frecuente (precio con descuento), pero con un disparador/tarea de fondo para consistencia.
Redis como L2 para llaves «hot» (con TTL y discapacidad por eventos).

10) Especificidad de los motores populares

10. 1 PostgreSQL

Индексы: B-Tree, Hash, GIN/GiST, BRIN, partial, functional, INCLUDE.

Ejemplo:
sql
CREATE INDEX idx_orders_tenant_created_desc
ON orders (tenant_id, created_at DESC, id DESC)
INCLUDE (amount, status);
Texto completo:
sql
CREATE INDEX idx_docs_fts ON docs USING GIN (to_tsvector('russian', title          ' '          body));

10. 2 MySQL/InnoDB

Índices compuestos que cubren (mediante la inclusión de campos en la clave), índices invisibles para pruebas:
sql
ALTER TABLE orders ALTER INDEX idx_old INVISIBLE; -- check risk-free plans

Estadísticas por histogramas ('ANALYZE TABLE... UPDATE HISTOGRAM` в 8. 0).

10. 3 ClickHouse

Clave principal = ordenar; 'ORDENAR POR (tenant_id, ts, id)'.

Índices de omisión:
sql
CREATE TABLE events (
tenant_id UInt64,
ts DateTime64,
id UInt64,
payload String,
INDEX idx_bloom_payload payload TYPE bloom_filter GRANULARITY 4
) ENGINE = MergeTree()
ORDER BY (tenant_id, ts, id);

10. 4 MongoDB

Compuestos/dibujos animados: el orden es importante, el filtro y la clasificación deben coincidir con el índice:
js db. orders. createIndex({ tenant_id: 1, created_at: -1, _id: -1 });
db. orders. createIndex({ status: 1 }, { partialFilterExpression: { archived: { $ne: true } } });

Utilice 'hint ()' para diagnosticar, siga 'covered query'.

10. 5 Elasticsearch/OpenSearch

Keyword vs text campos; doc_values para ordenar/agregados.
Segmentación heap: agregaciones - heavy; limite el 'tamaño' y utilice la agregación 'composición' (muestra por página).
No incluya analizadores donde se requiera una comparación precisa.

11) Competencia, bloqueo y MVCC

Transacciones cortas; evitar lecturas «largas» bajo 'LECTURA REPEATABLE' sin necesidad.
Las operaciones de índice también toman bloqueos (reducción de la escritura a través de la entrada).
Planifique la indexación en línea: 'CREATE INDEX CONCURRENTLY' (PG), 'ALGORITHM = INPLACE '/' ONLINE' (MySQL).
Insertar en la cola por hora/ID → «páginas calientes» del índice; distribuir la llave (UUIDv7/sal).

12) Observabilidad y SLO

Métricas:
  • 'db _ query _ latency _ ms' (P50/P95/P99) por el nombre de la consulta.
  • `rows_examined`, `rows_returned`, `buffer_hit_ratio`.
  • `deadlocks`, `lock_wait_ms`, `temp_sort_disk_usage`.
  • Una fracción de los planes con 'Seq Scan' donde se esperaba 'Index Scan'.
  • alertas de retroceso cuando se cambia la versión/los parámetros del DBM.
Logs/treising:
  • Habilite el registro de consulta lenta con un umbral (por ejemplo, 200 ms).
  • Correlación de solicitudes con spans (trace_id).
  • Retire los planes de consultas problemáticas y guárdelos en el almacén de objetos para una retrospectiva.
Ejemplo de SLO:
  • P95 de lecturas '<= 150 ms' en' LIMIT <= 50 'y tenante' caliente '.
  • P95 entradas '<= 200 ms' en batches de hasta 1000 líneas.

13) Seguridad y multi-tenencia

Los índices por campos de control de acceso ('tenant _ id', 'owner _ id') son obligatorios.
Las directivas (RLS/ABAC) deben ser pre-filtros; de lo contrario, el optimizador planea incorrectamente.
No indexe campos sensibles en vista abierta; use hashes/tokens.

14) Anti-patrones

Un 'OFFSET' profundo sin una alternativa de seek-cursor.
«Un índice para todo» - sobrecarga de memoria y escritura-path.
'SELECT' en caminos críticos.
Funciones sobre la columna en 'WHERE' sin índice funcional.
Planes inestables debido a las estadísticas antiguas.
Ausencia de 'ORDER BY' en espera de un orden estable.
Índices en aras de los índices: ROI <0 debido a su costoso registro/soporte.

15) Lista de verificación de implementación

1. La N superior de las consultas por QPS y tiempo → seleccionar 3-5 candidatos.
2. Retire los planes de 'EXPLAIN ANALYZE', verifique la cardinalidad vs real.
3. Diseñar índices: orden de campos, INCLUDE/parcial/funcional.
4. Implementar partición para tablas grandes (claves de tiempo/sombra).
5. Reescribir consultas: quitar 'SELECT', insertar CTE simples, limitar el conjunto.
6. Habilitar batching y paginación seek.
7. Configurar caché: L1/L2, discapacidad por eventos.
8. Introducir el monitoreo de planes y slow-logs, alertas de retrocesos.
9. Realizar pruebas de carga con distribución de datos real.
10. Actualizar las líneas de datos para el desarrollo (hints ORM, indexación, límites).

16) Ejemplos «antes/después»

Hasta:
sql
SELECT FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC
LIMIT 50 OFFSET 5000;
Después de:
sql
-- Индекс: (status, created_at DESC, id DESC) INCLUDE (amount, currency)
SELECT id, amount, currency, created_at
FROM orders
WHERE status = 'paid'
AND (created_at, id) < (:last_ts,:last_id)   -- seek
ORDER BY created_at DESC, id DESC
LIMIT 50;

17) FORM y protocolos API

Evitar N + 1: muestras codiciosas ('includes', 'JOIN FETCH', 'preload').
Proyecciones explícitas de campos, paginate con el cursor.
gRPC/NAT: limite 'page _ size', fije 'sort', use tokens opacos.
Plan de caché: utilice la parametrización; no genere SQL 'únicos' por cada llamada.

18) Migración y explotación

Agregue índices en línea y etiquete como INVISIBLE/CONCURRENTLY, pruebe los planes, luego cambie.
Las revisiones de los índices son limpieza sanitaria regular: duplicados, sin usar, «muertos» para los viejos fiches.
Plan de rotación de lotes (drop de antaño) y horario 'VACUUM/OPTIMIZE'.

19) Resumen

La optimización de consultas es ingeniería de sistemas: claves e índices correctos, planes ordenados, lotes y charding bien pensados, disciplina en consultas y ORM, almacenamiento en caché y observabilidad. Al cumplir con los patrones descritos, obtendrá un sistema rápido, predecible y rentable, resistente al crecimiento de datos y carga.

Contact

Póngase en contacto

Escríbanos ante cualquier duda o necesidad de soporte.¡Siempre estamos listos para ayudarle!

Iniciar integración

El Email es obligatorio. Telegram o WhatsApp — opcionales.

Su nombre opcional
Email opcional
Asunto opcional
Mensaje opcional
Telegram opcional
@
Si indica Telegram, también le responderemos allí además del Email.
WhatsApp opcional
Formato: +código de país y número (por ejemplo, +34XXXXXXXXX).

Al hacer clic en el botón, usted acepta el tratamiento de sus datos.