首先我们先来解释一下题目的关系:
MVCC(多版本并发控制):数据库事务的实现
数据库中的事务机制非常简单使用,比java的锁,原子类强很多。
因此,为了提高性能,模仿数据库的事务机制产生了软件事务内存STM。(JAVA第三方就有提供STM)
下面我们用三段代码展示STM:
这个是我们用java来模拟实现转账的业务,可以发现这个是线程不安全的,需要我们加锁,同时要防止死锁等问题。
class UnsafeAccount {
//余额
private long balance;
//构造函数
public UnsafeAccount(long balance) {
this.balance = balance;
}
//转账
void transfer(UnsafeAccount target, long amt){
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
若是使用数据库事务,就会 非常简单,可以保证事务的ACID(原子性,一致性,隔离性,持久性),同时没有死锁等问题。
Connection conn = null;
try{
//获取数据库连接
conn = DriverManager.getConnection();
//设置手动提交事务
conn.setAutoCommit(false);
//执行转账SQL
......
//提交事务
conn.commit();
} catch (Exception e) {
//出现异常回滚事务
conn.rollback();
}
使用STM实现转账操作,java不支持STM,可以引入第三方类库Multiverse实现
class Account{
//余额的类型Long->TxnLong
private TxnLong balance;
//构造函数
public Account(long balance){
this.balance = StmUtils.newTxnLong(balance);
}
//转账
public void transfer(Account to, int amt){
//原子化操作
atomic(()->{
if (this.balance.get() > amt) {
this.balance.decrement(amt);
to.balance.increment(amt);
}
});
}
}
MVCC原理
MVCC其实可以理解为,在数据库事务开启的时候,会给数据库打一个快照,以后再该事务中所有的读写操作都是基于这个快照的,当提交事务的时候,如果所有读写过的数据在该事务执行期间没有发生变化,就提交数据,如果发生变化了,说明在该事务执行期间,其他事务提交了,产生冲突,不能提交。
那么如何判断数据是否发生变化?给每条数据添加一个版本号,每次修改数据都会增加版本号的值。
自己实现STM
按照MVCC原理,数据的每次修改都对应一个版本号,不存在只修改数据或者版本号,所以这里我们可以使用不变性模式把数据和版本号封装成不可变的对象。 更加符合并发编程。
不变性模式参考,这篇并发设计模式1-避免共享文章。
如果想要查看具体实现请看实现STM