Elasticsearch:是时候离开了! - 在 Elasticsearch 文档上使用 TTL

作者:来自 Elastic David Pilato

想象一下,圣诞老人必须向世界上所有的孩子们分发礼物。 他有很多工作要做,他需要保持高效。 他有一份所有孩子的名单,并且知道他们住在哪里。 他很可能会将礼物按区域分组,然后再交付。 但他不会在同一个地方停留太久。 他会丢下礼物然后离开。 他不会等待孩子们打开礼物。 他就会离开。

也许我们可以建议他列出一份他仍然需要访问的城市的清单。 一旦他送出了礼物,他就可以将这些城市从名单中删除。 这样,他就会知道自己还要去哪里。 而且他也不会浪费时间回到同一个地方。

为此,他只需使用他必须访问的城市的 TTL(time to live - 生存时间)即可。 他只需将 TTL 设置为他需要递送礼物的时间即可。 一旦 TTL 过期,他就会从列表中删除这些城市。

他的旅程可能是这样的:

{ "city": "Sidney", "deliver": "ASAP", "ttl": 0 }
{ "city": "Singapore", "deliver": "1 minute", "ttl": 1 }
{ "city": "Vilnius", "deliver": "3 minutes", "ttl": 3 }
{ "city": "Paris", "deliver": "5 minutes", "ttl": 5 }
{ "city": "London", "deliver": "6 minutes", "ttl": 6 }
{ "city": "Montréal", "deliver": "7 minutes", "ttl": 7 }
{ "city": "San Francisco", "deliver": "9 minutes", "ttl": 9 }
{ "city": "North Pole", "deliver": "forever" }

ttl 包含我们的 TTL 值(以分钟为单位):

  • 没有值意味着该文档将永远保留
  • 零意味着我们要尽快删除该文档
  • 任何正值都对应于删除文档之前需要等待的分钟数

ttl 摄取管道

要实现这样的功能,你只需要一个摄取管道:

PUT /_ingest/pipeline/ttl
{"processors": [{"set": {"field": "ingest_date","value": "{{{_ingest.timestamp}}}"}},{"script": {"lang": "painless","source": """ctx['ttl_date'] = ZonedDateTime.parse(ctx['ingest_date']).plusMinutes(ctx['ttl']);""","if": "ctx?.ttl != null"}},{"remove": {"field": [ "ingest_date", "ttl" ],"ignore_missing": true}}]
}

让我们解释一下。

第一个处理器在文档中设置一个临时字段 (ingest_date),然后我们在其中注入执行管道的时间 (_ingest.timestamp),该时间或多或少是索引日期。

然后我们运行一个 painless 脚本:

ctx['ttl_date'] = ZonedDateTime.parse(ctx['ingest_date']).plusMinutes(ctx['ttl']);

他的脚本根据 ingest_date 字段中可用的字符串值创建一个 ZonedDateTime java 对象。 然后我们只需调用 plusMinutes 方法并提供 ttl 的值作为参数。 这只会将摄取日期移动几分钟。 我们将结果存储在 ttl_date 新字段中。

请注意,我们需要添加一个条件,仅当 ttl 字段存在时才运行该处理器:

"if": "ctx?.ttl != null"

然后,我们只需删除不需要的字段 ingest_date 和可选的 ttl(如果我们不再需要它)。 请注意,出于调试目的,保留 ttl 可能是明智之举。 如果没有设置 ttl,如果丢失,我们也需要忽略它。 这是通过 “ignore_missing”: true 参数完成的。

为了测试这个管道,我们可以使用 simulate API:

POST /_ingest/pipeline/ttl/_simulate?filter_path=docs.doc._source,docs.doc._ingest
{"docs": [{"_source": { "city": "Sidney", "deliver": "ASAP", "ttl": 0 }},{"_source": { "city": "Singapore", "deliver": "1 minute", "ttl": 1 }},{"_source": { "city": "Paris", "deliver": "5 minutes", "ttl": 5 }},{"_source": { "city": "North Pole", "deliver": "forever" }}]  
}

请注意在上面所显示的时间为 UTC 时间。我在当前的北京时间是下午 14点多。

我们可以看到文档删除的更改日期。

自动创建 ttl_date 字段

