Elasticsearch:对时间序列数据流进行降采样(downsampling)

本文主要是介绍Elasticsearch:对时间序列数据流进行降采样(downsampling),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

降采样提供了一种通过以降低的粒度存储时间序列数据来减少时间序列数据占用的方法。

指标(metrics)解决方案收集大量随时间增长的时间序列数据。 随着数据老化,它与系统当前状态的相关性越来越小。 降采样过程将固定时间间隔内的文档汇总为单个摘要文档。 每个摘要文档都包含原始数据的统计表示:每个指标的最小值(min)最大值 (max)总和 (sum)值计数 (value_count) 和平均值 (average)。 数据流时间序列维度存储不变。

实际上,降采样可以让你用数据分辨率和精度来换取存储大小。 你可以将其包含在索引生命周期管理 (ILM) 策略中,以自动管理指标数据的数量和相关成本。

它是如何工作的?

时间序列是特定实体随时间推移的一系列观察结果。 观察到的样本可以表示为连续函数,其中时间序列维度保持不变,时间序列指标随时间变化。

在 Elasticsearch 索引中,会为每个时间戳创建一个文档,其中包含不可变的时间序列维度以及指标名称和变化的指标值。 对于单个时间戳,可以存储多个时间序列维度和指标。

对于最新的相关数据,指标系列通常具有较低的采样时间间隔,因此它针对需要高数据分辨率的查询进行了优化。

图 2. 原始指标系列

降采样通过用更高采样间隔的数据流和该数据的统计表示替换原始时间序列来处理较旧的、访问频率较低的数据。 在原始指标样本可能已采集的情况下,例如每十秒采集一次,随着数据老化,你可以选择将样本粒度减少到每小时或每天。 你可以选择将冷归档数据的粒度减少到每月或更小。

图 3. 降采样指标系列

对时间序列数据运行降采样

要对时间序列索引进行降采样,请使用 Downsample API 并将 fixed_interval 设置为你想要的粒度级别:

POST /my-time-series-index/_downsample/my-downsampled-time-series-index
{"fixed_interval": "1d"
}

要将时间序列数据降采样作为 ILM 的一部分,请在 ILM 策略中包含降采样操作,并将 fixed_interval 设置为你想要的粒度级别:

PUT _ilm/policy/my_policy
{"policy": {"phases": {"warm": {"actions": {"downsample" : {"fixed_interval": "1h"}}}}}
}

查询降采样索引

你可以使用 _search 和 _async_search 端点来查询降采样索引。 可以在单个请求中查询多个原始数据和降采样索引,并且单个请求可以包括不同粒度(不同桶时间跨度)的降采样索引。 也就是说,你可以查询包含具有多个降采样间隔(例如15m、1h、1d)的降采样索引的数据流。

基于时间的直方图聚合的结果采用统一的桶大小,并且每个降采样索引返回忽略降采样时间间隔的数据。 例如,如果你对已按每小时分辨率 ( "fixed_interval": "1h") 降采样的降采样索引运行带有 "fixed_interval": "1m" 的 date_histogram 聚合,则查询将返回一个存储桶,其中包含位于 第 0 分钟,然后是 59 个空桶,然后是下一小时内再次有数据的桶。

关于降采样查询的注意事项

查询降采样索引有几点需要注意:

  • 当你在 Kibana 中并通过 Elastic 解决方案运行查询时,会返回正常响应,而不会通知某些查询索引已被降采样。
  • 对于日期直方图聚合,仅支持 fixed_intervals(而不支持日历感知间隔)。
  • 仅支持协调世界时 (UTC) 日期时间。

限制和局限

以下限制和局限适用于降采样:

  • 仅支持时间序列数据流中的索引。
  • 仅根据时间维度对数据进行降采样。 所有其他维度都将复制到新索引而不进行任何修改。
  • 在数据流内,降采样索引替换原始索引,并且原始索引被删除。 给定时间段内只能存在一个索引。
  • 源索引必须处于只读模式才能成功进行降采样过程。 有关详细信息,请查看如下的手动运行降采样示例。
  • 支持对同一时段的数据进行多次降采样(降采样索引的降采样)。 降采样间隔必须是降采样索引间隔的倍数。
  • 降采样作为 ILM 操作提供。 请参阅降采样。
  • 新的降采样索引是在原始索引的数据层上创建的,并继承其设置(例如,分片和副本的数量)。
  • 支持 gauge 和 counter 指标类型。
  • 降采样配置是从时间序列数据流索引映射中提取的。 唯一需要的额外设置是降采样固定间隔。

