# 序
生产环境我们时时刻刻在向数据库发送着写入、新增、删除数据的请求,不知道各位有没有和我一样的顾虑,如果服务器突然断电或者死机,数据会不会丢失,甚至数据库会不会挂了再也无法恢复?
心大的同学可能会说,我们生产环境的数据有副本,不用慌!
那么正在写入中的数据呢?写入请求发给了数据库,还没收到成功/失败的响应,这时候数据库断电,你慌不慌?
当然,作为数据库,自有它保障数据安全的方式,只要做好了合适的配置,就可以应对断电的情况。
今天我们就以 Elasticsearch 和 MySQL 为例来了解下数据库是如何保障数据安全的。
# 原理
# Elasticsearch
数据在 lucene 中的主要写入流程上如下图所示,
从图上可以看出,只有 segment 落盘了,数据才是真正安全的。但是这会带来一个比较严重的问题:在 refresh 之后,commit 之前,数据已经可以被用户查到,然而断电后数据丢失,重启后这个数据无法被搜到。
因此 es 设计了translog,每次数据的写入,会在分词、加入倒排索引等重逻辑的 lucene 操作之前,数据的原始信息率先写入 translog。这就是 WAL (write-ahead-logging) 机制。
有了 translog 后,即使是没有落盘到 segment 的数据,崩溃想要恢复也有了依据,如下图所示,前2个阶段依靠 translog 恢复。
当然 translog 文件本身也会有 fsync 的问题,可以通过配置选择 translog 文件 fsync 的时机,而这个时机最终决定了数据的可恢复性。
Elasticsearch 比较常见的设置是:
index.translog.durability=async
index.translog.sync_interval=5s
这样配置下,最多可能丢失 5s 的数据。而 Elasticsearch 要保障数据支持崩溃恢复,比较极端的追求崩溃恢复的设置为:
index.translog.durability=request
每个 request 都会进行数据落盘。
# MySQL
数据更新操作在 MySQL中的流程如下图所示,
其中崩溃恢复的核心是 redo log,innodb_flush_log_at_trx_commit 参数设置为 1 时,可以保障每次事务 commit 的时候 redo log 刷到磁盘。
# 对比
# 同
- 为了提升性能,都充分利用了内存。由此也提升了数据安全问题的复杂度,需要保障内存中的数据在断电丢失后,有办法进行恢复。
- 为了解决上面的问题,都利用了 WAL (write-ahead-logging) 机制。
- 都需要关注 log 何时 fsync 到磁盘。
# 异
- log 作用不同:
- Elasticsearch 的 translog 用于崩溃恢复
- MySQL 的 redo log 用于崩溃恢复
- MySQL 的 binlog 不支持崩溃恢复,主要用于数据的增量备份。可以支持主从复制,可以支持数据回溯,配合全量的快照,可以回到之前某一时刻的状态。
- log 内容不同:
- translog 记录请求原始信息。
- redo log 记录结构化之后的,具体 page 中的修改信息,详情可以参见庖丁解InnoDB之REDO LOG。
- binlog 记录的内容和 translog 更为类似,它的 statement 格式记录的是原始语句。
- log 保存时长不同:
- translog 在每次 flush 完后都会清空,体积很小。
- redo log 循环使用ib_logfile0、ib_logfile1… 体积也不会特别大。(仅讨论 crash-safe 问题的话,这一点应该属于相同点)
- binlog 可以保存很久,时长完全取决于用户想要数据可追溯多久。
我们再重点关注下为什么 translog 和 redo log 同样用于 crash-safe,但记录的信息不同。
我想了很久,苦寻无果后翻阅 lucene 文档中查找 translog 信息时突然想起来,其实这个问题很简单。
因为他们所处的层级不同。
Elasticsearch 以 lucene 为引擎,但 lucene 本身没有 translog,因此 Elasticsearch 要在较高层级记录请求信息。而 InnoDB 作为 MySQL 的引擎,自身就可以拿到本次请求的数据结构化之后的信息。
# Review
面对业界共同的问题,总是有相似的解决方案可供参考。
弄清楚自己的需求和实际的数据库配置之后,数据库的服务器断电不用慌。
回到我们最开始的一张图,数据库虽可以做到 crash-safe ,但不代表我们对重要业务可以把心放到肚子里。因为:
- MySQL 对在 crash 后对事务是否需要数据恢复的依据是 binlog 是否完整,无论 redo log 是否达到了 commit 状态,即无论这个事务是否最终提交完成,响应是否到达客户端。
- 图上的 request / response 其实本身就并不可靠。
因此对于极其重要的业务来说,极端场景的业务完整性仍然需要保持关注。