我们可以使用 final_pipeline 索引设置将 ttl 管道定义为在实际索引操作之前使用的管道。

PUT /ttl-demo
{"settings": {"final_pipeline": "ttl"},"mappings": {"_source": {"excludes": ["ttl_date"]},"properties": {"ttl_date": {"type": "date"}}}
}

你还可以使用 default_pipeline 索引设置,但你需要注意,如果一个用户想要使用用户管道索引文档,则不会调用 ttl 管道,例如:

POST /ttl-demo/_doc?pipeline=my-pipeline
{ "city": "Singapore", "deliver": "1 minute", "ttl": 1
}

请注意,我们从 _source 字段中删除了 ttl_date 字段。 我们不想将其存储在 _source 字段中,因为它只是一个 “技术 ”字段。

索引文档

我们现在可以注入我们的数据集:

POST /ttl-demo/_bulk
{ "index": {} }
{ "city": "Sidney", "deliver": "ASAP", "ttl": 0 }
{ "index": {} }
{ "city": "Singapore", "deliver": "1 minute", "ttl": 1 }
{ "index": {} }
{ "city": "Vilnius", "deliver": "3 minutes", "ttl": 3 }
{ "index": {} }
{ "city": "Paris", "deliver": "5 minutes", "ttl": 5 }
{ "index": {} }
{ "city": "London", "deliver": "6 minutes", "ttl": 6 }
{ "index": {} }
{ "city": "Montréal", "deliver": "7 minutes", "ttl": 7 }
{ "index": {} }
{ "city": "San Francisco", "deliver": "9 minutes", "ttl": 9 }
{ "index": {} }
{ "city": "North Pole", "deliver": "forever" }

删除经过 TTL 处理的文档

现在可以轻松运行 “Delete By Query” 调用:

POST /ttl-demo/_delete_by_query
{"query": {"range": {"ttl_date": {"lte": "now"}}}
}

我们只想删除所有早于现在(即请求执行时间)的文档。

如果我们立即运行它,我们可以看到只有文档 { "city": "Sidney", "deliver": "ASAP", "ttl": 0 } 被删除。
一分钟后,{ "city": "Singapore”, "deliver": "1 minute", "ttl": 1 }。 再过几分钟,就只剩下 { "city": "North Pole", "deliver": "forever" } 了。 它将永远保留。

使用 Watcher 每分钟运行一次

你可以使用 crontab 每分钟运行一次这样的查询:

* * * * * curl -XPOST -u elastic:changeme https://127.0.0.1:9200/ttl-demo/_delete_by_query -H 'Content-Type: application/json' -d '{"query":{"range":{"ttl_date":{"lte":"now"}}}}'

请注意,你必须监视此作业。 但如果你有商业许可证,你也可以使用 Watcher 直接从 Elasticsearch 运行它:

PUT _watcher/watch/ttl
{"trigger": {"schedule": {"interval": "1m"}},"input": {"simple" : {}},"condition": {"always" : {}},"actions": {"call_dbq": {"webhook": {"url": "https://127.0.0.1:9200/ttl-demo/_delete_by_query","method": "post","body": "{\"query\":{\"range\":{\"ttl_date\":{\"lte\":\"now\"}}}}","auth": {"basic": {"username": "elastic","password": "changeme"}}}}}
}

请注意,我们使用 interval 参数每分钟运行此操作。 我们使用 Webhook 来调用按查询删除 API。 另请注意,我们需要提供身份验证信息。在上面,你需要根据自己的用户账号修改上面的身份信息。

如果你不是在 cloud.elastic.co 上运行,而是在本地使用自签名证书运行,因为 Elasticsearch 默认情况下是安全的,你需要将 xpack.http.ssl.verification_mode 设置为 none。 否则 Elasticsearch 将不会接受自签名证书。 当然,这只是为了测试目的。 不要在生产中这样做!如果我们不设置这个参数为 none,我们可以看到如下的错误信息:

最多一分钟后,圣诞老人就会看到他必须访问的城市已从列表中删除:

GET /ttl-demo/_search
{"took": 1,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 6,"relation": "eq"},"max_score": 1,"hits": [{"_index": "ttl-demo","_id": "IBTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"deliver": "3 minutes","city": "Vilnius"}},{"_index": "ttl-demo","_id": "IRTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"deliver": "5 minutes","city": "Paris"}},{"_index": "ttl-demo","_id": "IhTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"deliver": "6 minutes","city": "London"}},{"_index": "ttl-demo","_id": "IxTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"deliver": "7 minutes","city": "Montréal"}},{"_index": "ttl-demo","_id": "JBTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"deliver": "9 minutes","city": "San Francisco"}},{"_index": "ttl-demo","_id": "JRTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"city": "North Pole","deliver": "forever"}}]}
}

