hdfs上的append测试
hbase在写入数据之前会先写hlog,hlog目前是sequencefile格式,采用append的方式往里追加数据。之前团队的同学测试关闭hlog会一定程序上提升写hbase的稳定性。而在我之前的想象中,hlog的写入速度应该是稳定的。于是写了个append程序专门测试hdfs的append性能。
代码如下:
图2:关闭sync功能

从图1和图2的结果可以看到打开和关闭sync操作同样不稳定,因此可以判断不稳定因素主要出在write本身上。观察write函数,发现在创建它时需要一个blocksize参数,我的代码中一开始是设置的1MB。于是修改为32MB,绝大部分毛刺消失了。进一步修改为64MB,性能有进一步的提升。如下图
图3:设为32MB

图4:设为64MB

这个参数是决定多大的文件在hdfs上可读的。传统的hdfs写文件要满足dfs.block.size大小(默认64MB)才可读。但是在append模式下这个可读的大小是由这里的blocksize决定的。默认值在本地文件系统下由fs.local.block.size决定,在hdfs文件系统下仍由dfs.block.size决定。如果设为1MB,那么hdfs上每append 1MB的大小,就可以读到了。当写入的数据达到这个大小时,会触发namenode执行fsync()操作。而在日志中观察到,每次发生这个操作时,都会造成读响应的变慢。
fsync()操作的内容比较多,没有仔细看源码,知道原理的同学联系我吧。
从附图中可以看到,append_block_size从1MB提高到32MB,再提高到64MB,都会有一定程序的稳定性改善。再提高就没有用了,因为hlog和dfs.block.size的默认大小都是64MB。不过hbase每1s会强制刷新执行一次fsync,所以会看到hbase在打开日志的情况下每1s会有一次小的响应时间波动
结论有两点:
1 hdfs的append的确是有一点不稳定的
2 修改fs.local.block.size或dfs.block.size可以影响这个不稳定因素。
1 楼 lance_123 2011-05-08 fsync()操作就是对当前的文件记录一下日志信息,包括当前的操作标记位op_add,文件的相关属性,以及文件的当前块信息。
代码如下:
public void logOpenFile(String path, INodeFileUnderConstruction newNode) {
DeprecatedUTF8 nameReplicationPair[] = new DeprecatedUTF8[] {
new DeprecatedUTF8(path),
FSEditLog.toLogReplication(newNode.getReplication()),
FSEditLog.toLogLong(newNode.getModificationTime()),
FSEditLog.toLogLong(newNode.getAccessTime()),
FSEditLog.toLogLong(newNode.getPreferredBlockSize())};
logEdit(OP_ADD,
new ArrayWritable(DeprecatedUTF8.class, nameReplicationPair),
new ArrayWritable(Block.class, newNode.getBlocks()),
newNode.getPermissionStatus(),
new DeprecatedUTF8(newNode.getClientName()),
new DeprecatedUTF8(newNode.getClientMachine()));
}
作用就是当在写操作失败后,NN会根据日志信息,当操作标记位为op_add,表示失败前正在写操作,于是先启动该数据块的恢复操作,等该文件恢复完后,才会将lease释放掉。
然后回到FSEditLogLoader类中的loadEditRcords()方法有对标记位为op_add的日志进行处理。
2 楼 lc_koven 2011-05-11 lance_123 写道fsync()操作就是对当前的文件记录一下日志信息,包括当前的操作标记位op_add,文件的相关属性,以及文件的当前块信息。
代码如下:
public void logOpenFile(String path, INodeFileUnderConstruction newNode) {
DeprecatedUTF8 nameReplicationPair[] = new DeprecatedUTF8[] {
new DeprecatedUTF8(path),
FSEditLog.toLogReplication(newNode.getReplication()),
FSEditLog.toLogLong(newNode.getModificationTime()),
FSEditLog.toLogLong(newNode.getAccessTime()),
FSEditLog.toLogLong(newNode.getPreferredBlockSize())};
logEdit(OP_ADD,
new ArrayWritable(DeprecatedUTF8.class, nameReplicationPair),
new ArrayWritable(Block.class, newNode.getBlocks()),
newNode.getPermissionStatus(),
new DeprecatedUTF8(newNode.getClientName()),
new DeprecatedUTF8(newNode.getClientMachine()));
}
作用就是当在写操作失败后,NN会根据日志信息,当操作标记位为op_add,表示失败前正在写操作,于是先启动该数据块的恢复操作,等该文件恢复完后,才会将lease释放掉。
然后回到FSEditLogLoader类中的loadEditRcords()方法有对标记位为op_add的日志进行处理。
非常感谢。我查看了代码。正是因为fsync的这些操作导致append的速度是不平稳的。另外更正一下,append下fsync的间隔在hdfs文件系统上仍然是由dfs.block.size决定的,另外hlog每1s也会强制执行一次fsync操作 3 楼 lance_123 2011-05-12 lc_koven 写道lance_123 写道fsync()操作就是对当前的文件记录一下日志信息,包括当前的操作标记位op_add,文件的相关属性,以及文件的当前块信息。
代码如下:
public void logOpenFile(String path, INodeFileUnderConstruction newNode) {
DeprecatedUTF8 nameReplicationPair[] = new DeprecatedUTF8[] {
new DeprecatedUTF8(path),
FSEditLog.toLogReplication(newNode.getReplication()),
FSEditLog.toLogLong(newNode.getModificationTime()),
FSEditLog.toLogLong(newNode.getAccessTime()),
FSEditLog.toLogLong(newNode.getPreferredBlockSize())};
logEdit(OP_ADD,
new ArrayWritable(DeprecatedUTF8.class, nameReplicationPair),
new ArrayWritable(Block.class, newNode.getBlocks()),
newNode.getPermissionStatus(),
new DeprecatedUTF8(newNode.getClientName()),
new DeprecatedUTF8(newNode.getClientMachine()));
}
作用就是当在写操作失败后,NN会根据日志信息,当操作标记位为op_add,表示失败前正在写操作,于是先启动该数据块的恢复操作,等该文件恢复完后,才会将lease释放掉。
然后回到FSEditLogLoader类中的loadEditRcords()方法有对标记位为op_add的日志进行处理。
非常感谢。我查看了代码。正是因为fsync的这些操作导致append的速度是不平稳的。另外更正一下,append下fsync的间隔在hdfs文件系统上仍然是由dfs.block.size决定的,另外hlog每1s也会强制执行一次fsync操作
是的,一个块只会执行一次fsync操作,用了一个boolean变量来判断是否需要执行fsync,当向NN申请了一个块时,该变量为true,则当flush时会执行fsync操作,之后会将其改为false,直到下一个新申请的块将其设为true后,才会再次执行该操作。 4 楼 hf81970 2011-11-28 能问下您用的测试工具是什么吗?这些图都是自动生成的吗? 5 楼 lc_koven 2011-11-28 hf81970 写道能问下您用的测试工具是什么吗?这些图都是自动生成的吗?
自己写的,这是excel中的图,很土吧:)