手动运行降采样

对时间序列数据流 (TSDS) 进行降采样的推荐方法是通过索引生命周期管理 (ILM)。我们将在下面进行详述。 但是,如果你不使用 ILM,则可以手动对 TSDS 进行降采样。 本指南向你展示如何使用典型的 Kubernetes 集群监控数据。

前提条件

  • 请参阅 TSDS 先决条件。
    • 集群权限:manage_ilm 和 manage_index_templates。
    • 索引权限:你创建或转换的任何 TSDS 的 create_doc 和 create_index。 要滚动 TSDS,你必须具有 manage 权限。
  • 不可能直接对数据流进行降采样,也不可能一次对多个索引进行降采样。 只能对一个时间序列索引(TSDS 后备索引)进行降采样。
  • 为了对索引进行降采样,它需要是只读的。 对于 TSDS 写入索引,这意味着需要先滚动并使其变为只读。
  • 降采样使用 UTC 时间戳。
  • 降采样需要时间序列索引中至少存在一个指标字段。

创建时间序列数据流

首先,你将创建 TSDS。 为了简单起见,在时间序列映射中,所有 time_series_metric 参数都设置为 gauge 类型,但也可以使用其他值,例如 counter 和 histogram。 time_series_metric 值确定降采样期间使用的统计表示的类型。

索引模板包含一组静态时间序列维度:主机 (host)、命名空间 (namespace)、节点 (node) 和 Pod。 时间序列维度不会因降采样过程而改变。

PUT _index_template/my-data-stream-template
{"index_patterns": ["my-data-stream*"],"data_stream": {},"template": {"settings": {"index": {"mode": "time_series","routing_path": ["kubernetes.namespace","kubernetes.host","kubernetes.node","kubernetes.pod"],"number_of_replicas": 0,"number_of_shards": 2}},"mappings": {"properties": {"@timestamp": {"type": "date"},"kubernetes": {"properties": {"container": {"properties": {"cpu": {"properties": {"usage": {"properties": {"core": {"properties": {"ns": {"type": "long"}}},"limit": {"properties": {"pct": {"type": "float"}}},"nanocores": {"type": "long","time_series_metric": "gauge"},"node": {"properties": {"pct": {"type": "float"}}}}}}},"memory": {"properties": {"available": {"properties": {"bytes": {"type": "long","time_series_metric": "gauge"}}},"majorpagefaults": {"type": "long"},"pagefaults": {"type": "long","time_series_metric": "gauge"},"rss": {"properties": {"bytes": {"type": "long","time_series_metric": "gauge"}}},"usage": {"properties": {"bytes": {"type": "long","time_series_metric": "gauge"},"limit": {"properties": {"pct": {"type": "float"}}},"node": {"properties": {"pct": {"type": "float"}}}}},"workingset": {"properties": {"bytes": {"type": "long","time_series_metric": "gauge"}}}}},"name": {"type": "keyword"},"start_time": {"type": "date"}}},"host": {"type": "keyword","time_series_dimension": true},"namespace": {"type": "keyword","time_series_dimension": true},"node": {"type": "keyword","time_series_dimension": true},"pod": {"type": "keyword","time_series_dimension": true}}}}}}
}

摄取时间序列数据

由于时间序列数据流被设计为仅接受最近的数据,因此在本例中,你将使用摄取管道在数据被索引时对数据进行时移。 因此,索引数据将具有最近 15 分钟的 @timestamp。

使用此请求创建管道:

PUT _ingest/pipeline/my-timestamp-pipeline
{"description": "Shifts the @timestamp to the last 15 minutes","processors": [{"set": {"field": "ingest_time","value": "{{_ingest.timestamp}}"}},{"script": {"lang": "painless","source": """def delta = ChronoUnit.SECONDS.between(ZonedDateTime.parse("2022-06-21T15:49:00Z"),ZonedDateTime.parse(ctx["ingest_time"]));ctx["@timestamp"] = ZonedDateTime.parse(ctx["@timestamp"]).plus(delta,ChronoUnit.SECONDS).toString();"""}}]
}

接下来,使用批量 API 请求自动创建 TSDS 并为一组 10 个文档编制索引:

