No aplique ciegamente la teoría de "subbase de datos y subtabla" en la base de datos a Elasticsearch

1. Descripción del problema

Recientemente, encontré algunos desafíos al optimizar los índices. Nuestro entorno es una máquina de 16*32G de 7 nodos y estoy tratando de optimizar la memoria. El número total actual de documentos es de 500 millones, pero existen problemas tanto en el diseño de mapas como en el diseño de fragmentos. Hay 480 fragmentos en cada nodo, que es una cantidad bastante ridícula.

Me encontré con problemas aún mayores cuando traté de perfilar el consumo de memoria. Aunque se puede calcular la cantidad de memoria ocupada por datos de campo, finalización, segmentos, caché de consulta y translog, el uso de memoria de Heap ha alcanzado los 15 G. Lo que me confunde es, ¿dónde se consume la memoria restante?

El diseño de fragmentación debe hacer referencia a la idea de la división de tablas mysql y dividir un índice en 300 índices, como index_1, index_2...index_300.

Creo que estos dos problemas pueden causar problemas de memoria.

——De: Deadly Elasticsearch Knowledge Planet 

https://t.zsxq.com/10fKvwz0k

2. Análisis de problemas

Los estudiantes dieron los datos completos de los resultados del análisis estadístico. Desarmémoslo e interpretémoslo sección por sección para que podamos encontrar el problema en la revisión. El siguiente contenido proviene de statslos resultados de retorno de los siguientes comandos. 

OBTENER _cluster/estadísticas

2.1 Problema 1 encontrado: hay una gran cantidad de operaciones de eliminación o actualización.

"docs": {
      "count": 331681467,
      "deleted": 73434046
    },

El número de documentos eliminados (eliminado: 73434046) es bastante alto. Esto puede deberse a que el escenario comercial actual actualiza o elimina documentos con frecuencia, por lo que se generarán muchos documentos marcados como eliminados en Elasticsearch.

En Elasticsearch, actualizar un documento es en realidad eliminar el documento anterior e indexar el nuevo. Los documentos eliminados seguirán ocupando espacio durante un período de tiempo y no se eliminarán hasta que se fusione el siguiente segmento.

Si la aplicación tiene muchas operaciones de eliminación o actualización, puede causar problemas de rendimiento, porque la fusión de segmentos es una operación relativamente costosa. Además, demasiados documentos eliminados también ocuparán más espacio de almacenamiento. En este caso, puede considerar ajustar el modelo de datos o la estrategia de indexación. Por ejemplo, evite operaciones de actualización excesivas o utilice índices basados ​​en el tiempo (time-based index). Cuando se utilizan índices basados ​​en el tiempo, se pueden crear nuevos índices periódicamente (por ejemplo, todos los días) y se pueden eliminar los índices antiguos, lo que puede evitar una gran cantidad de operaciones de eliminación.

2.2 Problema encontrado 2: hay una gran cantidad de documentos que se han eliminado pero no se han limpiado.

"segments": {
     "count": 3705,
     "memory_in_bytes": 43210351,
     "terms_memory_in_bytes": 34680393,
     "stored_fields_memory_in_bytes": 1870504,
     "term_vectors_memory_in_bytes": 0,
     "norms_memory_in_bytes": 1604160,
     "points_memory_in_bytes": 0,
     "doc_values_memory_in_bytes": 5055294,
     "index_writer_memory_in_bytes": 54801608,
     "version_map_memory_in_bytes": 211869,
     "fixed_bit_set_memory_in_bytes": 50741120,
     "max_unsafe_auto_id_timestamp": 1687046400930,
     "file_sizes": {}
   }

Los principales resultados e interpretaciones son los siguientes:

