写操作与数据有效性

从客户端有数据产生到数据被保存到持久存储系统上需要经历的步骤:
1. 数据在客户端的内存里
2. 客户端把数据发送到服务端内存里;
3. 服务端调用write写文件,写成功;
4. 文件系统把内容flush到磁盘控制器;
5. 磁盘控制器把内容保存到物理磁盘上;

上述的5个步骤中,1、2的时候无论客户端还是服务端死掉数据都是不会损坏的。
如果在步骤3之后应用死掉而机器还在的话,不会有太大的问题;如果这个时候机器死掉的话会丢失这个数据。

其中,磁盘控制器把数据刷到物理介质上的时间点是不确定的,如果不是磁盘有备用电源就不要开写缓存(可以用hdparm等命令看到并管理,但是没有试成功),仅开读缓存即可。

文件系统把数据刷到磁盘控制器上的时机可以由系统调用来搞定(fsync),但是此调用会阻塞应用一定的时间,调用的时候可以考虑合并一部分请求,具体如何来合并需要看系统的要求而定(Group Commit)。
   fsync操作比较昂贵,传统硬盘(10K转/分钟)大约每秒支撑150个fsync操作,SSD(Intel X25-M)大约每秒支撑1200个fsync操作。 (摘自mysql group commit

Group commit
在一次操作涉及到多个写的时候,需要保证多个写操作的事务性,即要么都完成,要么都不完成。因为每次写操作之后都有可能业务挂掉,因此需要有一些其它的机制保证。可用的几个策略:
1. 数据同步写到其他机器上,这样即使这个应用实例没有完成,也可以从其它机器上恢复。这个策略有很多问题,因为首先同步回来需要较长时间,而且同步的话,也可能出现错误等情况,影响响应时间。
2.定期做快照,这样可以从快照恢复。redis使用的策略就是fork一个进程,然后在新进程里把数据系统的写入一个文件里面。这样写入的操作是append only的,不会因为数据结构的损坏导致整个数据坏掉。
3.使用append only日志,由于是append only的,写失败也不会导致数据结构的损坏。对append only的日志也可以定期进行数据的清理,对同一个数据的多次写操作可以归并成一次,减少数据的大小。
4.两步write,首先把多个写操作的结果写入一个日志时文件,然后执行写入,如果出错可以从日志文件中读取再次写入。

2、3两个做法还有一个好处就是把写磁盘变成了顺序写入操作,这样可以充分利用磁盘的性能。

posted @ 2012-04-09 12:42  传灯  阅读(175)  评论(0编辑  收藏  举报