PUT /my-data-stream/_bulk?refresh&pipeline=my-timestamp-pipeline
{"create": {}}
{"@timestamp":"2022-06-21T15:49:00Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":91153,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":463314616},"usage":{"bytes":307007078,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":585236},"rss":{"bytes":102728},"pagefaults":120901,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:45:50Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":124501,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":982546514},"usage":{"bytes":360035574,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":1339884},"rss":{"bytes":381174},"pagefaults":178473,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:44:50Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":38907,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":862723768},"usage":{"bytes":379572388,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":431227},"rss":{"bytes":386580},"pagefaults":233166,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:44:40Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":86706,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":567160996},"usage":{"bytes":103266017,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":1724908},"rss":{"bytes":105431},"pagefaults":233166,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:44:00Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":150069,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":639054643},"usage":{"bytes":265142477,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":1786511},"rss":{"bytes":189235},"pagefaults":138172,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:42:40Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":82260,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":854735585},"usage":{"bytes":309798052,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":924058},"rss":{"bytes":110838},"pagefaults":259073,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:42:10Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":153404,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":279586406},"usage":{"bytes":214904955,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":1047265},"rss":{"bytes":91914},"pagefaults":302252,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:40:20Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":125613,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":822782853},"usage":{"bytes":100475044,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":2109932},"rss":{"bytes":278446},"pagefaults":74843,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:40:10Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":100046,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":567160996},"usage":{"bytes":362826547,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":1986724},"rss":{"bytes":402801},"pagefaults":296495,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}
{"create": {}}
{"@timestamp":"2022-06-21T15:38:30Z","kubernetes":{"host":"gke-apps-0","node":"gke-apps-0-0","pod":"gke-apps-0-0-0","container":{"cpu":{"usage":{"nanocores":40018,"core":{"ns":12828317850},"node":{"pct":2.77905e-05},"limit":{"pct":2.77905e-05}}},"memory":{"available":{"bytes":1062428344},"usage":{"bytes":265142477,"node":{"pct":0.01770037710617187},"limit":{"pct":9.923134671484496e-05}},"workingset":{"bytes":2294743},"rss":{"bytes":340623},"pagefaults":224530,"majorpagefaults":0},"start_time":"2021-03-30T07:59:06Z","name":"container-name-44"},"namespace":"namespace26"}}

你可以使用搜索 API 检查文档是否已正确索引:

GET /my-data-stream/_search

对数据运行以下聚合以计算一些有趣的统计数据:

GET /my-data-stream/_search
{"size": 0,"aggs": {"tsid": {"terms": {"field": "_tsid"},"aggs": {"over_time": {"date_histogram": {"field": "@timestamp","fixed_interval": "1d"},"aggs": {"min": {"min": {"field": "kubernetes.container.memory.usage.bytes"}},"max": {"max": {"field": "kubernetes.container.memory.usage.bytes"}},"avg": {"avg": {"field": "kubernetes.container.memory.usage.bytes"}}}}}}}
}

对 TSDS 进行降采样

TSDS 无法直接降采样。 你需要对其后备索引进行降采样。 你可以通过运行以下命令查看数据流的后备索引:

GET /_data_stream/my-data-stream

在对支持索引进行降采样之前,需要滚动 TSDS,并且需要将旧索引设为只读。

使用 rollver API 滚动 TSDS:

POST /my-data-stream/_rollover/

从响应中复制 old_index 的名称。 在以下步骤中,将索引名称替换为你的 old_index 的名称。

旧索引需要设置为只读模式。 运行以下请求:

PUT /.ds-my-data-stream-2023.11.30-000001/_block/write

接下来,使用 downsample API 对索引进行降采样,将时间序列间隔设置为一小时:

POST /.ds-my-data-stream-2023.11.30-000001/_downsample/.ds-my-data-stream-2023.11.30-000001-downsample
{"fixed_interval": "1h"
}

现在你可以修改数据流,并将原始索引替换为降采样后的索引:

POST _data_stream/_modify
{"actions": [{"remove_backing_index": {"data_stream": "my-data-stream","index": ".ds-my-data-stream-2023.11.30-000001"}},{"add_backing_index": {"data_stream": "my-data-stream","index": ".ds-my-data-stream-2023.11.30-000001-downsample"}}]
}

你现在可以删除旧的后备索引。 但请注意,这会删除原始数据。 如果将来可能需要原始数据,请不要删除索引。

查看结果

重新运行之前的搜索查询(请注意,在查询降采样索引时,需要注意一些细微差别):

