冲突可串行性判别算法实现

204 阅读4分钟

简介

在数据库管理系统与事务处理系统里,并发事务管理至关重要。多个事务同时读写共享资源,若管控不当,会引发数据不一致、脏读、幻读等问题,破坏系统可靠性。 冲突可串行性判别算法,旨在判定并发事务能否无冲突地按序串行执行,守护数据与事务处理的“秩序”。事务具ACID特性,并发时,像读写同一资源这类操作易有冲突,比如一事务写数据后另一事务读,前者回滚就致数据“脏”态。 该算法核心是依事务操作冲突关系构图,用拓扑排序查环。节点对应事务,边表冲突依赖,有环说明操作不可串行化、存在逻辑“死结”;无环则事务可串行化,能有序、合规执行。

代码实现思路

  1. 事务操作建模(TransactionOperation.javaTransactionOperation类定义事务操作,含transactionId标识事务、resuorceId指向操作资源、TypeREADWRITE)界定操作行为,构造函数助实例化封装信息,为后续流程奠基。
  2. 冲突判断逻辑(SerializableConflictChecker.javaisConflict方法):静态方法判断两事务操作冲突。先看transactionId,相同视作冲突;不同时,resuorceId不同或操作非WRITE才无冲突,紧扣读写冲突本质。
  3. 构建事务操作关系图(SerializableConflictChecker.javaisSerializable部分前置):以transactionIdHashMap按事务分组opList,建二维布尔数组graph作图存储。四层嵌套循环查操作对,依isConflict结果与操作索引,依冲突先后在graph添有向边勾勒冲突图。
  4. 拓扑排序与可串行化判断(SerializableConflictChecker.javaisSerializable部分后续):图建好,用拓扑排序查环判断可串行化。先算各节点(事务)入度存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));
 }

算法也是对其进行了正确判断

image.png