parámetro valor explicar
contar 3705 La cantidad de segmentos en el índice, este valor parece normal, porque Elasticsearch realizará automáticamente la operación de combinación de segmentos.
memoria_en_bytes 43210351 La cantidad total de memoria utilizada por todos los segmentos, incluidos términos, campos_almacenados, normas, valores_de_doc, etc. Este valor depende de los datos del índice y la carga de consultas, siempre que no exceda la memoria total de su nodo, no hay problema.
index_writer_memory_in_bytes 54801608 La cantidad total de memoria utilizada actualmente por Index Writer. Este valor parece normal, porque el autor del índice necesita algo de memoria para manejar las operaciones de indexación en curso.
versión_mapa_memoria_en_bytes 211869 La cantidad de memoria utilizada para contener la información de la versión del documento. También parece estar dentro del rango normal.
bit_fijo_establecer_memoria_en_bytes 50741120 La cantidad de memoria utilizada para almacenar información sobre documentos eliminados, un valor relativamente alto que puede indicar una gran cantidad de documentos eliminados pero no limpiados en el índice.

Problema de riesgo potencial: el valor de fixed_bit_set_memory_in_bytes es relativamente alto (50741120 bytes, aproximadamente 48,4 MB). Esta parte de la memoria se utiliza principalmente para almacenar información sobre documentos eliminados. En Elasticsearch, cuando se elimina o actualiza un documento, su versión anterior no se elimina físicamente de inmediato, sino que se marca como eliminada y no se eliminará hasta que se fusione el siguiente segmento. Esto significa que puede haber una gran cantidad de documentos eliminados pero no limpiados en el índice.

Esta condición puede degradar el rendimiento de las consultas y consumir espacio de almacenamiento adicional. Estos documentos eliminados se pueden limpiar a través de la operación de combinación forzada, pero tenga en cuenta que la combinación forzada es una operación intensiva de E/S y puede afectar el rendimiento del clúster durante la ejecución. Por lo general, la operación de combinación forzada debe realizarse durante las horas de trabajo de menor actividad. Además, si los documentos se actualizan o eliminan con frecuencia, puede ser necesario ajustar la estrategia de indexación o el modelo de datos para reducir tales operaciones.

2.3 Problema 3 encontrado: Hay una gran cantidad de operaciones que no han sido enviadas al índice de Lucene

"translog": {
      "operations": 4171567,
      "size_in_bytes": 2854130582,
      "uncommitted_operations": 4171567,
      "uncommitted_size_in_bytes": 2854130582,
      "earliest_last_modified_age": 0
    },

Los principales resultados e interpretaciones son los siguientes:

parámetro valor explicar
operaciones 4171567 El número de operaciones en el translog. Este valor es bastante grande, lo que indica que hay un gran número de operaciones que no se han enviado al índice de Lucene.
tamaño_en_bytes 2854130582 El tamaño del translog, que también es bastante grande, puede resultar en tiempos de recuperación de datos más prolongados en caso de un bloqueo del sistema.
operaciones_no_comprometidas 4171567 El número de operaciones que aún no se han confirmado, igual que "operaciones", lo que indica que estas operaciones aún no se han confirmado.
tamaño_no_comprometido_en_bytes 2854130582 Tamaño de las operaciones aún no confirmadas, igual que "size_in_bytes".
edad_primera_última_modificada 0 La hora de la operación no confirmada más antigua, un valor de 0 significa que todas las operaciones están actualizadas.

Problema de riesgo potencial: esto puede generar tiempos de recuperación de datos más prolongados en caso de un bloqueo del sistema, ya que es necesario volver a ejecutar las operaciones para restaurar el estado más reciente.

Si bien esto puede no tener un impacto significativo en un clúster en ejecución, en algunos casos, como el tiempo de inactividad del nodo o la recuperación del clúster, puede afectar el rendimiento de Elasticsearch y la velocidad de recuperación de datos. Por lo tanto, generalmente se recomienda enviar regularmente datos de Translog al índice de Lucene para mantener su tamaño dentro de un rango razonable.

