一、源码剖析之NameNode如何支持高并发访问:双缓冲机制:NameNode如何支撑高并发访问(双缓冲机制)
### --- 高并发访问NameNode会遇到什么样的问题:
~~~ 经过学习HDFS的元数据管理机制,
~~~ Client每次请求NameNode修改一条元数据(比如说申请上传一个文件,
~~~ 都要写一条edits log,包括两个步骤:
~~~ 写入本地磁盘--edits文件
~~~ 通过网络传输给JournalNodes集群(Hadoop HA集群--结合zookeeper来学习)。
~~~ 高并发的难点主要在于数据的多线程安全以及每个操作效率!!
### --- 对于多线程安全:NameNode在写edits log时几个原则:
~~~ 写入数据到edits_log必须保证每条edits都有一个全局顺序递增的transactionId(简称为txid),
~~~ 这样才可以标识出来一条一条的edits的先后顺序。
~~~ 如果要保证每条edits的txid都是递增的,就必须得加同步锁。
~~~ 也就是每个线程修改了元数据,要写一条edits 的时候,都必须按顺序排队获取锁后,
~~~ 才能生成一个递增的txid,代表这次要写的edits的序号。
### --- 产生的问题:
~~~ 如果每次都是在一个加锁的代码块里,生成txid,然后写磁盘文件edits log,
~~~ 这种既有同步锁又有写磁盘操作非常耗时!!
二、HDFS优化解决方案
### --- 问题产生的原因主要是在于,写edits时串行化排队生成自增txid + 写磁盘操作费时,
### --- HDFS的解决方案
~~~ 串行化:使用分段锁
~~~ 写磁盘:使用双缓冲
### --- 分段加锁机制
~~~ 首先各个线程依次第一次获取锁,生成顺序递增的txid,然后将edits写入内存双缓冲的区域1,
~~~ 接着就立马第一次释放锁了。趁着这个空隙,后面的线程就可以再次立马第一次获取锁,
~~~ 然后立即写自己的edits到内存缓冲。
### --- 双缓冲机制
~~~ 程序中将会开辟两份一模一样的内存空间,一个为bufCurrent,
~~~ 产生的数据会直接写入到这个bufCurrent,而另一个叫bufReady,
~~~ 在bufCurrent数据写入(达到一定标准)后,两片内存就会exchange(交换)。
~~~ 直接交换双缓冲的区域1和区域2。保证接收客户端写入数据请求的都是操作内存而不是同步写磁盘。
二、双缓冲源码分析 找到FsEditLog.java
....
void logEdit(final FSEditLogOp op) {
boolean needsSync = false;//是否同步的标识
synchronized (this) {//
assert isOpenForWrite() :
"bad state: " + state;
// wait if an automatic sync is scheduled 如果当前操作被其它线程调度,则等待1s钟
waitIfAutoSyncScheduled();
// check if it is time to schedule an automatic sync
needsSync = doEditTransaction(op);
if (needsSync) {
isAutoSyncScheduled = true;//标识bufCurrent满了,进行双缓冲刷写
}
}
// Sync the log if an automatic sync is required.
if (needsSync) {
logSync();//将缓冲区数据刷写到磁盘
}
}
...