GET /my-data-stream/_search

具有新的降采样后备索引的 TSDS 仅包含一份文档。 对于计数器,该文档仅具有最后的值。 对于 gauge,字段类型现在为 aggregate_metric_double。 你会看到基于原始采样指标的 min、max、sum 和 value_count 统计信息:

{"took": 3,"timed_out": false,"_shards": {"total": 4,"successful": 4,"skipped": 0,"failed": 0},"hits": {"total": {"value": 1,"relation": "eq"},"max_score": 1,"hits": [{"_index": ".ds-my-data-stream-2023.11.30-000001-downsample","_id": "0eL0wC_4-45SnTNFAAABjB5iLgA","_score": 1,"_source": {"@timestamp": "2023-11-30T04:00:00.000Z","_doc_count": 10,"ingest_time": "2023-11-30T04:24:20.124Z","kubernetes": {"container": {"cpu": {"usage": {"core": {"ns": 12828317850},"limit": {"pct": 0.0000277905},"nanocores": {"min": 38907,"max": 153404,"sum": 992677,"value_count": 10},"node": {"pct": 0.0000277905}}},"memory": {"available": {"bytes": {"min": 279586406,"max": 1062428344,"sum": 7101494721,"value_count": 10}},"majorpagefaults": 0,"pagefaults": {"min": 74843,"max": 302252,"sum": 2061071,"value_count": 10},"rss": {"bytes": {"min": 91914,"max": 402801,"sum": 2389770,"value_count": 10}},"usage": {"bytes": {"min": 100475044,"max": 379572388,"sum": 2668170609,"value_count": 10},"limit": {"pct": 0.00009923134},"node": {"pct": 0.017700378}},"workingset": {"bytes": {"min": 431227,"max": 2294743,"sum": 14230488,"value_count": 10}}},"name": "container-name-44","start_time": "2021-03-30T07:59:06.000Z"},"host": "gke-apps-0","namespace": "namespace26","node": "gke-apps-0-0","pod": "gke-apps-0-0-0"}}}]}
}

重新运行之前的聚合。 即使聚合在仅包含 1 个文档的降采样 TSDS 上运行,它也会返回与原始 TSDS 上的早期聚合相同的结果。

GET /my-data-stream/_search
{"size": 0,"aggs": {"tsid": {"terms": {"field": "_tsid"},"aggs": {"over_time": {"date_histogram": {"field": "@timestamp","fixed_interval": "1d"},"aggs": {"min": {"min": {"field": "kubernetes.container.memory.usage.bytes"}},"max": {"max": {"field": "kubernetes.container.memory.usage.bytes"}},"avg": {"avg": {"field": "kubernetes.container.memory.usage.bytes"}}}}}}}
}

此示例演示了降采样如何在你选择的任何时间范围内显着减少为时间序列数据存储的文档数量。 随着时间序列数据的老化和数据分辨率变得不那么重要,还可以对已经降采样的数据执行降采样,以进一步减少存储和相关成本。

对 TSDS 进行降采样的推荐方法是使用 ILM。 要了解更多信息,请尝试使用 ILM 运行降采样示例。这个将在我们的下面一篇文章中进行介绍。

这篇关于Elasticsearch:对时间序列数据流进行降采样(downsampling)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/463534

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

uva 10131 最长子序列

题意: 给大象的体重和智商,求体重按从大到小,智商从高到低的最长子序列,并输出路径。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>#include <stack>#include <vect

POJ1631最长单调递增子序列

最长单调递增子序列 import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.math.BigInteger;import java.util.StringTokenizer;publ

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

批处理以当前时间为文件名创建文件

批处理以当前时间为文件名创建文件 批处理创建空文件 有时候,需要创建以当前时间命名的文件,手动输入当然可以,但是有更省心的方法吗? 假设我是 windows 操作系统,打开命令行。 输入以下命令试试: echo %date:~0,4%_%date:~5,2%_%date:~8,2%_%time:~0,2%_%time:~3,2%_%time:~6,2% 输出类似: 2019_06

遮罩,在指定元素上进行遮罩

废话不多说,直接上代码: ps:依赖 jquer.js 1.首先,定义一个 Overlay.js  代码如下: /*遮罩 Overlay js 对象*/function Overlay(options){//{targetId:'',viewHtml:'',viewWidth:'',viewHeight:''}try{this.state=false;//遮罩状态 true 激活,f