2.4 Problema encontrado 4: El uso de memoria del sistema operativo donde se encuentra el clúster es muy alto

"os": {
    "timestamp": 1687165428228,
    "cpu": {
      "percent": 13,
      "load_average": {
        "1m": 2.11,
        "5m": 1.68,
        "15m": 1.75
      }
    },
    "mem": {
      "total_in_bytes": 32822083584,
      "free_in_bytes": 260890624,
      "used_in_bytes": 32561192960,
      "free_percent": 1,
      "used_percent": 99
    },
    "swap": {
      "total_in_bytes": 0,
      "free_in_bytes": 0,
      "used_in_bytes": 0
    },
    "cgroup": {
      "cpuacct": {
        "control_group": "/user.slice",
        "usage_nanos": 15187558135108329
      },
      "cpu": {
        "control_group": "/user.slice",
        "cfs_period_micros": 100000,
        "cfs_quota_micros": -1,
        "stat": {
          "number_of_elapsed_periods": 0,
          "number_of_times_throttled": 0,
          "time_throttled_nanos": 0
        }
      },
      "memory": {
        "control_group": "/",
        "limit_in_bytes": "9223372036854771712",
        "usage_in_bytes": "31734857728"
      }
    }
  },

Los principales resultados e interpretaciones son los siguientes:

parámetro valor explicar
cpu.percent 13 Uso de CPU, que está dentro del rango normal.
mem.total_en_bytes 32822083584 cantidad total de memoria.
mem.free_in_bytes 260890624 La cantidad de memoria libre, muy baja, lo que puede causar problemas de rendimiento.
mem.used_in_bytes 32561192960 La cantidad de memoria utilizada.
mem.free_percent 1 Porcentaje de memoria libre, muy bajo, que puede causar problemas de rendimiento.
mem.used_percent 99 Es posible que sea necesario ajustar el porcentaje de memoria utilizado, muy alto, para obtener un mejor rendimiento.
swap.total_en_bytes, swap.free_in_bytes, swap.used_in_bytes 0 La cantidad total de espacio de intercambio, la cantidad libre y la cantidad utilizada son 0, lo que indica que no se utiliza ningún espacio de intercambio.

Problema de riesgo potencial: el sistema operativo (SO) donde reside el clúster de Elasticsearch tiene un uso de memoria muy alto ("used_percent": 99) y muy poca memoria libre ("free_percent": 1). Esto puede causar problemas de rendimiento, ya que es posible que el sistema tenga que usar el disco con frecuencia para operaciones de intercambio, lo que puede reducir significativamente el rendimiento.

Se recomienda tomar medidas para liberar memoria o agregar más memoria lo antes posible para mejorar el rendimiento de Elasticsearch.

2.5 Problema encontrado 5: el uso de la memoria del montón es bastante alto

