• Elasticsearch常用命令


    2021年9月28日08:45:19

    东西都是收集的,部分经过测试,es版本是17.4.1

     https://blog.csdn.net/zhuoshengda/article/details/90378466

    https://www.cnblogs.com/shaner/p/5661071.html

    https://www.cnblogs.com/caoweixiong/p/11792049.html

    https://blog.csdn.net/zhangningkid/article/details/97937895

    1、未分配分片查看

    GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state:asc
    

    2、动态调整副本数

    PUT my-index-2021.05.30-000002/_settings
    {"number_of_replicas": 0}
    

    ps:主分片不可以修改(除非shrink),但:副本可以动态调整大小。

    3、重新打开分片分配策略

    PUT /_cluster/settings
    {
      "transient": {
        "cluster.routing.allocation.enable": "all"
      }
    }
    

    4、手动移动未分配的分片

    POST /_cluster/reroute
    {
      "commands": [
        {
          "move": {
            "index": "test",
            "shard": 0,
            "from_node": "node1",
            "to_node": "node2"
          }
        },
        {
          "allocate_replica": {
            "index": "test",
            "shard": 1,
            "node": "node3"
          }
        }
      ]
    }
    

    5、查看磁盘使用率

    GET /_cat/allocation?v
    

    使用率大于等于85%,知道已经达到警戒水位线了,要预警。

    6、查看各个节点的版本号

    GET /_cat/nodes?v&h=host,name,version
    

    集群多节点版本不一致,可能会引发各种未知异常。

    7、检索性能调优实战

    7.1 慢日志设置

    PUT /my-index-000001/_settings
    {
      "index.search.slowlog.threshold.query.warn": "10s",
      "index.search.slowlog.threshold.query.info": "5s",
      "index.search.slowlog.threshold.query.debug": "2s",
      "index.search.slowlog.threshold.query.trace": "500ms",
      "index.search.slowlog.threshold.fetch.warn": "1s",
      "index.search.slowlog.threshold.fetch.info": "800ms",
      "index.search.slowlog.threshold.fetch.debug": "500ms",
      "index.search.slowlog.threshold.fetch.trace": "200ms",
      "index.search.slowlog.level": "info"
    }
    

    7.2 构建Mapping 设置路由

    PUT my-index-000002
    {
      "mappings": {
        "_routing": {
          "required": true 
        }
      }
    }
    

    7.3 段合并

    POST /my-index-000001/_forcemerge
    

    8、写入优化篇

    8.1 批量写入

    POST _bulk
    { "index" : { "_index" : "test", "_id" : "1" } }
    { "field1" : "value1" }
    { "delete" : { "_index" : "test", "_id" : "2" } }
    { "create" : { "_index" : "test", "_id" : "3" } }
    { "field1" : "value3" }
    { "update" : {"_id" : "1", "_index" : "test"} }
    { "doc" : {"field2" : "value2"} }
    

    8.2 增加刷新频率

    PUT /my-index-000001/_settings
    {
      "index" : {
        "refresh_interval" : "30s"
      }
    }
    

    8.3 副本设置为0,提升写入

    PUT my-index-000001/_settings
    {
      "number_of_replicas": 0
    }
    

    8.4 translog 异步刷盘

    PUT my-index-2021.06.03/_settings
    {
      "index": {
        "translog": {
          "durability": "async"
        }
      }
    }
    

    9、堆内存调优实战

    在 jvm.option 配置文件中(不支持动态调整,需要重启后生效)

    ES_HEAP_SIZE=DESIRED_SIZE (e.g. "3g")
    

    10、磁盘不足解决方案

    磁盘的三个警戒水位线需要再一次强调。如果 Elasticsearch 集群节点的磁盘空间不足,则会影响集群性能。

    一旦可用存储空间低于特定阈值限制,它将开始阻止写入操作,进而影响数据进入集群。那么,如何扩展呢?

    三个警戒水位线推荐阅读:你不得不关注的 Elasticsearch Top X 关键指标

    第一:横向扩展,添加数据节点(前提分片分配相对合理)。第二:纵向扩展,升级机器,加磁盘(可能需要调整data.path)。第三:迁移数据,历史久远不用数据可以考虑迁移到别的集群,或者归档到别的机器。

    1、集群状态非绿排查清单

    1.1 集群状态的含义

    • 红色:至少一个主分片未分配成功;
    • 黄色:至少一个副本分片未分配成功;
    • 绿色:全部主&副本都分配成功。

    1.2 排查实战

    1.2.1 查看集群状态

    GET _cluster/health

    返回状态举例:"status" : "red", 红色,至少一个主分片未分配成功。

    1.2.2 到底哪个节点出现了红色或者黄色问题呢?

    GET _cluster/health?level=indices

    如下的方式,更明快直接

    GET /_cat/indices?v&health=yellow
    GET /_cat/indices?v&health=red

    找到对应的索引。

    1.2.3 到底索引的哪个分片出现了红色或者黄色问题呢?

    GET _cluster/health?level=shards

    1.2.4 到底什么原因导致了集群变成红色或者黄色呢?

    GET _cluster/allocation/explain

    返回核心信息解读举例:

    "current_state" : "unassigned",——未分配
      "unassigned_info" : {
        "reason" : "INDEX_CREATED",——原因,索引创建阶段
        "at" : "2020-01-29T07:32:39.041Z",
        "last_allocation_status" : "no"
      },
      "explanation" : """node does not match index setting [index.routing.allocation.require] filters [box_type:"hot"]"""
            }

    根本原因,shard分片与节点过滤类型不一致 到此,找到了根本原因,也就知道了对应解决方案。

    1.3 扩展思考:类似 "current_state" : "unassigned",——未分配 还有哪些?

    实战:

    GET _cat/shards?h=index,shard,prirep,state,unassigned.reason

    官网:https://www.elastic.co/guide/en/elasticsearch/reference/7.2/cat-shards.html

    未分配状态及原因解读:

    (1)INDEX_CREATED
    Unassigned as a result of an API creation of an index.
    (2)CLUSTER_RECOVERED
    Unassigned as a result of a full cluster recovery.
    (3)INDEX_REOPENED
    Unassigned as a result of opening a closed index.
    (4)DANGLING_INDEX_IMPORTED
    Unassigned as a result of importing a dangling index.
    (5)NEW_INDEX_RESTORED
    Unassigned as a result of restoring into a new index.
    (6)EXISTING_INDEX_RESTORED
    Unassigned as a result of restoring into a closed index.
    (7)REPLICA_ADDED
    Unassigned as a result of explicit addition of a replica.
    (8)ALLOCATION_FAILED
    Unassigned as a result of a failed allocation of the shard.
    (9)NODE_LEFT
    Unassigned as a result of the node hosting it leaving the cluster.
    (10)REROUTE_CANCELLED
    Unassigned as a result of explicit cancel reroute command.
    (11)REINITIALIZED
    When a shard moves from started back to initializing, for example, with shadow replicas.
    (12)REALLOCATED_REPLICA
    A better replica location is identified and causes the existing replica allocation to be cancelled.

    2、节点间分片移动

    适用场景:手动移动分配分片。将启动的分片从一个节点移动到另一节点。

    POST /_cluster/reroute
    {
      "commands": [
        {
          "move": {
            "index": "indexname",
            "shard": 1,
            "from_node": "nodename",
            "to_node": "nodename"
          }
        }
      ]

    3、集群节点优雅下线

    适用场景:保证集群颜色绿色的前提下,将某个节点优雅下线。

    PUT /_cluster/settings
    {
      "transient": {
        "cluster.routing.allocation.exclude._ip": "122.5.3.55"
      }
    }

    4、强制刷新

    适用场景:刷新索引是确保当前仅存储在事务日志中的所有数据也永久存储在Lucene索引中。

    POST /_flush

    注意:这和 7.6 版本之前的同步刷新(未来8版本+会废弃同步刷新)一致。

    POST /_flush/synced

    5、更改并发分片的数量以平衡集群

    适用场景:

    控制在集群范围内允许多少并发分片重新平衡。默认值为2。

    PUT /_cluster/settings
    {
      "transient": {
        "cluster.routing.allocation.cluster_concurrent_rebalance": 2
      }
    }

    6、更改每个节点同时恢复的分片数量

    适用场景:

    如果节点已从集群断开连接,则其所有分片将都变为未分配状态。经过一定的延迟后,分片将分配到其他位置。每个节点要恢复的并发分片数由该设置确定。

    PUT /_cluster/settings
    {
      "transient": {
        "cluster.routing.allocation.node_concurrent_recoveries": 6
      }
    }

    7、调整恢复速度

    适用场景:

    为了避免集群过载,Elasticsearch限制了分配给恢复的速度。你可以仔细更改该设置,以使其恢复更快。

    如果此值调的太高,则正在进行的恢复可能会消耗过多的带宽和其他资源,这可能会使集群不稳定。

    PUT /_cluster/settings
    {
      "transient": {
        "indices.recovery.max_bytes_per_sec": "80mb"
      }
    }

    8、清除节点上的缓存

    适用场景:如果节点达到较高的JVM值,则可以在节点级别上调用该API 以使 Elasticsearch 清理缓存。

    这会降低性能,但可以使你摆脱OOM(内存不足)的困扰。

    POST /_cache/clear

    9、调整断路器

    适用场景:为了避免在Elasticsearch中进入OOM,可以调整断路器上的设置。这将限制搜索内存,并丢弃所有估计消耗比所需级别更多的内存的搜索。

    注意:这是一个非常精密的设置,你需要仔细校准。

    PUT /_cluster/settings
    {
      "persistent": {
        "indices.breaker.total.limit": "40%"
      }
    }

    10、集群迁移

    适用场景:集群数据迁移、索引数据迁移等。

    方案一、 针对索引部分或者全部数据,reindex

    图片

    POST _reindex
    {
      "source": {
        "index": "my-index-000001"
      },
      "dest": {
        "index": "my-new-index-000001"
      }
    }

    方案二:借助第三方工具迁移索引或者集群

    • elasticdump
    • elasticsearch-migration

    工具本质:scroll + bulk 实现。

    11、集群数据备份和恢复

    适用场景:高可用业务场景,定期增量、全量数据备份,以备应急不时之需。

    PUT /_snapshot/my_backup/snapshot_hamlet_index?wait_for_completion=true
    {
      "indices": "hamlet_*",
      "ignore_unavailable": true,
      "include_global_state": false,
      "metadata": {
        "taken_by": "mingyi",
        "taken_because": "backup before upgrading"
      }
    }

    POST /_snapshot/my_backup/snapshot_hamlet_index/_restore

    小结

    文章开头的几个运维问题已经解决,其他性能相关的问题,后面会有另外的博文做梳理。

    运维工作包罗万象,文章内容只是抛砖引玉,开了个头。

    牛逼的集群运维需要结合可视化工具(如:kibana,cerebro,elastic-hd,Prometheus + grafana,结合业务自研工具如 阿里云Eyou等)能极大提高效率。

     

    1、集群健康维度:分片和节点

    集群、索引、分片、副本的定义不再赘述。分片数的多少对集群性能的影响至关重要。分片数量设置过多或过低都会引发一些问题。

    分片数量过多,则批量写入/查询请求被分割为过多的子写入/查询,导致该索引的写入、查询拒绝率上升;
    对于数据量较大的索引,当分片数量过小时,无法充分利用节点资源,造成机器资源利用率不高或不均衡,影响写入/查询的效率。

    通过GET _cluster/health监视群集时,可以查询集群的状态、节点数和活动分片计数的信息。还可以查看重新定位分片,初始化分片和未分配分片的计数。

    GET _cluster/health
    {
      "cluster_name" : "elasticsearch",
      "status" : "yellow",
      "timed_out" : false,
      "number_of_nodes" : 1,
      "number_of_data_nodes" : 1,
      "active_primary_shards" : 127,
      "active_shards" : 127,
      "relocating_shards" : 0,
      "initializing_shards" : 0,
      "unassigned_shards" : 120,
      "delayed_unassigned_shards" : 0,
      "number_of_pending_tasks" : 0,
      "number_of_in_flight_fetch" : 0,
      "task_max_waiting_in_queue_millis" : 0,
      "active_shards_percent_as_number" : 51.417004048582996
    }

    图片

    集群运行的重要指标

    • Status:状态群集的状态。红色:部分主分片未分配。黄色:部分副本分片未分配。绿色:所有分片分配ok。

    • Nodes:节点。包括群集中的节点总数,并包括成功和失败节点的计数。 Count of Active

    • Shards:活动分片计数。集群中活动分片的数量。 Relocating Shards:重定位分片。由于节点丢失而移动的分片计数。

    • Initializing Shards:初始化分片。由于添加索引而初始化的分片计数。 Unassigned

    • Shards。未分配的分片。尚未创建或分配副本的分片计数。

    2、搜索性能维度:请求率和延迟

    我们可以通过测量系统处理请求的速率和每个请求的使用时间来衡量集群的有效性。

    当集群收到请求时,可能需要跨多个节点访问多个分片中的数据。系统处理和返回请求的速率、当前正在进行的请求数以及请求的持续时间等核心指标是衡量集群健康重要因素。

    请求过程本身分为两个阶段:

    • 第一是查询阶段(query phase),集群将请求分发到索引中的每个分片(主分片或副本分片)。

    • 第二个是获取阶段(fetch phrase),查询结果被收集,处理并返回给用户。

    通过GET index_a/_stats查看对应目标索引状态。限于篇幅原因,返回没有给全。具体的自己实践一把吧。

          "search" : {
            "open_contexts" : 0,
            "query_total" : 10,
            "query_time_in_millis" : 0,
            "query_current" : 0,
            "fetch_total" : 1,
            "fetch_time_in_millis" : 0,
            "fetch_current" : 0,
            "scroll_total" : 5,
            "scroll_time_in_millis" : 15850,
            "scroll_current" : 0,
            "suggest_total" : 0,
            "suggest_time_in_millis" : 0,
            "suggest_current" : 0
          },

    请求检索性能相关的重要指标如下

    • query_current:当前正在进行的查询数。集群当前正在处理的查询计数。

    • fetch_current:当前正在进行的fetch次数。集群中正在进行的fetch计数。

    • query_total:查询总数。集群处理的所有查询的聚合数。

    • query_time_in_millis:查询总耗时。所有查询消耗的总时间(以毫秒为单位)。

    • fetch_total:提取总数。集群处理的所有fetch的聚合数。

    • fetch_time_in_millis:fetch所花费的总时间。所有fetch消耗的总时间(以毫秒为单位)。

    3、索引性能维度:刷新(refresh)和合并(Merge)时间

    文档的增、删、改操作,集群需要不断更新其索引,然后在所有节点上刷新它们。所有这些都由集群负责,作为用户,除了配置 refresh interval 之外,我们对此过程的控制有限。

    增、删、改批处理操作,会形成新段(segment)并刷新到磁盘,并且由于每个段消耗资源,因此将较小的段合并为更大的段对于性能非常重要。同上类似,这由集群本身管理。

    监视文档的索引速率( indexing rate )合并时间(merge time)有助于在开始影响集群性能之前提前识别异常和相关问题。将这些指标与每个节点的运行状况并行考虑,这些指标为系统内的潜问题提供重要线索,为性能优化提供重要参考。

    可以通过GET /_nodes/stats 获取索引性能指标,并可以在节点,索引或分片级别进行汇总。

      "merges" : {
              "current" : 0,
              "current_docs" : 0,
              "current_size_in_bytes" : 0,
              "total" : 245,
              "total_time_in_millis" : 58332,
              "total_docs" : 1351279,
              "total_size_in_bytes" : 640703378,
              "total_stopped_time_in_millis" : 0,
              "total_throttled_time_in_millis" : 0,
              "total_auto_throttle_in_bytes" : 2663383040
            },
            "refresh" : {
              "total" : 2955,
              "total_time_in_millis" : 244217,
              "listeners" : 0
            },
            "flush" : {
              "total" : 127,
              "periodic" : 0,
              "total_time_in_millis" : 13137
            },

    索引性能维度相关重要指标

    • refresh.total:总刷新计数。刷新总数的计数。

    • refresh.total_time_in_millis:刷新总时间。汇总所有花在刷新的时间(以毫秒为单位进行测量)。

    • merges.current_docs:目前的合并。合并目前正在处理中。

    • merges.total_docs:合并总数。合并总数的计数。

    • merges.total_stopped_time_in_millis。合并花费的总时间。合并段的所有时间的聚合。

    4、节点运行状况维度:内存,磁盘和CPU指标

    每个节点都运行物理硬件上,需要访问系统内存,磁盘存储和CPU周期,以便管理其控制下的数据并响应对集群的请求。

    Elasticsearch是一个严重依赖内存 以实现性能的系统,因此密切关注内存使用情况与每个节点的运行状况和性能相关。改进指标的相关配置更改也可能会对内存分配和使用产生负面影响,因此记住从整体上查看系统运行状况非常重要。

    监视节点的CPU使用情况并查找峰值有助于识别节点中的低效进程或潜在问题。CPU性能与Java虚拟机(JVM)的垃圾收集过程密切相关。

    磁盘高读写可能导致系统性能问题。由于访问磁盘在时间上是一个“昂贵”的过程,因此应尽可能减少磁盘I/O。

    通过如下命令行可以实现节点级别度量指标,并反映运行它的实例或计算机的性能。

    GET /_cat/nodes?v&h=id,disk.total,disk.used,disk.avail,disk.used_percent,ram.current,ram.percent,ram.max,cpu
    id   disk.total disk.used disk.avail disk.used_percent ram.current ram.percent ram.max cpu
    Hk9w    931.3gb   472.5gb    458.8gb             50.73       6.1gb          78   7.8gb  14

    节点运行的重要指标

    • disk.total :总磁盘容量。节点主机上的总磁盘容量。

    • disk.used:总磁盘使用量。节点主机上的磁盘使用总量。

    • avail disk:可用磁盘空间总量。

    • disk.avail disk.used_percent:使用的磁盘百分比。已使用的磁盘百分比。

    • ram:当前的RAM使用情况。当前内存使用量(测量单位)。

    • percent ram:RAM百分比。正在使用的内存百分比。

    • max : 最大RAM。 节点主机上的内存总量

    • cpu:中央处理器。正在使用的CPU百分比。
      实际业务场景中推荐使用:Elastic-HQ, cerebro监控。

      图片

    5、JVM运行状况维度:堆,GC和池大小(Pool Size)

    作为基于Java的应用程序,Elasticsearch在Java虚拟机(JVM)中运行。JVM在其“堆”分配中管理其内存,并通过garbage collection进行垃圾回收处理。

    如果应用程序的需求超过堆的容量,则应用程序开始强制使用连接的存储介质上的交换空间。虽然这可以防止系统崩溃,但它可能会对集群的性能造成严重破坏。监视可用堆空间以确保系统具有足够的容量对于集群的健康至关重要。

    JVM内存分配给不同的内存池。您需要密切注意这些池中的每个池,以确保它们得到充分利用并且没有被超限利用的风险。

    垃圾收集器(GC)很像物理垃圾收集服务。我们希望让它定期运行,并确保系统不会让它过载。理想情况下,GC性能视图应类似均衡波浪线大小的常规执行。尖峰和异常可以成为更深层次问题的指标。

    可以通过GET /_nodes/stats 命令检索JVM度量标准。

      "jvm" : {
            "timestamp" : 1557588707194,
            "uptime_in_millis" : 22970151,
            "mem" : {
              "heap_used_in_bytes" : 843509048,
              "heap_used_percent" : 40,
              "heap_committed_in_bytes" : 2077753344,
              "heap_max_in_bytes" : 2077753344,
              "non_heap_used_in_bytes" : 156752056,
              "non_heap_committed_in_bytes" : 167890944,
              "pools" : {
                "young" : {
                  "used_in_bytes" : 415298464,
                  "max_in_bytes" : 558432256,
                  "peak_used_in_bytes" : 558432256,
                  "peak_max_in_bytes" : 558432256
                },
                "survivor" : {
                  "used_in_bytes" : 12178632,
                  "max_in_bytes" : 69730304,
                  "peak_used_in_bytes" : 69730304,
                  "peak_max_in_bytes" : 69730304
                },
                "old" : {
                  "used_in_bytes" : 416031952,
                  "max_in_bytes" : 1449590784,
                  "peak_used_in_bytes" : 416031952,
                  "peak_max_in_bytes" : 1449590784
                }
              }
            },
            "threads" : {
              "count" : 116,
              "peak_count" : 119
            },
            "gc" : {
              "collectors" : {
                "young" : {
                  "collection_count" : 260,
                  "collection_time_in_millis" : 3463
                },
                "old" : {
                  "collection_count" : 2,
                  "collection_time_in_millis" : 125
                }
              }
            },

    JVM运行的重要指标如下

    • mem:内存使用情况。堆和非堆进程和池的使用情况统计信息。

    • threads:当前使用的线程和最大数量。

    • gc:垃圾收集。算和垃圾收集所花费的总时间。

    6、ElasticsearchTop10监控指标

    经过上面的分析,Top10监控指标如下。使用英文是为了命令行返回一致,更好理解。

    1. Cluster Health – Nodes and Shards

    2. Search Performance – Request Latency and

    3. Search Performance – Request Rate

    4. Indexing Performance – Refresh Times

    5. Indexing Performance – Merge Times

    6. Node Health – Memory Usage

    7. Node Health – Disk I/O

    8. Node Health – CPU

    9. JVM Health  – Heap Usage and Garbage Collection

    10. JVM health – JVM Pool Size

    在监控Elasticsearch集群时,很难对每个关注领域做出公正的判断。不同指标之间的紧密耦合以及了解配置变化如何影响每个指标需要一支经验丰富且训练有素的工程师团队。

    对于将Elasticsearch作为解决方案的任何公司而言,投资全面的监控策略至关重要。有效的监控可以节省公司因非响应或无法修复的集群问题而导致的停机时间成本和经济成本

     

     

     

    0、背景

    Elasticsearch 具有一组丰富的易于理解的 REST API,这些 API  均可如下几种方式通过 HTTP 调用进行访问。

    • Curl
    • Postman
    • head 插件
    • cerebro 工具
    • kibana

    开发实战环节,我推荐使用:kibana Dev-tools。

    图片

    原因如下:

    • 有提示功能
    • 方便快捷、效率高
    • 不容易出错

    本文结合多年实战经验和网络资源,梳理出开发环节最重要的命令清单,希望对你有帮助!

    1、Elasticsearch REST URL 拆解

    1.1 REST URL和选项概述

    举个栗子:

    POST products/_search?size=0
    {
      "aggs": {
        "product_aggs": {
          "terms": {
            "field":"name.keyword"
          }
        }
      }
    }

    调用REST API很容易,除了实际的主机名/ IP和端口外,它还包含四个部分:

    • 动词——REST调用的 GET,PUT,POST和DELETE部分。

    在Kibana中,您可以直接指定这些名称,对于cURL,请使用-X选项。

    对应示例中的:POST

    • 路径——API和路径。

    例如:/_cluster / health 或 /logstash-cron-2020.07.03/_mapping-路径的第一部分通常是索引名称,除非它以_开头。

    对应示例中的:products/_search,其中products是索引。

    • 参数——后面的各种选项?

    例如?h或?v

    对应示例中的:?size=0。参数设定部分。

    • 正文——某些调用需要JSON正文(例如设置选项),并将包含在{}中

    对应示例中的:检索语句部分。

    1.2 常用选项

    有一些通用选项适用于许多(不是全部)URL。这些是:

    • ?help——帮助选项。

    将在列表中提供 API 可用的字段,其中包含短名称和长名称、说明等。

    举例:

    GET _cat/indices?help
    • ?h =-“ h”——使用上方“帮助”显示中的短名称或长名称指定要包括在结果中的字段。这些用逗号分隔,没有空格。

    举例:

    GET _cat/indices?h=docs.count,store.size

    这样以后返回结果一头雾水,相当于 Excel 有 N 列字段,但没有表头一样,很痛苦!

    • ?v——'v'在回复的顶部包括字段名称。
    GET _cat/indices?h=docs.count,store.size&v

    -v 参数的妙处主要体现在:加上了表头,直观交互呈现。

    • ?s——'s'用于排序,使用列出的字段作为排序键。

    如下所示:我们可能会看到节点列表。包括:返回字段名称,字段名称要显示并按名称name排序:

    GET /_cat/nodes?v&h=heap.percent,diskUsedPercent,cpu,master,name&s=name

    另外,Kibana 会将您的 Dev Tools 查询保存在 cookie 中。

    有了上面的背景知识,下面的常见开发相关的常用命令清单看起来就相对容易了。

    2、状态 & 统计相关命令清单

    最有用的 API 调用通常与集群的运行状况,状态和统计信息有关,例如:

    2.1 获取版本和集群名称等信息。

    GET / 

    2.2 获取集群健康状态等信息,包括集群名称、节点数、数据节点数、分片等的一些统计信息。

    GET /_cluster/health

    2.3 获取节点列表信息。显示了堆内存、磁盘使用情况,CPU 、负载和主机角色。

    用途:用来监视负载和磁盘使用情况以及主机角色。

    GET /_cat/nodes?v&h=heap.percent,diskUsedPercent,cpu,load_1m,master,name&s=name

    2.4 Index Level 索引层面健康

    GET /_cluster/health?level=indices&pretty

    2.5 Shard Level 分片层面健康

    GET /_cluster/health?level=shards&pretty

    2.6 获取索引,文档,缓存,段,节点等的集群统计信息的更深入概述。

    用途:有助于基本故障排除。

    GET /_cluster/stats 

    2.7 获取节点级别的更多统计信息,包括堆使用情况等。

    GET /_nodes/stats 

    3、线程相关状态&统计命令清单

    3.1 很长但有用的线程队列视图

    GET /_cat/thread_pool?v&h=node_name,name,type,active,size,queue,queue_size,rejected,largest,completed,min,max&s=node_name,name

    3.2 查看热点线程,用于排查谁在占用CPU资源等

    GET /_nodes/hot_threads/ 

    3.3 快速获取索引模式。

    例如:所有这些logstash *前缀索引的文档总数的方法。

    GET /_cat/count/logstash*?v

    4、索引相关状态&统计命令清单

    4.1 通配符索引列表获取

    包含:大小,文档计数,状态等。

    GET /_cat/indices/logstash*?v

    4.2 找到size 最大的索引

    GET /_cat/indices/logstash-*?v&h=index,ss&s=ss:desc

    4.3 获取状态为黄色的索引

    GET /_cat/indices?v&health=yellow
    • 红色 red:至少一个主分片不可用。
    • 黄色 yellow:至少一个副本分片不可用。
    • 绿色 green:集群主、副本分片都可用,集群健康。

    5、 设置相关清单

    Elasticsearch中有许多设置,但最常见的是“集群”和“索引”级别。

    5.1  集群设置

    最基本的集群设置视图——显示非默认的持久性和瞬态设置信息。

    GET /_cluster/settings

    更大的列表视图——包括所有默认值,并使用平面视图使其更易于阅读。

    GET /_cluster/settings?include_defaults=true&flat_settings=true

    5.2 索引设置

    获取指定索引的设置列表信息。

    GET /logstash-cron-2020.08.03/_settings 

    获取索引的元信息、设置信息以及Mapping信息。

    GET /logstash-cron-2020.08.03 

    6、 关闭&删除索引

    6.1 关闭索引

    支持单个或者模糊匹配多个索引。

    POST /logstash-*-2020.03.*/_close 

    6.2 删除索引

    支持单个或者模糊匹配多个

    DELETE /logstash-*-2020.04.* 

    7、故障排查清单

    之前运维清单有过陈述,开发也需要。

    7.1 获取:已分配和未分配的分片。

    GET /_cat/shards?v&h=n,index,shard,prirep,state,sto,sc,unassigned.reason,unassigned.details&s=sto,index

    7.2 查看未分配的分片及原因

    GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state 

    7.3 查看未分配的细节说明信息

    GET /_cluster/allocation/explain

    7.4 查看指定分片未分配的细节

    GET /_cluster/alloGET /_cluster/allocation/explain 
    { "index": "logstash-cloudtrail-2019.10.16", "shard": 0, "primary": true} cation/explain { "index": "logstash-cloudtrail-2019.10.16", "shard": 0, "primary": true} 

    7.5 下线节点

    PUT _cluster/settings?pretty
    {
      "transient": {
        "cluster.routing.allocation.exclude._ip": "10.0.0.1"
      }
    }

    8、模板清单

    8.1 列举已定义的所有模板

    GET /_cat/templates?v&s=order,name

    8.2 列举指定名称的模板信息

    GET /_template/logstash

    9、快照&恢复

    9.1 列出系统中配置的快照存储库。大多数其他API调用都需要这些名称。

    GET /_snapshot 

    9.2 获取任何正在运行的快照的状态和统计信息

    GET /_snapshot/_status 

    9.3 获取快照的有用信息

    GET /_cat/snapshots/my_repository?v&h=id,status,start_time,duration,indicies,successful_shards,failed_shards,total_shards

    10、 分片分配与恢复清单

    10.1 分片分配查看

    GET /_cat/allocation?v

    10.2 分片恢复查看

    GET /_cat/recovery?v

    10.3 运行任务查看

    GET /_cat/pending_tasks?v

    10.4 清理缓存

    POST /_cache/clear

    小结

    几个月以来,我一直在记录自己开发Elasticsearch应用程序的最佳实践。本文梳理的内容试图传达Java的某些思想,我相信其同样适用于其他编程语言。我尝试尽量避免重复教程和Elasticsearch官方文档中已经介绍的内容。本文梳理的内容都是从线上实践问题和个人总结的经验汇总得来的。

    文章从以下几个维度展开讲解:

    • 映射(Mapping)
    • 设置(Setting)
    • 查询方式(Querying)
    • 实战技巧(Strategy)

    1、映射(Mapping)

    1.1 避免使用nested类型

    每个Elasticsearch文档都对应一个Lucene文档。

    nested类型是个例外,对于nested类型,每个字段都作为单独的文档存储与父Lucene的关联。

    其影响是:

    • nested与父文档中的字段相比,查询字段的速度较慢
    • 检索匹配nested字段会降低检索速度
    • 一旦更新了包含nested字段的文档的任何字段(与是否更新嵌套字段无关,则所有基础Lucene文档(父级及其所有nested子级)都需要标记为已删除并重写)。除了降低更新速度外,此类操作还会产生大量垃圾文件,直到通过段合才能进行清理。

    在某些情况下,你可以将nested字段展平。

    例如,给定以下文档:

    {
    "attributes": [
    {"key": "color", "val": "green"},
    {"key": "color", "val": "blue"},
    {"key": "size", "val": "medium"}
    ]
    }

    展平如下:

    {
    "attributes": {
    "color": ["green", "blue"],
    "size": "medium"
    }
    }

    1.2 Mapping设置strict

    实际业务中,如果不明确设定字段类型,Elasticsearch有动态映射机制,会根据插入数据自动匹配对应的类型。

    假定:本来准备插入浮点型数据,但由于第一个插入数据为整形,Elasticsearch 自定会判定为long类型,虽然后续数据也能写入,但很明显“浮点类型”只阉割保留了整形部分。

    铭毅给个demo一探究竟:

    POST my_index03/_doc/1
    {
    "tvalue":35
    }

    POST my_index03/_doc/2
    {
    "tvalue":3.1415
    }

    GET my_index03/_mapping

    GET my_index03/_search
    {
    "query": {
    "term": {
    "tvalue": {
    "value": 3.1415
    }
    }
    }
    }

    注意:term查询是不会返回结果的。

    所以,实战环境中,Mapping设定要注意如下节点:

    • 显示的指定字段类型
    • 尽量避免使用动态模板(dynamic-templates)
    • 禁用日期检测 (date_detection),默认情况下处于启用状态。“strict”实践举例:
    PUT my_index
    {
    "mappings": {
    "dynamic": "strict",
    "properties": {
    "user": {
    "properties": {
    "name": {
    "type": "text"
    },
    "social_networks": {
    "dynamic": "strict",
    "properties": {
    "network_id": {
    "type": "keyword"
    },
    "network_name": {
    "type": "keyword"
    }
    }
    }
    }
    }
    }
    }
    }

    1.3 合理的设置string类型

    Elasticsearch5.X 之后,String 被分成两种类型,text和keyword。两者的区别:

    • text:适用分词全文检索场景
    • keyword:适用字符串的精准匹配场景

    默认,如果不显示指定字段类型,字符串类型自定映射后的Mapping如下所示:

    "cont" : {
    "type" : "text",
    "fields" : {
    "keyword" : {
    "type" : "keyword",
    "ignore_above" : 256
    }
    }

    而公司实战的业务场景,通常会面临:

    • 需不需要分词,不需要的话仅保留keyword即可。
    • 需要用什么分词?英文分词还是中文分词?
    • 分词后是否还需要排序和聚合,即fielddata是否需要开启
    • 是否需要精准匹配,即是否需要保留keyword

    所以,回答了如上几个问题,再有针对的显示设定string类型的Mapping方为上策!

    2、设置(Setting)

    在这里,我分享了Elasticsearch集群 设置 相关的技巧。

    2.1 避免过度分片

    分片是Elasticsearch的最大优势之一,即将数据分散到多个节点以实施并行化。关于这个主题有过很多讨论。

    但请注意,索引的主分片一旦设置便无法更改(除非重建索引或者reindex)。

    对于新来者来说,过度分片是一个非常普遍的陷阱。在做出任何决定之前,请确保先通读官方的这篇博文:

    我在 Elasticsearch 集群内应该设置多少个分片?

    https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster

    铭毅提示:

    主分片数过多:

    • 批量写入或者查询请求被分割成过多的子写入、子查询,导致索引的写入、查询拒绝率上升。

    主分片数过少:

    • 尤其对于数据量非常庞大的索引,若分片数过少或者就1个分片,会导致无法利用集群多节点资源(也就是分布式特性),造成资源利用率不高或者不均衡,影响写入或者查询效率。
    • 并且,一旦该大的主分片出现问题,恢复起来耗时会非常长。

    2.2 取消学习任何段合并的技巧

    从本质上讲,Elasticsearch是另一种分布式 Lucene产品,就像Solr一样 。在底层,大多数时候,每个Elasticsearch文档都对应一个Lucene文档(nested除外,如1.1所述)。在Lucene中,文档存储在 segment中。后台的Elasticsearch通过以下两种模式连续维护这些Lucene段:

    • 在Lucene中,当你删除或更新文档时,旧文档被标记为已删除,而新文档被创建。Elasticsearch会跟踪这些标记为deleted的文档,适时对其段合并。
    • 新添加的文档可能会产生大小不平衡的段。Elasticsearch可能会出于优化目的而决定将它们合并为更大的段。

    图片

    实战中一定要注意:段合并是高度受磁盘I / O和CPU约束的操作。

    作为用户,我们不想让段合并破坏Elasticsearch的查询性能。

    事实上,在某些情况下可以完全避免使用它们:一次构建索引,不再更改它。尽管在许多应用场景中可能很难满足此条件。一旦开始插入新文档或更新现有文档,段合并就成为不可避免的一部分。

    正在进行的段合并可能会严重破坏集群的总体查询性能。在Google上进行随机搜索,你会发现许多人发帖求助求助:“在段合并中减少对性能的影响的配置“,还有许多人共享某些适用于他们的配置。但,很多配置都是早期1.x,2.X版本的设置,新版本已经废弃。

    综上,我进行段合并的经验法则如下:

    • 取消学习任何段合并的技巧。早期版本的段合并配置是与Elasticsearch的内部紧密耦合的操作,新版本一般不再兼容。几乎没有“神秘”的底层配置修改可以使它运行得更快。
    • 找到translog flush 的最优配置 。尝试调整index.translog.sync_interval和index.translog.flush_threshold_size设置。

    详见:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html

    • 动态调整index.refresh_interval以满足业务需求。如果实时性要求不高,可以调大刷新频率(默认是1s,可以调到30s甚至更大)。
    PUT /twitter/_settings
    {
    "index" : {
    "refresh_interval" : "30s"
    }
    }

    2.3 注意JVM内存设置

    Elasticsearch可以根据两个主要内存设置产生引人注目的性能特征:

    • JVM堆空间——主要用途:缓存(节点缓存、分片请求缓存、Field data缓存以及索引缓存)
    • 堆外内存空间——lucene段文件缓存

    图片

    提醒你不要根据过去的非Elasticsearch JVM应用程序经验来盲目设置Elasticsearch JVM堆大小。

    详见官方文档:

    https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html

    3、查询方式(Querying)

    下面我收集了一些技巧,你可以在Elasticsearch查询时使用它们。

    3.1 Elasticseach里面多线程修改如何保证数据准确性?

    • 1,用如下两个参数校验冲突
    PUT products/_doc/1?if_seq_no=1&if_primary_term=1
    { "title":"iphone", "count":100 }
    • 2,用version避免冲突
    PUT products/_doc/1?version=30000&version_type=external
    { "title":"iphone", "count":100 }

    3.2 尝试分割复杂的查询,并行执行提升性能

    如果你同时具有过滤器和聚合组件的复杂查询,则在大多数情况下,可以将它们拆分为多个查询并并行执行它们可以提高查询性能。

    也就是说,在第一个查询中,仅使用过滤器获取匹配,然后在第二个查询中,仅获取聚合结果而无需再获取检索结果,即size: 0。

    3.3 了解你的数字类型,防止被优化导致精度损失

    许多JSON解析器可以进行各种优化,以提供有效的读/写性能。但可能造成了精度的损失,所以在选型Jackson json解析器时:优先使用BigDecimal和BigInteger。

    3.4 不要使用Elasticsearch Transport / Node客户端

    TransportClient可以支持2.x,5.x版本,TransportClient将会在Elasticsearch 7.X版本弃用并在8.X版本中完成删除. 

    官方推荐使用Java High Level REST Client,它使用HTTP请求而不是Java序列化请求。为了安全起见,坚持使用HTTP上的JSON格式,而不使用 SMILE (二进制格式)。

    3.5 使用官方的Elasticsearch High-level REST客户端

    非官方客户端一般更新太慢,几乎无法跟上Elasticsearch新版本的特性,如:Jest客户端近一年几乎没有更新,只支持到6.X版本。

    相比之下,官方REST客户端仍然是你相对最好的选择。https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/index.html

    3.6 不要使用HTTP缓存来缓存Elasticsearch响应结果

    由于便利性和低进入门槛,许多人陷入了将HTTP缓存(例如Varnish http://varnish-cache.org/)置于Elasticsearch集群前面的陷阱。使用HTTP缓存缺点如下:

    在生产环境中使用Elasticsearch时,由于各种原因如:弹性扩展、测试和线上环境分离、零停机升级等,你很有可能最终会拥有多个集群。(1)一旦为每个集群提供专用的HTTP缓存,99%的缓存内容是重复的。(2)如果你决定对所有集群使用单个HTTP缓存,那么很难以编程方式配置HTTP缓存以适应不断变化的集群状态的需求。

    1. 如何传达集群负载以使缓存平衡流量?

    2. 如何配置计划内或手动停机时间?

    3. 在维护时段期间,如何使缓存逐渐从一个集群迁移到另一个集群?

    这些都是亟待考虑的问题。如上所述,HTTP缓存很难以编程方式进行实现。当你需要手动删除一个或多个条目时,它并不总是像DELETE FROM cache WHERE keys IN (...)查询那样容易。还得通过手动实现。

    铭毅提示:这一条我实际没有用过,有用过的童鞋可以留言讨论。

    3.7 使用基于_doc排序的slice scroll 遍历数据

    Scrolls 是Elasticsearch提供的一种遍历工具,用来扫描整个数据集以获取大量甚至全量数据。它在功能上及内部实现上与RDBMS游标非常相似。但是,大多数人在第一次尝试中都没有使正确他。以下是一些基本知识:

    • 如果你接触到scrolls,你可能正在读取大量数据。slicing 很可能会帮助你显著提高读取性能。
    • 使用_doc进行排序,读取速度就会提高20%+,而无需进行其他任何更改。(_doc是一个伪字段)
    • scrollId调用之后会有变化。因此,请确保你始终使用最新检索的滚动scrollId。
    • 在Reindex的时候使用slicing 也能提升索引数据迁移效率。

    3.8 单文档检索 优先使用 GET /index/type/{id}而非POST /index/_search

    Elasticsearch使用不同的线程池来处理 GET /index/type/{id}和 POST /index/_search查询。

    使用POST /index/_search与有效载荷{query: {"match": {"_id": "123"}}}(或类似的东西)占据搜索专用线程池。

    在高负载下,这将同时降低搜索和单个文档的获取性能。

    所以,单文档坚持使用:GET /index/type/{id}。

    3.9 使用size: 0和includes/ excludes限定字段返回

    Elasticsearch在添加size: 0子句前后会带来显著的性能差异 。

    除非业务需要,才返回必要字段,无需返回的字段通过includes和excludes控制。

    3.10 提前做好压力测试,了解系统支持的上限

    分享我的个人最佳实践:

    • 使用应用程序的性能基准( performance benchmarks)测试来估计应用程序能提供支持的性能负载上限。

      如基于esrally测试。

    • 避免将线程池与无限制的任务队列一起使用。

      队列的过度增长会对内存增加压力。

    • 如果你的应用程序是借助第三方引擎中转或写入数据(例如,从kafka队列到Elasticsearch集群写入数据),请确保你的生产者对消费者的压力做出反应。

      也就是说,如果消费者延迟开始增加,则最好开始降低生产者的速度。

    图片

    3.11 在查询中提供明确的超时

    几乎所有的Elasticsearch API都允许用户指定超时。

    找出并摆脱耗时长的操作,节省相关资源,建立稳定的服务,这将对你的应用程序和Elasticsearch集群都有帮助。

    3.12 不要使用注入变量的JSON模板

    永远不要这样做:

    {
    "query": {
    "bool": {
    "filter": [
    {
    "term": {
    "username": {
    "value": {{username}}
    }
    }
    },
    {
    "term": {
    "password": {
    "password": {{password}}
    }
    }
    },
    ]
    }
    }
    }

    防止SQL注入,只要有人通过恶意username 和password输入,将暴露你的整个数据集,这只是时间问题。

    我建议使用两种安全的方法来生成动态查询:

    • 使用Elasticsearch官方客户端提供的查询模型。(这在Java上效果很好。)
    • 使用JSON库(例如Jackson)构建JSON树并将其序列化为JSON。

    4、实战技巧(Strategy)

    在最后一节中,我收集了解决上述未解决问题的便捷的实战技巧。

    4.1  始终(尝试)坚持使用最新的JVM和ES版本

    Elasticsearch是一个Java应用程序。像其他所有Java应用程序一样,它也有hot paths和垃圾回收问题。几乎每个新的JVM版本都会带来很多优化,你可以不费吹灰之力利用这些优化。

    Elasticsearch有一个官方页面,列出了支持的JVM版本和垃圾收集器。在尝试任何JVM升级之前,请务必先翻一翻如下文章清单:

    https://www.elastic.co/guide/en/elasticsearch/guide/current/_don_8217_t_touch_these_settings.html

    https://www.elastic.co/cn/support/matrix#matrix_jvm

    注意:Elasticsearch升级也是免费获得性能提升的来源。

    4.2 使用Elasticsearch完整和部分快照进行备份

    Elasticsearch可以便捷的实现全部索引的全量快照或者部分索引数据的增量快照。

    根据你的更新模式和索引大小,找到适合你的用例的快照最佳组合。

    也就是说,例如,在00:00时有1个完整快照,在06:00、12:00和18:00时有3个局部增量快照。将它们存储在第三方存储也是一种好习惯。

    有一些第三方 插件 可以简化这些情况。

    举例:https://www.elastic.co/guide/en/elasticsearch/plugins/master/repository.html

    与每份备份方案一样,安全起见,请确保快照可以还原并反复练习几次。

    4.3 有一个持续的性能测试平台

    像任何其他数据库一样,Elasticsearch在不同条件下显示不同的性能:

    • 索引,文档大小;
    • 更新,查询/检索模式;
    • 索引,集群设置;
    • 硬件,操作系统,JVM版本等。

    很难跟踪每个设置的改变以观察其对整体性能的影响。确保你(至少)进行每日性能测试,以帮助缩小范围,快速定位最近引入的、导致性能下降的可能的原因。

    这种性能测试说起来容易做起来难。你需要确保测试环境:

    • 能有代表性的生产环境数据
    • 配置和生产环境一致
    • 完全覆盖用例
    • 考虑包括操作系统缓存的测试的影响。

    4.4 使用别名

    告诉你一些颇有见地的实操经验:永远不要查询索引,而要查询 别名。

    别名是指向实际索引的指针。你可以将一个或多个索引归为一个别名。

    许多Elasticsearch索引在索引名称上都有内部上下文,例如events-20190515 代表20190515这一天的数据。

    现在,在查询events-*索引时,应用程序代码中有两个选择:

    • 选择1:通过特定日期格式即时确定索引名称:events-YYYYMMDD。

    这种方法有两个主要缺点:

    (1)需要回退到特定日期的索引,因此需要对整个代码库进行相应的设计以支持这种操作。

    (2)撇开所有时钟同步问题,在凌晨,你需要用程序或者脚本控制索引切换,确保数据写入下一天索引。

    • 选择2:创建一个events别名,指向events-*相关的索引。负责创建新索引的组件如:curator或者ILM(索引生命周期管理)可以自动将别名切换到新索引。

    这种方法将带来两个明显的好处:

    (1)它没有以前方法的缺点。

    (2)只需指向events 别名,代码就会更简洁。

    4.5 避免拥有大量同义词

    Elasticsearch支持索引阶段和查询阶段指定 同义词。

    没有同义词,搜索引擎是不完整的,但实战使用环境,注意如下问题:

    • 索引阶段同义词增加了索引大小,并增加了运行时开销。
    • 查询阶段同义词不会增加索引的大小,但顾名思义,这会增加运行时开销。
    • 使用同义词,很容易在尝试修复其他问题时无意间破坏某些其他内容。

    所以,要持续监视同义词对性能的影响,并尝试为添加的每个同义词编写测试用例。

    同义词官方文档:

    https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html

    4.6 在启用副本之前强制段合并及增加带宽

    一个非常常见的Elasticsearch用例是:定期(每两小时一次)创建一个索引。

    关于如何实现最佳性能,SoundCloud上有一篇非常不错的 文章。从该文中引用,我特别发现以下几项“必须”。

    • 在完成索引创建后,务必启用副本。

    • 在启用副本之前,请确保:

    (1)通过强制合并来缩小索引大小;

    POST /twitter/_forcemerge

    (2)临时增加副本传输带宽,直到分配完成为止 indices.recovery.max_bytes_per_sec。默认:40mb,该属性允许用户在恢复过程中控制网络的流量。设置一个比较大的值会导致网络变得繁忙,当然恢复过程也会加快。可以通过如下方式调整:

    PUT /_cluster/settings
    {
    "transient": {
    "indices.recovery.max_bytes_per_sec": "50mb"
    }
    }

    推荐阅读:

    https://developers.soundcloud.com/blog/how-to-reindex-1-billion-documents-in-1-hour-at-soundcloud

    4.7 记录应用程序级别指标

    Kibana对Elasticsearch性能提供了多维监控指标仪表盘:

    图片

    • indexing,
    • search latency and throughput,
    • flush
    • merge operations
    • GC pauses
    • heap size
    • OS (CPU usage, disk I/O
    • kernel caches 等......

    但,这还不够。如果由多个应用程序使用,Elasticsearch将受到各种访问模式的影响。

    想象一下,你的应用程序A试图删除1000万个不太重要的用户文档,而另一个组件B试图更新用户帐户详细信息。

    如果你查看Elasticsearch监控指标,一切都是绿色正常。

    但是,此时更新账户的用户可能不满意他们尝试更新帐户时的延迟。

    因此,始终为你的Elasticsearch查询提供额外的应用程序级指标。

    尽管Elasticsearch结合kibana或者cerbro已经为整体集群性能提供了足够的指标,但它们缺乏特定于操作的上下文监控,需要结合实际业务特事特办。

    4.8 重视CPU的配置选型和使用率监控

    怎么强调CPU都不过分。

    从我过去的经验来看,无论是写负载还是读负载场景,CPU一直是我们的瓶颈。

    4.9 谨慎编写自定义的Elasticsearch插件

    • 许多Elasticsearch版本包含重大的内部更改。你的插件所基于的公共API很可能会向后不兼容。
    • 你需要调整部署过程,不能再使用原始的Elasticsearch工作。
    • 由于你的应用程序依赖于于插件提供的特定功能,因此在集成测试过程中运行的Elasticsearch实例也需要包含插件。你也就不能再使用原始的Docker镜像。

    参考:https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247487427&idx=1&sn=a6b416d8a5a8a7589e0b85ee16759017&chksm=eaa827ebdddfaefd4722fd3d1c2224fd784d271845acdbb297cf00acfbe28562773e3076d029&scene=132#wechat_redirect

    QQ群 247823727 博客文件如果不能下载请进群下载
    如果公司项目有技术瓶颈问题,如有需要,请联系我,提供技术服务 QQ: 903464207
  • 相关阅读:
    单例模式
    二、CSS
    十一、多线程
    十二、协程
    十、多进程
    九、内存管理
    八、元类
    七、上下文管理器/魔术方法
    六、单例模式
    五、装饰器
  • 原文地址:https://www.cnblogs.com/zx-admin/p/15346229.html
Copyright © 2020-2023  润新知