为什么 mysql innodb_flush_log_at_trx_commit 设置为 0 没有数据丢失?

htt*_*086 2 mysql innodb

这是MySQL信息:

mysql> show variables like 'innodb%flush%log%';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_timeout    | 2700  |
| innodb_flush_log_at_trx_commit | 0     |
+--------------------------------+-------+
2 rows in set (0.00 sec)

mysql> select version()
    -> ;
+-----------+
| version() |
+-----------+
| 5.6.14    |
+-----------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

然后我插入一些东西:

mysql> insert into x select now();
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from x;
+---------------------+
| now()               |
+---------------------+
| 2014-08-20 18:59:09 |
+---------------------+
1 row in set (0.00 sec)

mysql>
Run Code Online (Sandbox Code Playgroud)

我立即杀死mysqld以使其崩溃:

:/data/mysql_dbf # killall -9 mysqld
:/data/mysql_dbf # /usr/local/bin/mysqld_safe: line 166: 28859 Killed                  nohup /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/data/mysql_dbf --plugin-dir=/usr/local/mysql/lib/plugin --user=mysql --log-error=/data/mysql_dbf/.err --pid-file=/data/mysql_dbf/.pid < /dev/null >> /data/mysql_dbf/.err 2>&1 >> /data/mysql_dbf/.err 2>&1 >> /data/mysql_dbf/.err 2>&1 >> /data/mysql_dbf/.err 2>&1 >> /data/mysql_dbf/.err 2>&1 >> /data/mysql_dbf/.err 2>&1 >> /data/mysql_dbf/.err 2>&1
140820 18:59:22 mysqld_safe Number of processes running now: 0
140820 18:59:22 mysqld_safe mysqld restarted
Run Code Online (Sandbox Code Playgroud)

然后我再次检查:

mysql> select * from x;
ERROR 2013 (HY000): Lost connection to MySQL server during query
mysql> select * from x;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    1
Current database: test

+---------------------+
| now()               |
+---------------------+
| 2014-08-20 18:59:09 |
+---------------------+
1 row in set (0.02 sec)
Run Code Online (Sandbox Code Playgroud)

没有数据丢失。有人可以解释为什么吗?

编辑 1

在@jynus 的帮助下,我们将测试是否有足够的数据。

i=0;
while true;
do ./bin/mysql --defaults-file=my.cnf -umysql -pmysql test -e "insert into x select now()";
    if [ $i -eq 100 ];
    then killall -9 mysqld;
        sleep 3;
    fi;
    ./bin/mysql --defaults-file=my.cnf -umysql -pmysql test -e "select count(*) from x" >> trace.log;
    if [ $i -gt 110 ];
    then break;
    fi;
    ((++i));
done
Run Code Online (Sandbox Code Playgroud)

输出显示我们正面临数据丢失

......
count(*)
99
count(*)
100
count(*)
101
count(*)
102
count(*)
103
count(*)
3
count(*)
4
count(*)
5
count(*)
6
......
Run Code Online (Sandbox Code Playgroud)

jyn*_*nus 6

innodb_flush_log_at_trx_commit = 0没有保证数据丢失(这样的选择是废话),但非保证数据不会在最后丢失innodb_flush_log_at_timeout秒(之间fsyncs)。

要测试丢失了多少数据,您需要每秒写入几次,然后在其中一些写入以自动方式成功提交后立即终止 mysqld。innodb_flush_log_at_timeout由于更改持久性选项,您将遭受 0 到(可能多一点;innodb_flush_log_at_timeout 默认为 1 秒,在您的示例中为 2700)秒丢失事务。

顺便说一句,我们中的一些人更喜欢innodb_flush_log_at_trx_commit = 2它提供相同的性能和持久性承诺,但它在操作系统级别进行缓冲,可能会在仅 MySQL 崩溃的情况下最大限度地减少数据丢失。

与持久性无关,除非出现硬件故障,否则由于 REDO 日志和双写缓冲区,您永远不会出现损坏或最终处于不一致状态。

编辑:似乎原因尚不清楚。让我详细说明:

innodb_flush_log_at_timeoutmysql每隔几秒调用 OS 函数fsync(),它 - 除了在那些操作系统/虚拟机上它被实现为空操作(其他时间的另一个有趣的故事) - 它应该确保特定的文件描述符(用于事务的描述符) log) 包含已写入的所有内容,而不是写入内存(文件系统缓存),而是真正写入磁盘(或硬件缓存)。

这并不需要 MySQL 或操作系统从不时从日志缓冲区或操作系统缓存写入磁盘(实际上,他们会希望这样做以避免 I/O 排队)。所以fsync只是在“一切都应该立即写入”方面的限制,实际上是在IO可用时写入日志(即使事务尚未提交!),以使一切更加高效。如果您希望数据不持久,请不要提交事务(不推荐)或使用非持久的、仅内存的引擎,如 HEAP。