咱们在HDFS - 什么是元数据中提到了,元数据会存在与内存和磁盘中,内存为了提升响应速度,磁盘是为了持久化保证数据的安全,可是写磁盘的速度相对于内存是慢了几个数量级的,若是NameNode每次都要把数据落到磁盘上那是没办法处理那么多客户端的请求的,因此NameNode用了双缓冲机制以及分段加锁。
所谓双缓冲就是定义了两个内存块,一个是bufCurrent,用于当前写入元数据的,一个是bufReady,用于写入磁盘的,两个内存块都是512k。segmentfault
线程1会先获取锁,而后会看看当前有没有在交换缓存(这里经过isAutoSyncScheduled来判断),若是没有,就去获取一个全局惟一的事务ID,这个ID是递增的。拿到事务ID后,就开始写入bufCurrent,写完后判断bufCurrent是否超过了512k,若是没有,线程1的流程就结束了(后面结束的背景是白色的)。
因为线程2,3,4在线程1没释放锁的时候,都会一直等到,若是线程2,3,4拿到了锁,也会继续上面线程1的操做。
因为这个锁里的操做都是基于内存的,因此速度就会很是块。
咱们假设线程4写完后,发现bufCurrent内存超过了512k,此时就会把isAutoSyncScheduled设置为true,说明要开始交换内存了,其余线程就不能作以上的操做了。这里的bufCurrent就是线程1,2,3,4写入的数据。
这个时候,线程5进来并拿到锁,咱们给拿锁的进程颜色标深一点,发现要开始交换内存了,因而他就是wait状态。缓存
咱们假设接下来获取到锁的是线程4。他发现此时并无其余线程交换内存(这里经过isSyncRunning判断),因而他就开始bufCurrent和bufReady的内存进行了交换,交换后,bufCurrent的数据就清空了。在这里还会把isSyncRunning设置true,而后isAutoSyncScheduled设置为false,最后就唤醒wait的线程。
线程4唤醒其余线程后,他就开始把bufReady的数据写入磁盘,这个操做是很耗时的,因此并无加锁,可是他是运行状态,因此下图标记为绿色。
这里的bufReady就是线程1,2,3,4写入的数据。
此时是线程5获取了锁,发现内存交换完毕了,开始往bufCurrent写数据。
此后线程6拿到了锁,此时bufCurrent又超过了512k了,就会把isAutoSyncScheduled设置为true,说明开始交换内存,其余线程就不能往bufCurrent写入数据了。可是他发现isSyncRunning为true,说明有其余线程在写磁盘了,因此他就开始wait。安全
此时线程4写完了磁盘,而后他获取到锁,就会把synctxid更改成本身的事务ID,而后赋值isSyncRunning为false,说明磁盘写入完成了。最后唤醒其余wait的线程。
此时线程6从新拿到了锁,他发现isSyncRunning为false了,那就是说明其余线程已经把本身的内容刷入磁盘了,因此线程6开始了上面线程4的操做,交换内存,写入磁盘。性能
第一把锁,主要是判断isAutoSyncScheduled以及对isAutoSyncScheduled的赋值,这个主要是说明bufCurrent和bufReady开始交换内存了。
第二把锁,主要是判断isSyncRunning以及对isSyncRunning和isAutoSyncScheduled的赋值。isSyncRunning是用来判断是否在写磁盘,isAutoSyncScheduled用来判断是否在交换内存,若是在交换,就不能写入bufCurrent,若是在写磁盘,那就不能写磁盘。
第三把锁,赋值isSyncRunning,说明磁盘写入完成。
这期间最耗时的操做并无加锁,其余内存操做的加锁,可是速度比较快,采用在这种分段加锁的方式和双缓冲机制,大大提升了性能。spa