高峰只对攀登它而不是仰望它的人来说才有真正意义。

停止传播ES里这个荒谬的观点吧

Elasticsearch | 作者 Charele | 发布于2023年09月14日 | 阅读数:2033

就是ES在refresh的时候写缓存的问题。
这视乎是老话题了,但有种不吐不快的感角
 
 各种网上资料,B战上大佬的视频,都是给你灌输这个理念:
ES在refresh时,内存中的数据是先写到缓存
(这个缓存叫啥, System cache,OS cache, Filesystem cache,五花八门),
 
然后从缓存中读出新段,因为此刻不需要写磁盘,所以比较快,这就实现了NRT。
而缓存里的数据会在flush时写到磁盘上
 听起来很heli,但这是真相吗?

 
下面解释:为什么我认为这是个极其荒唐的说法。
(如果有任何怀疑或错误之处,请指出)
对任何一句话有异议,我会告诉你具体实现代码在哪
 
我的偶像们镇楼:
06be5c193299a73ab1be86490e33aebd.jpeg
已邀请:

Charele - Cisco4321

赞同来自:

111.png

ES执行refresh的时候,会执行到这里。
 
图中这行代码会写内存中各种内容到新段文件(doc, pos, tim, tip,fnm等等),
就是通过标准的Java API, write(), flush()/close()来写文件, 
你说不对,写的时候会有缓存,
就是什么“内核缓冲区高速缓存”之类的东东。
 
不否认OS层面会有缓存,但那是底层做的事。
它用的是标准的写文件API,它并没有用某种“写缓存API”(也不存在)。
它和你在别的地方看到的Java写一个文件,并没有何任区别!
 
如果非得跟缓存拉上关系,那照这个说法,
Java中所有写文件操作就都是在写缓存了?(也就是没有“不是写缓存”的了)
既然大家没区别,那为什么得特别强调说这时,ES refresh是在“写缓存(从缓存读)”呢?
 
我认为ES refresh操作,就是写磁盘文件,
并不是“写缓存"(可能会有某种缓存会参与,但本质上是写文件)
 
当这行代码完成后,新的段文件就写到了磁盘上,
然后把新段从磁盘上读出来,
 
这里会有个观点,说数据可能还在缓存,也可能已经写到了磁盘,要看系统繁忙程度而定,
意思就是说:如果在缓存就从缓存读,在磁盘就从磁盘读,不确定。
 
我的看法是:在读的时候,肯定在磁盘上。因为它就是从磁盘读的
 
(新段 + 老段) ---> dr ---> 返回给你
 
既然是普通的写/读文件,好像就体现不出NRT“快”的优势了,
大佬博客里有说明:

Charele - Cisco4321

赞同来自:

111.png

 
NRT,实际就是会复用原有段引用,节省了开销,所以快。
相比非NRT方式而言,因为那种方式要(从头)打开所有的段才能读到数据

Charele - Cisco4321

赞同来自:

在说ES fresh的时候,会跟某种cache联系起来,
为什么呢。
 
看这里:(这个链接来自Lucene源码注释)
https://blog.thetaphi.de/2012/ ... .html
111.png

 
这是关于mmap的文章(细节可以自己看)
mmap里确实会有filesystem cache这些概念。
 
 mmap那套牛B高效的机制,只是用来读的。
在写上的,是没有什么magic的。

Charele - Cisco4321

赞同来自:

aaa.png

bbb.png

 

Charele - Cisco4321

赞同来自:

开始我曾想过, Lucene里面段的写和读之间会不会有某种关联,
比如ES refresh完成后,会不会传一个(writer里面的)缓存的指针给reader,
reader就不需要从磁盘读了,而从某个缓存读?
 
跟本没这么回事。reader接受的就是一个文件名,
它只能老老实实地从磁盘打开这个文件,,,
 
面且writer和reader之间也没有类似的notify机制,
就是说“我写完了,立马通知你,你赶紧读,趁数据还在缓存里,晚了你就得读磁盘了”
writer和reader完全是两个不相关的过程。
  
拿doc文件举例
111.png

请问:当你第一次去读一个新文件时,这个文件能在某个缓存里吗?
如果在缓存里,那说明别人已经读过了,你就不是第一次读了

leohe777

赞同来自:

有个问题没想明白,refresh直接写盘了,那现在tanslog存在的意义是啥,主分片数据恢复? tanslog 5s才刷一次盘,refresh默认1s刷一次,而且现在副本恢复和ccr使用软删除都可以不需要translog了 @Charele

Charele - Cisco4321

赞同来自:

我上面强调的的ES refresh时“写文件”,是指Lucene行为,跟ES无关。
 
分工很明确,我管不了你Lucene怎么做。
我ES refresh了,我就得可见。
你如何实现,数据在哪我不管,
我ES这里自己写日志来保证我(来不及flush时)的安全。
我ES flush了,我就认为数据安全了(就可以丢弃日志了)。
 
 ES refresh = Lucene flush, ES flush = Lucene commit
注意下单词在不同场合的区分
 
 我先从Lucene的角度来说,
