Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如果未提交的日志放在内存中,会不会导致已提交后也会被推翻? #18

Open
yanwz opened this issue Feb 28, 2021 · 5 comments

Comments

@yanwz
Copy link

yanwz commented Feb 28, 2021

假设有节点 1,2,3,4,5
1 是leader
客户端写入一条消息 leader成功同步给2,3 两个节点(此时未提交日志保存在内存中)
leader 增加commitIndex 并提交日志(还未同步给2,3)

假设这个时候 节点1下线,节点2重启(内存中未提交的日志不存在了) 接下来的选举节点2成为leader(是可以的,因为4,5节点会支持) 这个时候会导致之前上一个term节点1已提交的日志被推翻.

@xnnyygn
Copy link
Owner

xnnyygn commented Mar 1, 2021

首先,日志是有持久化前提的。内存日志只是为了调试存在的。所以你的问题中节点2重启后日志是最新的,最终会同步给4和5,不存在日志冲突的问题。

@yanwz
Copy link
Author

yanwz commented Mar 1, 2021

感谢回复。我最近在拜读您的《分布式一致性算法开发实战》 ,其中提到有三种日志模式,1:未提交日志 放在缓存 已提交日志持久化到文件并删除缓存 2:未提交日志直接写入文件 已提交 无操作 3:未提交日志 写入文件和缓存 已提交日志 删除缓存,我上面的问题就是说的第一种模式情况下产生的,因为未提交的日志是没有持久化的。

@xnnyygn
Copy link
Owner

xnnyygn commented Mar 2, 2021

第一种模式下,提交时日志是写到文件里去的,此时节点2重启时会从文件读取日志,不会产生日志丢失的情况。换句话说,日志提交成功 -> 写入文件成功,写入文件成功 -> 日志不会丢失。结果是节点2或者3成为leader,没有日志冲突。

@yanwz
Copy link
Author

yanwz commented Mar 2, 2021

节点1(leader),2,3,4,5
1:leader收到客户端消息保存到内存(未提交),然后将日志同步给 follower节.2,3将日志保存到内存,当前日志还是未提交状态
2:leader收到2,3节点的确认,将内存中的日志保存至文件,更新commitIndex,此时这条日志在leader节点是已提交状态了;
3:leader 同步 commitIndex 到follwer节点,follower节点将日志保存至文件,删除内存里的日志,更新commitIndex
假设上面第二步执行完leader就down机了,节点2重启(内存中未提交的日志不存在了) ,接下来的选举节点2成为leader(是可以的,因为4,5节点会支持) 这个时候会导致上面第二步节点1已提交的日志被覆盖.

@xnnyygn
Copy link
Owner

xnnyygn commented Mar 3, 2021

首先我理解你这个场景是针对这个实现设计出来的场景,其次这种场景下节点3更有可能成为leader而不是节点2,节点2重启需要时间,重启之后也不是立马开始选举,相比之下,节点3发现没有心跳信息之后立马开始选举过程。

如果你问一部分日志放在内存中同步过程中丢失了怎么办,那你可以考虑
1:数据是否会丢失,可能性有多大
2:数据丢失会导致什么问题
3:有什么解决方案

按照你设计出来的场景,commtIndex没有同步到follower,follower的数据放在内存并且重启了,此时这个follower是否会成为leader然后覆盖掉其他节点的数据,首先要看leader是否仍旧存在,你的场景恰好leader宕机了,其次这个follower要抢在其他follower之前宣布自己想要成为leader并且成功升级。只能说三个条件加在一起机率极小。
那么数据丢失会造成什么问题,客户端传来的指令消失了,此时客户端会发生什么,原先的leader宕机了,所以客户端收不到回复或者超时了,客户端会认为指令没有成功。事实上客户端此时根本不能判断指令成功。
解决方案有解决和不解决两种。不解决是因为机率极小危害不大不予考虑,解决的话是重启前强制把内存数据写入文件(commitIndex启动时为0和文件无关)或者回归全部是文件的实现方式。前者你可能会说不能处理强制kill所以你的意思是文件才是安全的。

未提交的数据放在内存还是直接放在文件里和具体实现有关,只要这个实现不破坏算法本身的语义。你可以想象数据库,你开了一个事务,没有提交之前,数据库重启了,你认为未提交的数据可以继续被访问到,还是丢失了?个人认为不能预期数据库会保留未提交的数据(当然很多数据库因为MVCC会保存数据)。回过头来看Raft算法中,未提交的数据是否一定需要存在,算法本身没有给出明确的要求。假如给了,那些用批量日志保存和异步日志保存机制的产品级别Raft算法实现也是有问题的,他们都没有在回复leader时立即写入日志。

希望上面的回复能解决你的问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants