先明确两个概念:
* 事务:事务必须符合ACID特性
* 单行:一行数据中包含多个列(column),一个事务中可以操作同一行中的多个列,在操作过程中保证ACID。
* 单行事务:仅要求单行数据符合事务特性,行于行之间,表与表之间不要求符合事务特性
看一个Bigtable单行事务应用的例子:
// 选自Google关于Procalator的论文《Large-scale Incremental Processing Using Distributed Transactions and Notifications》 // 行中包括的列名: key bal:data bal:lock bal:write bool Prewrite(Write w, Write primary) { Column c = w.col; bigtable::Txn T = bigtable::StartRowTransaction(w.row); //Abort on writes after our start timestamp . . . if (T.Read(w.row, c+"write", [start ts , ∞])) return false; //. . . or locks at any timestamp. if (T.Read(w.row, c+"lock", [0, ∞])) return false; T.Write(w.row, c+"data", start ts , w.value); T.Write(w.row, c+"lock", start ts , {primary.row, primary.col}); // The primary’s location. return T.Commit(); }当调用bigtable::StartRowTransaction后行事务即开始,其后针对w.row这一行的操作在整个操作过程中好像独享这一行的访问权限.
从例子中可见,事务开始后代码中分别对write, lock,date三个列进行了读写,如果没有事务的保证则可能出现脏读、写丢失等问题。那么,Bigtable是如何保证对单行操作的事务性呢?
* Bigtable支持版本的概念,支持COW.
在bigtable的论文中有这么两段话:
Version: Each cell in a Bigtable can contain multiple versions of the same data; these versions are indexed by timestamp. COW: The only mutable data structure that is accessed by both reads and writes is the memtable. To re- duce contention during reads of the memtable, we make each memtable row copy-on-write and allow reads and writes to proceed in parallel.利用COW可以使得一个事务开始后其它事务对数据进行的写操作不会干扰当前事务中的读操作。如果利用COW技术,要求在提交的时候需要对数据加锁,保证原子提交。并且,提交时只能提交修改了的数据,对于未修改数据不可以提交(貌似比较显然。。)
今天与传辉交流,得知Pyramid中曾经用行锁的方式来实现行事务。但是该方法最后没有实际使用,因为行锁在实际使用中存在较多问题,如加锁解锁数据包乱序怎么办?加锁后宕机怎么办?虽然都有办法,但解决这些问题的过程中无疑会让实现变得复杂。
单机+行锁,肯定是可以很容易实现单行事务的。仅仅两个事务同时操作同一行的时候才会出现锁争用,对于不同行的操作不会存在锁争用的问题,因此效率基本可以保证。
快照技术(COW)理论上也可以用于实现事务,仅仅在commit的瞬间需要加锁,效率较行锁的方式高。但技术上也复杂一些。