比如现你在磁盘上有段1,和提交文件Segment_A(这个里面记录它生成时所有的段)
你现在加了新文档,(且执行flush,生成了新段2)
 
这个时候,如果用NRT Reader,尽管没有生成新的提交文件,但它是可以直接读段2的。
(就是ES中refresh后就能查到数据)
 
执行commit()时,它会写新的Segment_B文件(里面包括段1和段2)
 
好,你重新打开程序时,它会读最后的Segment_B,就可以读出段1和段2的数据
(这时它并不能直接读段文件,而是要通过段提交文件来识别有哪些段) 
如果你只是flush了,就是段2的文件写到了磁盘上,
但没有commit,没有生成Segment_B提交文件。
如果程序挂了,重开,只能去读Segment_A(里面只记录了段1),
所以尽管段2文件在磁盘上,但你无法获取到,等于段2就丢失了。
 
Lucene commit的意义在就在这里,生成提交点文件。
(它还会fsync来确保各个文件的安全)
 
  
=======================================================================
 全局检查点:简称gcp
回到ES,你说的“tanslog 5s才刷一次盘”,这是不准确的。
ES中有两种模式,一种是Request,这种是每次插入一个文档,都会把日志刷盘
(这个工作叫“同步gcp”,除了刷日志,它还会写最新的gcp)
 
另一种Async时,的确是缺省5秒一次执行“同步gcp”
 
1 Request方式时,你插入新文档(它会马上刷日志,生成最新的gcp),
(且refresh,1秒一次,磁盘上生成了新段),
 
 如果你没有flush,ES挂了。重启后,
记录的gcp来判断你应该有哪些数据(缺哪些数据),
然后它从日志中找出来,replay一下,重新插入这个文档(等于挂之前生成的那些段文件就作废了)
 
如果你执行了flush操作
(ES flush 也会写段文件,如果你禁用了refresh,它会在这里写),
 
最新gcp里所需要的段文件和Segment提交文件同时都在磁盘上,日志也就没用了
(旧日志并不是简单的认为没用)
 
2 Async方式,你插入了文档(同时refresh生成了新段)。
如果你没有来得及等5秒执行“写最新gcp”和刷日志。
挂了重启后,你还是读老的gcp,读出老的提交文件,读出老的段。
(因为老的gcp并不要求你这个新文档,所以不会去找日志,也没日志)
 尽管磁盘上有新段文件,等于不存在。等于这个文档就丢失了
 
像CCR,peer恢复这种所需的数据,都是由租约去保证的
租约可能会引用到老日志文件,通过“最小保留SeqNo点”来引用。只有小于这个点的日志才能被删除。
所以“副本恢复和ccr使用软删除都可以不需要translog了”这是不对的

Charele - Cisco4321

赞同来自:


 
这里ES flush后,执行的“删除日志”的操作,它叫“清除未参考的”,
它有一系列判断,
就是说,并不是flush之后,就能删除旧日志
 
人们常说的“flush操作后会删除旧日志“也是不对的

leohe777

赞同来自:

明白了,Charele 感谢你的仔细回答,也就是说Async方式有可能丢失5S的数据了

emmning - for learn you know

赞同来自:

就在上面mmap博客的评论区,Lucene的成员表示NRT directory会缓存写入数据直到commit或者缓存区满了。这应该就是ES中说的内存中的数据是先写到缓存。

Charele - Cisco4321

赞同来自:

“NRT directory会缓存写入数据直到commit或者缓存区满了”
你的意思,数据会一直呆在缓存里,直到commit时才会实际写入磁盘,是吗?
 
如果你是这个意思,你可以去试啊
3种方式,不管哪都可以。
 
1 Lucene只flush,不commit,退出2 Lucene,用NRT Reader查一下数据,就退出
3 ES,只refresh(不管自动还是手动),不执行flush,杀死ES进程
(2和3,本质上是是一样的操作)
 
 A 看看这3种情况下有没有数据文件
B 看看这3种情况下和正常情况生成的数据文件是不是一模一样

zhous

赞同来自:

    Page Cache 的机制是Linux默认启用的,所有应用写文件都有这个机制,那么是否是ES或者lucene在设计上针对这一机制做了特殊的优化从而能更好的使用这一机制,例如:批量写、顺序写、使用MMP,如果是这样,那么也不能算是一个荒谬的观点,就像很多文章说到到kafka高性能是使用了Page Cache一样,虽然这个机制是系统底层提供的,但是不是所有应用程序都能够充分利用这一机制。以下是我所了解到的MMP和Page Cache:
  • MMP可以利用 Page Cache 的机制。当使用内存映射文件时,操作系统会将文件的部分或全部内容缓存在内存中。这意味着,如果文件的某个部分已经在 Page Cache 中,而应用程序又通过内存映射访问了这个区域,那么数据可以直接从 Page Cache 中读取,而无需从磁盘读取。
  • 在写文件时通常会先将数据写入到操作系统的 Page Cache 中,但尚未写入磁盘,操作系统通常采用异步写的方式将数据从 Page Cache 刷写到磁盘。

要回复问题请先登录注册