-- doMiniBatchPut(BatchOperationInProgress)
实例化一个WALEdit
如果CoprocessorHost不为空,对每一个Put操作调用CoprocessorHost的prePut方法,如果该方法返回true,设置这次Put操作的返回状态为
SUCCESS。(为什么要这样做?CoprocessorHost是干什么用的?调用prePut又是为何,源代码中说是为了防止死锁,为何这样做就能防止死锁?如果不这样做就会有死锁发生?)
一开始设置变量firstIndex和lastIndexExclusive,二者都为此次batchOperation的nextIndexToProcess。
只要lastIndexExclusive小于此次批次操作的总数量,做下面的循环,该循环的主要功能是尽可能多地得到操作某一行的行锁。
++++++++++++++++++++++++++++++++++++++++++++++++++++
得到下次要做处理的Put(通过batchOp.operations[lastIndexExclusive]得到),同时提取该Put的familyMap信息。
如果该次Put操作的状态已经执行过了(不一定是已经执行过,只要该次操作的结果code不等于OperationStatusCode.NOT_RUN),
lastIndexExclusive递增,退出该次操作,处理下一个Put操作。
检查familyMap是否合法,如果不合法,将该次Put操作的结果code设置为OperationStatusCode.BAD_FAMILY,同时递增lastIndexExclusive,退
出该次操作,处理下一个操作。
下面做该步骤的主要任务:得到该行的锁。
要获得锁需要判断客户端是否提供了lockId:
====================================================
a) 如果客户端提供了lockId,但是该行并没有被锁住,说明客户端提供了一个非法的锁,抛出一个IOException。
b) 如果客户端没有提供lockId,尝试调用internalObtainRowLock获得该行的锁,该方法保证一个row只能有一个锁存在。HRegion在关于行锁方
面有三个数据结构,分别是lockedRows,该变量是一个ConcurrentHashMap<HashedBytes, CountDownLatch>,以HashedBytes为key,该key即是
row,value为CountDownLatch;另外一个变量是lockIds,该变量也是一个ConcurrentHashMap<Integer, HashedBytes>,该变量的key为Integer
,即一把锁id,value为该锁对应的row;第三个变量是AtomicInteger类型的lockIdGenerator,当生成新的lockId时,循环incrementAndGet方
法直到得到一个没有被使用的锁id。使用ConcurrentHashMap.putIfAbsent方法保证了map操作的原子性。
====================================================
++++++++++++++++++++++++++++++++++++++++++++++++++++
此刻,我们尽可能地将游标(lastIndexExclusive)移到了该批次的尾端,firstIndex到lastIndexExclusive之间的Put操作是我们下面要做的
操作。下面记这些Put操作为集合PS。
对集合PS中的每一个put操作,调用updateKVTimestamps更新family的timestamp。
尝试获得updatesLock的读锁。
对集合PS中的每一个put操作,调用addFamilyMapToWALEdit,将该put的familyMap信息保存到WALEdit。
调用HLog的append方法,将WALEdit保存,这一步即是写入操作日志。
对集合PS中的每一个put操作,调用applyFamilyMapToMemstore,将该put的familyMap信息保存的memstore。此时,数据在内存中就做了添加或
更新。如果该
如果coprocessorHost不为空,对集合PS中的每一个put操作,调用coprocessorHost的postPut做善后处理。
设置标识变量success为true,该变量代表本批次操作是否完全成功(当然,并不是每一个put操作都成功,如果某个put的familyMap不合法,该
put的操作结果code为BAD_FAMILY)。
finally的善后工作
++++++++++++++++++++++++++++++++++++++++++++++++++++
如果获得了读锁,就解锁。
集合PS中的每一个put操作的行锁(这些行锁在最开始的循环中得到,并放入了List<Integer>类型的acquiredLocks变量中)。
如果success为false,即在处理某一个put时出错抛出了异常,需要对PS集合中操作结果是NOT_RUN的操作做处理,将这些操作结果code设置为
FAILURE。
++++++++++++++++++++++++++++++++++++++++++++++++++++
-- 能学到什么?
1) 合理使用ConcurrentHashMap.putIfAbsent,在不使用synchronized的情况下保证操作的原子性,提高性能。
2) 对大块的逻辑操作,一定要使用finally做最终的善后处理,处理诸如解锁,移除一些变量,将某些错误的操作记录下来。