这是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)
与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_timeout
mysql每隔几秒调用 OS 函数fsync()
,它 - 除了在那些操作系统/虚拟机上它被实现为空操作(其他时间的另一个有趣的故事) - 它应该确保特定的文件描述符(用于事务的描述符) log) 包含已写入的所有内容,而不是写入内存(文件系统缓存),而是真正写入磁盘(或硬件缓存)。
这并不需要 MySQL 或操作系统从不时从日志缓冲区或操作系统缓存写入磁盘(实际上,他们会希望这样做以避免 I/O 排队)。所以fsync只是在“一切都应该立即写入”方面的限制,实际上是在IO可用时写入日志(即使事务尚未提交!),以使一切更加高效。如果您希望数据不持久,请不要提交事务(不推荐)或使用非持久的、仅内存的引擎,如 HEAP。