到最后,这里就只剩下他的家了:

{"took": 1,"timed_out": false,"_shards": {"total": 1,"successful": 1,"skipped": 0,"failed": 0},"hits": {"total": {"value": 1,"relation": "eq"},"max_score": 1,"hits": [{"_index": "ttl-demo","_id": "JRTn-4sBOKvQy-0aU35M","_score": 1,"_source": {"city": "North Pole","deliver": "forever"}}]}
}

这是一个简单快速但不够规范的解决方案,用于从 Elasticsearch 集群中删除旧数据。 但请注意,你永远不应该将此技术应用于日志或任何基于时间的索引,或者如果要以这种方式删除的数据量超过数据集的 10%。

相反,你应该更喜欢使用 delete index API 立即删除完整索引,而不是删除整套文档。 这是一种更有效的方式。

删除索引(首选)

为此,我们实际上可以更改管道以将数据发送到名称包含 ttl 日期的索引:

PUT /_ingest/pipeline/ttl
{"processors": [{"set": {"field": "ingest_date","value": "{{{_ingest.timestamp}}}"}},{"script": {"lang": "painless","source": """ctx['ttl_date'] = ZonedDateTime.parse(ctx['ingest_date']).plusDays(ctx['ttl']);""","ignore_failure": true}},{"date_index_name" : {"field" : "ttl_date","index_name_prefix" : "ttl-demo-","date_rounding" : "d","date_formats": ["yyyy-MM-dd'T'HH:mm:ss.nz"],"index_name_format": "yyyy-MM-dd","if": "ctx?.ttl_date != null"}},{"set": {"field" : "_index","value": "ttl-demo-forever","if": "ctx?.ttl_date == null"}},{"remove": {"field": [ "ingest_date", "ttl", "ttl_date" ],"ignore_missing": true}}]
}

在此示例中,我切换到每日索引,因为它比你在生产中看到的要准确得多,因为通常数据不会在几分钟后过期,但会在几天或几个月后过期。有关 date_index_name 的用法,请阅读文章 “Elasticsearch:使用 pipelines 路由文档到想要的 Elasticsearch 索引中去”。

我们更改了脚本以添加天数:

ctx['ttl_date'] = ZonedDateTime.parse(ctx['ingest_date']).plusDays(ctx['ttl']);

如果 ttl_date 字段存在,我们使用 date_index_name 处理器根据 ttl_date 字段创建一个新的索引名称。 我们使用 date_rounding 参数将日期四舍五入到当天。 我们使用 index_name_format 参数将日期格式化为 yyyy-MM-dd。 这将使用索引名称,例如 ttl-demo-2023-11-27:

{"date_index_name" : {"field" : "ttl_date","index_name_prefix" : "ttl-demo-","date_rounding" : "d","date_formats": ["yyyy-MM-dd'T'HH:mm:ss.nz"],"index_name_format": "yyyy-MM-dd","if": "ctx?.ttl_date != null"}
}

如果 ttl_date 字段不存在,我们只需将索引名称设置为 ttl-demo-forever:

{"set": {"field" : "_index","value": "ttl-demo-forever","if": "ctx?.ttl_date == null"}
}

我们可以重新索引我们的数据集:

POST /ttl-demo/_bulk?pipeline=ttl
{ "index": {} }
{ "city": "Sidney", "deliver": "ASAP", "ttl": 0 }
{ "index": {} }
{ "city": "Singapore", "deliver": "1 day", "ttl": 1 }
{ "index": {} }
{ "city": "Vilnius", "deliver": "3 days", "ttl": 3 }
{ "index": {} }
{ "city": "Paris", "deliver": "5 days", "ttl": 5 }
{ "index": {} }
{ "city": "North Pole", "deliver": "forever" }

我们可以看到这些文档现在位于不同的索引中:

