背景
某天,项目中来了一个需求,简单描述下就是这样的:
全量查询业务系统mysql中某一张表的数据,灌入到es中
easy so much,索引设定一个字段versionTime,每天同步数据时塞入时间戳,之后根据条件,将不是这次的versionTime的数据删除,就完成了全量更新,并将这一天中业务系统可能发生的删除操作的数据在es中删除。
本以为结束了,但却忽略了一个问题,es有延迟,近实时搜索引擎不只是说说而已。结果就杯具了,60%以上的几率,最终存在于es中的数据只有一半左右,难以接受呀!接下来就是排查问题
排查问题的过程
因为数据量比较少,项目最近也要求轻量化,就没有部署到平台运行,而是用springboot整合的quartz框架完成,于是启动本地,打个断点,想看看批量写入后返回的啥(最初以为写数据出现了问题...),结果返回的正常,放掉断点,执行完后续代码后,一切正常,查看es,数据也对的上,一条没少。然后打着断点,试了个5 、6、 7、 8次之后,依然无法复现,最后排除了所有的可能,剩下唯一的不可能,难道断点有影响?
说干就干,去掉断点,试了两次之后,果然又出现了,es里面数据不全。
这下稍微想了想es的介绍,近实时搜索引擎,为啥是近实时而不是实时?这可真的有区别的,然后仔细查阅了资料,基本上可以断定,问题的原因就是es的更新延迟问题了。
接下来说下为啥有延时,延迟的本质在哪
延时的原因
首先要了解的一件事,ES的索引数据是写入到磁盘上的。但这个过程是分阶段实现的,因为IO的操作是比较费时的。整个过程大概如下:
写数据----->ES内存 buffer(缓存区)-------定期refresh成segment------>os系统文件缓冲区(在这里的数据对搜索可见)---->磁盘
而在这里还有个很重要的参数需要介绍一下,refresh_interval
refresh_interval默认为1s,可以通过 GET /index_name/_settings?include_defaults=true
命令查看,在返回中会看到类似这样的结果:
(由于返回的数据太多,只截取了部分,各位看官可以自行实验)
这也就是本人这次创建的索引并未指定refresh_interval参数,使用了默认的1s,最终导致,在写操作结束后,立马执行删除操作,由于新的versionTime还没能被检索到,所以就删掉了一部分数据
如何解决
解决的思路在于es写入的过程,首先第一个想到的是sleep个 2s,反正是定时任务,而且还是在基本无人使用的时间段更新数据,程序执行慢点也无所谓,但是刚冒出来就否了,首先太挫,其次如果后续有人改了refresh_interval参数,sleep个 2s也没卵用,于是就使用了第二种办法,保证被索引的文档能够立即被搜索到
保证被索引的文档能够立即被搜索到, 有两种方法:_refresh 或者_flush。
调用_refresh就可以立即实现内存->文件系统缓存, 从而使文档可以立即被搜索到。
flush,flush是用于translog的。
这次用了refresh解决问题。以下列出了java版本常用API设置刷新策略的方法
java high level client中,为index、insert、update、bulk 提供了setRefreshPolicy方法,用于设置数据更改后的刷新策略。NONE("false"), // 默认;异步刷新,立即返回;
文章来源:https://www.toymoban.com/news/detail-716862.html
IMMEDIATE("true"), // 立即刷新,对es集群压力会比较大
WAIT_UNTIL("wait_for"); // 等刷新时返回,响应性不太好,
io.serchbox中 Bulk.Builder Index.Builder 的 refresh()方法文章来源地址https://www.toymoban.com/news/detail-716862.html
到了这里,关于记录一次es写数据延迟引发的问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!