简介
在数据库管理系统与事务处理系统里,并发事务管理至关重要。多个事务同时读写共享资源,若管控不当,会引发数据不一致、脏读、幻读等问题,破坏系统可靠性。 冲突可串行性判别算法,旨在判定并发事务能否无冲突地按序串行执行,守护数据与事务处理的“秩序”。事务具ACID特性,并发时,像读写同一资源这类操作易有冲突,比如一事务写数据后另一事务读,前者回滚就致数据“脏”态。 该算法核心是依事务操作冲突关系构图,用拓扑排序查环。节点对应事务,边表冲突依赖,有环说明操作不可串行化、存在逻辑“死结”;无环则事务可串行化,能有序、合规执行。
代码实现思路
- 事务操作建模(
TransactionOperation.java):TransactionOperation类定义事务操作,含transactionId标识事务、resuorceId指向操作资源、Type(READ或WRITE)界定操作行为,构造函数助实例化封装信息,为后续流程奠基。 - 冲突判断逻辑(
SerializableConflictChecker.java的isConflict方法):静态方法判断两事务操作冲突。先看transactionId,相同视作冲突;不同时,resuorceId不同或操作非WRITE才无冲突,紧扣读写冲突本质。 - 构建事务操作关系图(
SerializableConflictChecker.java中isSerializable部分前置):以transactionId用HashMap按事务分组opList,建二维布尔数组graph作图存储。四层嵌套循环查操作对,依isConflict结果与操作索引,依冲突先后在graph添有向边勾勒冲突图。 - 拓扑排序与可串行化判断(
SerializableConflictChecker.java中isSerializable部分后续):图建好,用拓扑排序查环判断可串行化。先算各节点(事务)入度存inDegree数组,入度为0的入queue作起点,循环取节点处理、更新邻接节点入度,入度0则入queue。最后对比已处理节点数与事务总数,相等即无环、事务可串行化,反之不可。
代码实现
TransactionOperation.java
package org.example.并发控制.冲突可串行化判断;
// 事务操作
public class TransactionOperation {
int transactionId;
int resuorceId;
Type type;
enum Type{
READ, WRITE
}
public TransactionOperation(int transactionId, int resuorceId, Type type) {
this.transactionId = transactionId;
this.resuorceId = resuorceId;
this.type = type;
}
public TransactionOperation() {
}
}
SerializableConflictChecker.java
package org.example.并发控制.冲突可串行化判断;
import java.util.*;
public class SerializableConflictChecker {
// 判断俩事务操作是否冲突
public static boolean isConflict(TransactionOperation a, TransactionOperation b) {
if (a.transactionId == b.transactionId)
return true;
return !(a.resuorceId != b.resuorceId || (a.type != TransactionOperation.Type.WRITE && b.type != TransactionOperation.Type.WRITE));
}
// 可串行化判断
public static boolean isSerializable(List<TransactionOperation> opList) {
// 将事务操作按照事务id分组
// 假设事务id是连续的,从0开始
Map<Integer, List<TransactionOperation>> transacList = new HashMap<>();
for (TransactionOperation operation : opList) {
if (!transacList.containsKey(operation.transactionId)) {
transacList.put(operation.transactionId, new ArrayList<>());
}
transacList.get(operation.transactionId).add(operation);
}
// 生成一个大小为 n (事务数量)的图,然后4层for循环暴力遍历,每两个事务(A,B)里面的两对操作是否冲突,
// 若冲突,则将列表中先发生的操作对应的事务 产生一条边 指向另一个事务
int n = transacList.size();
boolean[][] graph = new boolean[n][n];
Set<Integer> keySet = transacList.keySet();
for (Integer i : keySet) {
for (Integer j : keySet) {
if (i == j) continue;
List<TransactionOperation> list1 = transacList.get(i);
List<TransactionOperation> list2 = transacList.get(j);
for (int k = 0; k < list1.size(); k++) {
for (int l = 0; l < list2.size(); l++) {
if (isConflict(list1.get(k), list2.get(l))) {
int i1 = opList.indexOf(list1.get(k));
int i2 = opList.indexOf(list2.get(l));
// 列表中先发生的操作对应的事务 产生一条边 指向另一个事务
if (i1 < i2) {
graph[i][j] = true;
} else {
graph[j][i] = true;
}
}
}
}
}
}
// 最后判断图中是否有环,有环则不可串行化
// 使用拓扑排序判断是否有环
// 计算入度
int processedNodes = 0; // 已处理节点数
int[] inDegree = new int[n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (graph[i][j]) {
inDegree[j]++;
}
}
}
// 初始化:将入度为0的节点加入队列
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (inDegree[i] == 0) {
queue.offer(i);
}
}
// 拓扑排序
while (!queue.isEmpty()) {
int i = queue.poll();
processedNodes++;
for (int j = 0; j < n; j++) {
if (graph[i][j]) {
inDegree[j]--;
if (inDegree[j] == 0) {
queue.offer(j);
}
}
}
}
return processedNodes == n;
}
}
测试
@Test
public void test() {
// 可串行化样例
List<TransactionOperation> opList = new ArrayList<>();
opList.add(new TransactionOperation(0, 1, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(0, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 1, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(2, 0, TransactionOperation.Type.WRITE));
System.out.println(SerializableConflictChecker.isSerializable(opList));
}
@Test
public void test2() {
// 不可串行化样例
List<TransactionOperation> opList = new ArrayList<>();
opList.add(new TransactionOperation(0, 1, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 1, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(0, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(2, 0, TransactionOperation.Type.WRITE));
System.out.println(SerializableConflictChecker.isSerializable(opList));
}
@Test
public void test3() {
// 可串行化样例
List<TransactionOperation> opList = new ArrayList<>();
opList.add(new TransactionOperation(1, 0, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(0, 1, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(1, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(2, 0, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(0, 1, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(2, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 1, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(1, 1, TransactionOperation.Type.WRITE));
System.out.println(SerializableConflictChecker.isSerializable(opList));
}
@Test
public void test4() {
// 不可串行化样例
List<TransactionOperation> opList = new ArrayList<>();
opList.add(new TransactionOperation(1, 0, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(0, 1, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(1, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 1, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(2, 0, TransactionOperation.Type.READ));
opList.add(new TransactionOperation(0, 1, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(2, 0, TransactionOperation.Type.WRITE));
opList.add(new TransactionOperation(1, 1, TransactionOperation.Type.WRITE));
System.out.println(SerializableConflictChecker.isSerializable(opList));
}
算法也是对其进行了正确判断