GET /ttl-demo-*/_search?filter_path=hits.hits._index,hits.hits._source.deliver

索引名称不再引用,因为我们习惯于查看数据的日期,而是删除数据的日期。 因此我们可以每天再次运行 crontab 来删除旧索引。 以下脚本旨在在 Mac OS X 系统上运行:

0 0 * * * curl -XDELETE -u elastic:changeme https://127.0.0.1:9200/ttl-demo-$(date -v -1d -j +%F)

总结起来

我们看到了在 Elasticsearch 文档上执行 TTL 的两种方法。 第一个是使用 TTL 字段并使用 “Delete By Query ” 调用来删除文档。 第二种(效率更高,需要删除大量数据)是使用 TTL 字段将文档路由到不同的索引,然后使用 crontab 删除索引。

但对于这两种解决方案,在 contrab 运行之前文档仍然可见。

你可以考虑使用索引过滤别名来隐藏旧文档:

POST _aliases
{"actions": [{"add": {"index": "ttl-demo","alias": "ttl-filtered","filter": {"bool": {"filter": [{"range": {"ttl_date": {"gt": "now/m"}}}]}}}}]
}

即使批处理(crontab 或 watcher)尚未删除过期文档,在 ttl-filtered  别名内搜索也只会返回尚未过期的文档。

圣诞老人现在可以知道接下来安全地去哪里,然后享受当之无愧的休息一年!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/619936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker部署ng实现反向代理

场景 按规定尽可能减少开放到外网的端口,所以需要将多个服务部署到一个ip一个端口上。 方案 使用ng实现请求转发。根据http请求中的host与ng配置文件中的server_name匹配,转发到对应的机器上。 在docker上部署三个容器,每个容器中启动一个…

Java内存模型之可见性

文章目录 1.什么是可见性问题2.为什么会有可见性问题3.JMM的抽象:主内存和本地内存3.1 什么是主内存和本地内存3.2 主内存和本地内存的关系 4.Happens-Before原则4.1 什么是Happens-Before4.2 什么不是Happens-Before4.3 Happens-Before规则有哪些4.4 演示&#xff…

【SQL注入】SQLMAP v1.7.11.1 汉化版

下载链接 【SQL注入】SQLMAP v1.7.11.1 汉化版 简介 SQLMAP是一款开源的自动化SQL注入工具,用于扫描和利用Web应用程序中的SQL注入漏洞。它在安全测试领域被广泛应用,可用于检测和利用SQL注入漏洞,以验证应用程序的安全性。 SQL注入是一种…

调试(c语言)

前言: 我们在写程序的时候可能多多少少都会出现一些bug,使我们的程序不能正常运行,所以为了更快更好的找到并修复bug,使这些问题迎刃而解,学习好如何调试代码是每个学习编程的人所必备的技能。 1. 什么是bug&#xf…

