上一章说了事务的问题,下面就必然要看看如何实现锁机制的。
ThreadLocal 实现事务管理
还是从 addVertex 出发,调用api时,web server会
根据从线程池分配一个线程给 用户,用户获取一个 hugegraph实例,
并调用 addVertex方法。
@Override
public Vertex addVertex(Object... keyValues) {
return this.graphTransaction().addVertex(keyValues);
}
private GraphTransaction graphTransaction() {
/*
* NOTE: this method may be called even tx is not opened,
* the reason is for reusing backend tx.
* so we don't call this.verifyOpened() here.
*/
GraphTransaction graphTx = this.graphTransaction.get();
if (graphTx == null) {
graphTx = openGraphTransaction();
this.graphTransaction.set(graphTx);
}
return graphTx;
}
this.graphTransaction 是一个 private ThreadLocal<GraphTransaction> graphTransaction; 对象。
ThreadLocal 经常被用于 多线程下的事务管理,本质上可以理解为一个 Map<Thread,Transaction>, 他的key
为当前线程,也就是说为每个线程保留一个 Transaction对象。
好了,到此位置,我们知道 不同用户的每一个 addVertex 操作都会有不同 的transaction 维护,
那么很自然的就会提出一个问题,如果多个线程同时 添加一个节点, 会不会存在更新丢失的问题呢?
比如,我对A节点,设置其值为 1, 另外一个用户后面对A节点设置为 3, 结果会不会是1 呢? hugeraph 是否做了保护。
hugegraph 如何做 写的事务隔离
public HugeVertex addVertex(HugeVertex vertex) {
this.checkOwnerThread();
// Override vertexes in local `removedVertexes`
this.removedVertexes.remove(vertex.id());
try {
this.locksTable.lockReads(LockUtil.VERTEX_LABEL_DELETE,
vertex.schemaLabel().id());
this.locksTable.lockReads(LockUtil.INDEX_LABEL_DELETE,
vertex.schemaLabel().indexLabels());
// Ensure vertex label still exists from vertex-construct to lock
this.graph().vertexLabel(vertex.schemaLabel().id());
/*
* No need to lock VERTEX_LABEL_ADD_UPDATE, because vertex label
* update only can add nullable properties and user data, which is
* unconcerned with add vertex
*/
this.beforeWrite();
this.addedVertexes.put(vertex.id(), vertex);
this.afterWrite();
} catch (Throwable e){
this.locksTable.unlock();
throw e;
}
return vertex;
}
从这里可以发现,addVetex 只做了 VERTEX_LABEL_DELETE ,INDEX_LABEL_DELETE 操作的互斥锁。
也就是说在进行添加数据的时候, 不能对schema进行增删改查,但是不同的线程还是可以对同一个 vertex进行
操作,那么就会存在丢失更新的问题。
不知道是不是我代码看得不全? 还是hugegraph目前暂时不支持严格的事务隔离。
hugegraph锁是如何实现和复用?
我们看看LockUtils的代码
public static final String INDEX_LABEL_DELETE = "il_delete";
public static final String EDGE_LABEL_DELETE = "el_delete";
public static final String VERTEX_LABEL_DELETE = "vl_delete";
public static final String INDEX_LABEL_REBUILD = "il_rebuild";
public static final String INDEX_LABEL_ADD_UPDATE = "il_add_update";
public static final String EDGE_LABEL_ADD_UPDATE = "el_add_update";
public static final String VERTEX_LABEL_ADD_UPDATE = "vl_add_update";
public static final String PROPERTY_KEY_ADD_UPDATE = "pk_add_update";
public static final long WRITE_WAIT_TIMEOUT = 30L;
public static void init(String graph) {
LockManager.instance().create(join(graph, INDEX_LABEL_DELETE));
LockManager.instance().create(join(graph, EDGE_LABEL_DELETE));
LockManager.instance().create(join(graph, VERTEX_LABEL_DELETE));
LockManager.instance().create(join(graph, INDEX_LABEL_REBUILD));
LockManager.instance().create(join(graph, INDEX_LABEL_ADD_UPDATE));
LockManager.instance().create(join(graph, EDGE_LABEL_ADD_UPDATE));
LockManager.instance().create(join(graph, VERTEX_LABEL_ADD_UPDATE));
LockManager.instance().create(join(graph, PROPERTY_KEY_ADD_UPDATE));
}
考虑到整个操作过程中可能对不同的节点,关系,索引进行加锁处理,
为了避免重复的锁对象的创建, hugegraph 使用了lockManager,实现把可能需创建的锁进行分类。比如
针对 删除节点的 group,删除index 的LockGroup 等。
当addVertex的时候,比如增加一个 Company 的节点。 lockManager会检查 Company 上是否已经有读锁,如果有就复用
否则就创建。
同样,在修改Company的schema的时候,则会获取一个Company 的写锁。
就这样实现了全局的锁的复用。
网友评论