"jvm": {
    "timestamp": 1687165428234,
    "uptime_in_millis": 5988052030,
    "mem": {
      "heap_used_in_bytes": 16480235136,
      "heap_used_percent": 76,
      "heap_committed_in_bytes": 21474836480,
      "heap_max_in_bytes": 21474836480,
      "non_heap_used_in_bytes": 309785016,
      "non_heap_committed_in_bytes": 354152448,
      "pools": {
        "young": {
          "used_in_bytes": 8967421952,
          "max_in_bytes": 0,
          "peak_used_in_bytes": 12968787968,
          "peak_max_in_bytes": 0
        },
        "old": {
          "used_in_bytes": 7479258752,
          "max_in_bytes": 21474836480,
          "peak_used_in_bytes": 11748245496,
          "peak_max_in_bytes": 21474836480
        },
        "survivor": {
          "used_in_bytes": 33554432,
          "max_in_bytes": 0,
          "peak_used_in_bytes": 1610612736,
          "peak_max_in_bytes": 0
        }
      }
    },
    "threads": {
      "count": 268,
      "peak_count": 314
    },
    "gc": {
      "collectors": {
        "young": {
          "collection_count": 434416,
          "collection_time_in_millis": 18999559
        },
        "old": {
          "collection_count": 0,
          "collection_time_in_millis": 0
        }
      }
    },

Los principales resultados e interpretaciones son los siguientes:

parámetro valor explicar
heap_used_in_bytes 16480235136 Uso de memoria de montón, este valor es bastante grande.
montón_utilizado_porcentaje 76 El porcentaje de memoria de pila utilizada, este valor también es bastante grande y puede afectar el rendimiento.
heap_committed_in_bytes 21474836480 La cantidad de memoria de almacenamiento dinámico asignada a la JVM.
heap_max_in_bytes 21474836480 La cantidad máxima de memoria de pila, debe seguir prestando atención y asegurarse de que el valor de heap_used_percent no esté demasiado cerca de este valor para evitar la presión de la memoria.
non_heap_used_in_bytes 309785016 Uso de memoria no acumulada.
non_heap_committed_in_bytes 354152448 La cantidad de memoria que no es de almacenamiento dinámico asignada a la JVM.
joven.usado_en_bytes 8967421952 La cantidad de memoria utilizada por la generación joven.
viejo.usado_en_bytes 7479258752 Cantidad de memoria utilizada en la generación anterior.
gc.collectors.young.collection_count 434416 El número de recolecciones de basura de generaciones jóvenes.
gc.collectors.old.collection_count 0 El número de recolecciones de elementos no utilizados de la generación anterior. Este valor es 0, lo que significa que el recolector de elementos no utilizados de la generación anterior aún no se ha ejecutado. Esto es normal, excepto en el caso de una gran presión de memoria.

Problema de riesgo potencial: el uso de la memoria del montón es bastante alto ("heap_used_percent": 76). Si bien este valor puede no causar problemas inmediatos, si aumenta la carga del índice o si hay más consultas, puede aumentar la presión de la memoria, provocar una recolección de basura más frecuente y, por lo tanto, afectar el rendimiento.

Se recomienda monitorear los cambios en estos valores y ajustar la configuración de memoria de la JVM si es necesario para mantener el rendimiento de Elasticsearch.

2.6 Problema encontrado 6: Las operaciones de lectura son mucho más que las operaciones de escritura

"io_stats": {
      "devices": [
        {
          "device_name": "dm-0",
          "operations": 5250539512,
          "read_operations": 4478787246,
          "write_operations": 771752266,
          "read_kilobytes": 129711481927,
          "write_kilobytes": 23684659984
        }
      ],
      "total": {
        "operations": 5250539512,
        "read_operations": 4478787246,
        "write_operations": 771752266,
        "read_kilobytes": 129711481927,
        "write_kilobytes": 23684659984
      }
    }
  }

Los principales resultados e interpretaciones son los siguientes:

parámetro valor explicar
operaciones 5250539512 El número total de operaciones de E/S.
leer_operaciones 4478787246 El número total de operaciones de lectura, que es más que las operaciones de escritura.
escribir_operaciones 771752266 El número total de operaciones de escritura.
leer_kilobytes 129711481927 La cantidad total de operaciones de lectura, en KB.
escribir_kilobytes 23684659984 La cantidad total de operaciones de escritura, en KB.

Problema de riesgo potencial: lo anterior muestra las estadísticas de operación de E/S para el clúster de Elasticsearch. Parece que hay muchas más lecturas que escrituras, pero eso no es necesariamente un problema, todo depende de cómo su aplicación use Elasticsearch. Si el escenario comercial actual es principalmente para consultar datos, entonces se puede explicar la cantidad de operaciones de lectura.

Puede ajustar la configuración de E/S de Elasticsearch en función de esta información. Por ejemplo, si hay muchas operaciones de lectura, es posible que deba optimizar el hardware o la configuración para aumentar la velocidad de lectura.