Java项目:06 Springboot的进销存管理系统

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 进销存管理系统 介绍 进销存系统是为了对企业生产经营中进货、出货、批发销售、付款等全程进行(从接获订单合同开 始,进入物料采购、入…

浅析Linux进程地址空间

前言 现代处理器基本都支持虚拟内存管理,在开启虚存管理时,程序只能访问到虚拟地址,处理器的内存管理单元(MMU)会自动完成虚拟地址到物理地址的转换。基于虚拟内存机制,操作系统可以为每个运行中的进程创建…

ros2+gazebo(ign)激光雷达+摄像头模拟

虽然ign不能模拟雷达,但是摄线头是可以模拟的。 好了现在都不用模拟了,ign摄线头也模拟不了。 ros2ign gazebo无法全部模拟摄线头和雷达。 只能有这样2个解决方法: 方法1:使用ros2 gazebo11 方案2:使用ros2买一个实…

【问题探讨】基于非支配排序的蜣螂优化算法NSDBO求解微电网多目标优化调度研究

目录 主要内容 模型研究 结果一览 下载链接 主要内容 该模型以环境保护成本和运行成本为双目标构建了微电网优化调度模型,模型目标函数和约束条件复现文献《基于改进粒子群算法的微电网多目标优化调度》,程序的特点是采用非支配排序的蜣螂…

Redis缓存使用问题

数据一致性 只要使用到缓存,无论是本地内存做缓存还是使用 redis 做缓存,那么就会存在数据同步的问题。 以 Tomcat 向 MySQL 中写入和删改数据为例,来解释数据的增删改操作具体是如何进行的。 我们分析一下几种解决方案, 1、先更新缓存,再更新数据库 2、先更新数据库,…

2023年第十四届中国数据库技术大会(DTCC2023):核心内容与学习收获(附大会核心PPT下载)

随着信息化时代的深入发展,数据库技术作为支撑信息化应用的核心技术,其重要性日益凸显。本次大会以“数据价值,驱动未来”为主题,聚焦数据库领域的前沿技术与最新动态,吸引了数千名业界专家、企业代表和数据库技术爱好…

橘子学Spring01之spring的那些工厂和门面使用

一、Spring的工厂体系 我们先来说一下spring的工厂体系(也称之为容器),得益于大佬们对于单一职责模式的坚决贯彻,在十几年以来spring的发展路上,扩展出来大量的工厂类,每一个工厂类都承担着自己的功能(其实就是有对应的方法实现)…

阿里云云服务器u1实例和e实例有什么区别?

阿里云服务器u1和e实例有什么区别?ECS通用算力型u1实例是企业级独享型云服务器,ECS经济型e实例是共享型云服务器,所以相比较e实例,云服务器u1性能更好一些。e实例为共享型云服务器,共享型实例采用非绑定CPU调度模式&am…

监督学习 - 岭回归(Ridge Regression)

什么是机器学习 岭回归(Ridge Regression)是一种线性回归的扩展,它通过在损失函数中添加正则化项(L2范数)来解决线性回归中可能存在的过拟合问题。正则化项有助于限制模型的参数,使其不过分依赖于训练数据…

LeetCode264. 丑数 II(相关话题:多重指针动态规划)

题目描述 给你一个整数 n ,请你找出并返回第 n 个 丑数 。丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 1: 输入:n 10 输出:12 解释:[1, 2, 3, 4, 5, 6, 8, 9, 10, 12] 是由前 10 个丑数组成的序列。示例 2&am…

【Sharding-Sphere 整合SpringBoot】

Sharding-Jdbc在3.0后改名为Sharding-Sphere。Sharding-Sphere相关资料,请自行网上查阅,这里仅仅介绍了实战相关内容,算是抛砖引玉。 Sharding-Sphere 整合SpringBoot 一、项目准备二、项目实战1. pom.xml及application.yml2. OrderInfoCont…

串行Nor Flash的结构和参数特性

文章目录 引言1、Nor Flash的结构2、Nor Flash的类别3.标准Serial Nor Flash的特征属性1.Wide Range VCC Flash2.Permanent Lock3.Default Lock Protection4.Standard Serial Interface5.Multi-I/O6.Multi-I/O Duplex (DTR)7.XIP(片上执行) 4.标准Serial…

Java内置锁:深度解析Lock接口中lock方法和lockInterruptibly方法

Java11中的Lock接口提供lock()和lockInterruptibly()两种锁定方法,用于获取锁,但处理线程中断时有所不同,lock()使线程等待直到锁释放,期间无视中断;而lockInterruptibly()在等待中若收到中断请求,会立即响…

倍福PLC控制器开发环境介绍

倍福PLC控制器是一款功能强大、易于使用的可编程逻辑控制器,广泛应用于各种工业自动化控制系统中。为了充分发挥倍福PLC控制器的功能,需要使用合适的开发环境。下面将介绍倍福PLC控制器的开发环境,主要包括软件安装与配置、工程创建与管理、编…

“超人练习法”系列08:ZPD 理论

01 先认识一个靓仔 看过 Lev Vygotsky 这个人的书吗?他是一位熟练心理学家,对人们习得技能的方式非常感兴趣,但他 37 岁的时候就因肺炎英年早逝了。 他认为社会环境对学习有关键性的作用,认为社会因素与个人因素的整合促成了学习…

element ui el-table展示列表,结合分页+过滤功能

vueelement-ui实现的列表展示&#xff0c;列表分页&#xff0c;列表筛选功能 1&#xff0c;分页器 el-table模块下面是分页器代码 <el-pagination></el-pagination> <el-table></el-table> <!-- 分页器 --><div class"block" st…