mysql cas mvcc_MVCC 、CAS 和CopyOnWrite

update一行的时候的时候不是in-place的修改,而是产生一个行的新版本,在新行上修改,最后有点类似copy on write array,在提交的时候切换到新版本。好处是不影响现有数据的读取,一致性好。

概括为:准备数据 + 原子commit 切换版本,和无锁数据结构实现的思路很像,先准备好数据,最后往结构上挂的那一下用CAS原子性保证。MVCC是把一个复杂事务的原子性问题转化到commit动作的原子性上来。

CAS 是一种思想:Conditional Update,后验保证一致,而锁是前验机制。CAS的基本框架

1)读取当前状态

2)基于读取的状态进行计算得到新状态。

3)Conditional Update:如果计算结果基于的状态没有变,则更新,否则回到第一步。

system model:

Server State A  —— Transaction  T——> Server State B

和一个版本管理系统比如TFS类比:changeset 就是 transaction, changeset number就是 transaction id

每个文件有版本,整个代码库也有版本,都是以changeset number做为版本号。整个代码库的版本历史就是所有的changeset number,是连续的,而文件的版本历史是非连续的,因为不是每个changeset都修改这个文件。

数据库

每个事务分配一个Transaction Id,是自增的。server的当前版本就是上一次成功提交的transaction Id

INNoDB实现MVCC的方法,每一行有2个隐藏字段,记录创建本行的事务ID和删除本行的事务ID(如果被删了的话)

在一个事务中:

对于insert,把当前事务ID填到新行的Create字段

对于delete,把当前事务ID填到要删除的行的Delete字段

对于update,产生一个新行,把旧行标记删除

对于select,返回的记录满足两个条件

1) 行的create事务ID 小于等于Server 当前版本号,(或者等于当前事务ID,本次事务创建的行)这确保读到的数据都是已经提交的

2)行的delete事务ID 大于当前事务ID ,当前事务之后的事务才删除的数据在当前事务可以被读到。

具体的,每个事务会拷贝一个“当前活动事务列表“,用其中最小的那个做为select的上界。相当于在事务开始的时候保存了server的版本号,之后就算有别的事务提交increment了server的版本,当前事务一直用自己保存的那个,Repeatable read。

事务提交就是更新server的当前版本为当前commit的事务号。

标记删除的数据用一个后台janitor定期处理,这个叫purge。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MVCC(Multi-Version Concurrency Control)是MySQL InnoDB存储引擎的一种并发控制机制,可以提高数据库的并发性能。下面是Java实现MVCC的大致思路: 1. 在Java中实现MVCC需要使用到数据库连接池,可以使用开源的c3p0或Druid连接池。 2. 在InnoDB存储引擎中,每行数据都有一个隐藏的6字节的事务ID(即版本号),表示该行数据的版本号。在Java中,可以使用一个类来表示这个版本号,比如: ```java public class TxnId { private int trxId; // 事务ID private int rollPointer; // 回滚指针 // 省略get、set方法 } ``` 3. 当一个事务开始时,需要从连接池中获取一个数据库连接,并设置该连接的事务隔离级别为可重复读(Repeatable Read)。在Java中,可以使用JDBC来设置事务隔离级别。 ```java try { Connection conn = pool.getConnection(); conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); conn.setAutoCommit(false); // 执行一些SQL语句 conn.commit(); } catch (Exception e) { // 异常处理 } ``` 4. 当从数据库中读取数据时,需要读取该数据的最新版本。如果该数据的版本号比当前事务的版本号旧,说明该数据已经被其他事务修改过了,当前事务不能读取该数据。在Java中,可以使用SELECT ... FOR UPDATE语句来实现这个功能。 ```java try { Connection conn = pool.getConnection(); conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); conn.setAutoCommit(false); String sql = "SELECT * FROM table WHERE id = ? FOR UPDATE"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, id); ResultSet rs = ps.executeQuery(); if (rs.next()) { // 读取数据 } conn.commit(); } catch (Exception e) { // 异常处理 } ``` 5. 当向数据库中插入、更新、删除数据时,需要生成一个新的版本号,并将该版本号保存到数据中。在Java中,可以使用UPDATE ... SET语句来实现这个功能,例如: ```java try { Connection conn = pool.getConnection(); conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); conn.setAutoCommit(false); int newTrxId = getNextTrxId(); String sql = "UPDATE table SET value = ?, trx_id = ? WHERE id = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, value); ps.setInt(2, newTrxId); ps.setInt(3, id); int rows = ps.executeUpdate(); if (rows == 1) { // 更新成功 } conn.commit(); } catch (Exception e) { // 异常处理 } ``` 6. 当事务提交时,需要将该事务的版本号保存到事务ID列表中。在Java中,可以使用一个列表来保存事务ID,例如: ```java public class TxnIdList { private List<TxnId> txnIds = new ArrayList<>(); // 添加事务ID public void addTxnId(TxnId txnId) { txnIds.add(txnId); } // 删除事务ID public void removeTxnId(TxnId txnId) { txnIds.remove(txnId); } // 判断事务是否已提交 public boolean isCommitted(TxnId txnId) { for (TxnId id : txnIds) { if (id.getTrxId() == txnId.getTrxId() && id.getRollPointer() != txnId.getRollPointer()) { return true; } } return false; } } ``` 7. 当事务回滚时,需要根据回滚指针来回滚数据。在Java中,可以使用ROLLBACK语句来回滚事务,例如: ```java try { Connection conn = pool.getConnection(); conn.rollback(); } catch (Exception e) { // 异常处理 } ``` 这样,就可以在Java中实现MVCC机制了。当然,这里只是一个简单的示例,实际上还有很多细节需要考虑,比如事务的隔离级别、事务的提交和回滚、事务ID的生成等等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值