2.7 Problema encontrado 6: baja tasa de aciertos de caché

"query_cache": {
    "memory_size_in_bytes": 422629063,
    "total_count": 18178614894,
    "hit_count": 4107645935,
    "miss_count": 14070968959,
    "cache_size": 405975,
    "cache_count": 16870486,
    "evictions": 16464511
  }

Los principales resultados e interpretaciones son los siguientes:

parámetro valor paráfrasis
tamaño_memoria_en_bytes 422629063 Tamaño de la memoria caché de consultas (bytes)
cuenta total 18178614894 Número total de solicitudes de caché de consultas
hit_count 4107645935 Aciertos de caché de consultas
miss_count 14070968959 Errores de caché de consultas
tamaño del caché 405975 El número de cachés de consultas actuales
cache_count 16870486 查询缓存创建的总个数
evictions 16464511 查询缓存逐出的总次数

潜在风险问题——查询缓存命中率似乎有些低,这可能意味着当前业务查询有很大的多样性,或者缓存设置不够理想。

建议:如果想提高查询缓存的效率,可能需要调整查询缓存的大小,或者看看是否有一些查询可以做些修改以适应缓存。此外,一些不需要缓存的查询,可以明确地在查询中设置 ——"cache": false 来避免对缓存造成不必要的压力。

3、问题总结

我们从响应中得到了一些显著的内存相关统计信息:操作系统级别的内存使用非常高,只剩下1%的总内存空闲。如果内存使用继续上升,可能会导致性能问题或崩溃。

  • JVM内存使用

首先,JVM的堆内存使用了76%,接近80%的警戒线。如果内存使用超过80%,将会触发更频繁的垃圾收集,可能会对性能产生影响。同时,“young”内存池的使用超过了其“max”值,这也可能是一个需要进一步调查的问题。

  • 操作系统内存使用

操作系统的内存使用很高,仅剩1%的空闲内存,这可能会导致系统性能降低,甚至导致进程被操作系统杀死以释放内存。

  • 索引压力

"Indexing_pressure.memory.total.combined_coordinating_and_primary_in_bytes"远远大于"indexing_pressure.memory.limit_in_bytes",这表示索引操作产生的内存压力超过了预设的限制。这可能导致新的写入操作被拒绝,以防止内存耗尽。

  • 索引失败

“indexing.index_failed”值为10253,这表示有一些索引操作失败。可能需要查看Elasticsearch的日志来确定失败的原因。

  • 缓冲区使用

“buffer_pools.mapped.used_in_bytes”值很高,表示映射的文件缓冲区使用了很大的内存。这通常是由于大量的文件被打开并映射到内存中,可能是由于大量的读取操作或大量的小文件。

  • 可能存在大量删除或更新操作

因为在Elasticsearch中,删除的文档不会立即被清除,而是在下次合并段时才被清除,这可能会占用额外的空间。

3.1 可能的原因

上述问题可能由以下几个原因引起:

  • 1、大量的数据操作

频繁的索引、更新和删除操作可能会使Elasticsearch需要更多的内存来处理这些操作。

  • 2、大量的并发查询

高并发查询会使Elasticsearch需要在短时间内处理大量请求,也可能导致内存使用上升。

  • 3、大量的数据段合并

数据段合并需要消耗大量的计算和内存资源。

  • 4、数据库分库分表理论直接迁移到 Elasticsearch

分片设置不合理,sharding(分片)设计应该是参考了 mysql 的分表的思路,给一个 index 拆成了300个 index。

解决这些问题通常需要结合监控数据和日志来确定具体原因,然后根据具体情况进行优化或扩容。

3.2 根因:MySQL 的分库分表理论不直接适用于 Elasticsearch

在进行深入分析之后,我发现主要问题出在mapping和sharding的设计上。

  • 一开始,我们的mapping设计比较粗糙,甚至对一些hash也进行了分词。这导致了索引非常大,占用了大量的内存。

  • 另外,我们在设计sharding时,参考了MySQL的分表思路,给一个index拆成了300个index,例如index_1, index_2...index_300。

这两个问题都可能导致内存问题。

  • 一方面,mapping设计使索引很大,占用大量内存。

  • 另一方面,一次查询可能会打开300个shard,每个shard都有自己的pool,这可能就是导致“buffer_pools.mapped.used_in_bytes”值较大的原因。比如进行分页查询时,每次打开300个shard或segment,那就意味着一次查询打开了6000个文档。

因此,优化的当务之急就是合并索引。当前的单分片应该是不到 2G,小的分片应该是几百兆,分片并不均匀。

我计算了一下,这些分片应该可以合并到8个分片(原来数百个)。这种优化应该能够显著减少内存的消耗,进一步提升Elasticsearch的性能。

4、小结

Elasticsearch的设计理念和关系型数据库(例如MySQL)的设计理念是有明显区别的。在关系型数据库中,分表是常见的处理大量数据的策略,但是在Elasticsearch中,过度分片会导致效率降低和内存占用过高。以下是一些深入的分析和后续开发人员的注意事项:

4.1 Mapping 的设计:

  • 在设计字段类型时,尽量使用更加精确的数据类型,避免不必要的文本字段,特别是对于一些hash值或者ID值,它们无需分词,直接用keyword类型存储即可。

  • 如果必须要分词的话,合理选择分词器。例如,对于中文,ik_max_word可能会产生大量的词条,而ik_smart则更为节省资源。

  • 对于大文本字段,可以考虑禁用倒排索引,或者只对部分关键内容做索引,避免索引过大。

4.2 Sharding 分片的设计:

  • Elasticsearch的每个shard都是一个完全的Lucene索引,拥有自己的数据结构和资源开销,所以shard的数量不应该过多。过多的shard会消耗大量的内存和CPU,降低查询性能。

  • 单个shard的大小通常建议在20GB-40GB之间。过小的shard会增加开销,过大的shard在做recovery时会消耗更多的时间。

  • 尽量避免一个查询涉及到太多的shard,这会增加查询时间和资源消耗。如果可能,尽量在一个index内部进行数据的切分和查询,而不是在多个index之间。

  • 考虑使用index alias或者routing功能,减少不必要的shard查询。

4.3 后续开发人员的注意事项:

在构建和优化Elasticsearch数据模型时,我们必须深入理解其内在工作机制,并借鉴已有的最佳实践,而非简单地迁移关系型数据库的理论。

持续监控Elasticsearch的核心数据,如shard的数量、大小,以及CPU和内存的使用情况,是预防问题、提前发现和处理隐患的关键。

此外,我们需要定期进行性能测试,以了解系统的性能瓶颈和限制,并通过对不同shard数量和大小的性能变化的测试,找出最优的shard设计方案。

为了更好地使用和优化Elasticsearch,我们必须不断学习和保持对其新功能和最佳实践的关注。

遇到问题时,要充分利用Elasticsearch提供的各种分析工具,如_slow log_和_hot threads_,以准确找出问题的根源。这是我们向更高效、更稳定的Elasticsearch 服务迈进的关键步骤。

推荐阅读

  1. 全网首发!从 0 到 1 Elasticsearch 8.X 通关视频

  2. 重磅 | 死磕 Elasticsearch 8.X 方法论认知清单

  3. 如何系统的学习 Elasticsearch ?

  4. 2023,做点事

6159bb7cdb8a2cb5cfd8704da839eaf8.jpeg

更短时间更快习得更多干货!

和全球 近2000+ Elastic 爱好者一起精进!

2716091f19b340edea1ec9f2117b7681.gif

大模型时代,抢先一步学习进阶干货!

Supongo que te gusta

Origin blog.csdn.net/wojiushiwo987/article/details/131692935
Recomendado
Clasificación