Skip to content

中文 系统实现

domino-succ edited this page Sep 30, 2014 · 5 revisions

系统架构

现有的分布式事务引擎架构设计可以分为两类,集成于客户端,如Percolator、HBaseSI、HAcid等;亦或将事务控制模块独立出来,形成一个事务控制系统,如ReTSO、CloudTPS等。

将事务处理完全放置于客户端会带来更大的不可控性,客户端软硬件环境的稳定性与性能都不可预知,因此事务系统的处理性能不得而知;同时将事务处理逻辑完全放在客户端将大大增加客户端与服务端的通信成本,很多不必要的信息,如数据行和事务表中包含的事务信息都必须通过网络传递给客户端,还有,在事务处理过程,一个读写请求可能包含的对同一行数据的多次读写,而这些全部都需要客户端与服务端进行多次远程通信才能完成,因此是非常浪费的。

独立的事务处理系统也有很多弊端。对于事务处理系统本身,其可扩展性及可用性都需要经过长时间大规模测试才能被验证,而现有的事务引擎如ReTSO、CloudTPS等,要么是本身的设计就缺乏可扩展性,要么则是没有经过长时间大规模测试以及广泛的应用。同时,独立的事务处理系统除事务本身的并发控制之外,还带来了大量其他的设计、开发及维护开销,如事务处理集群的分布式管理、维护与升级等。

在Domino的系统架构设计中,我们使用HBase Coprocessor框架实现了一类全新的事务处理集群——依附于HBase RegionServer的Domino Endpoint Transaction System(DETS),DETS扩展性与HBase一样强大,路由协议也完全基于RowKey,省去了大量开发与维护成本的同时,还增强了系统可控性与可靠性,免去了绝大部分不必要的客户端与服务端的网络通信,从而大大提升了事务系统的性能。同时,Domino还利用Coprocessor框架实现了事务ID的生成模块(Domino Timestamp Oracle,DTO)以及事务元数据管理模块(Transaction Metadata Endpoint,TME),下图展示了Domino的系统架构设计。

<IMG SRC=http://g.hiphotos.bdimg.com/album/s%3D1400%3Bq%3D90/sign=63d157e652da81cb4ae687c96256eb67/14ce36d3d539b600f44cb0a5ea50352ac65cb726.jpg>

接口设计与使用(API)

Domino接口设计的原则是尽可能接近HBase原生接口,以降低使用者的学习成本,同时为使用者提供出显式的事务控制。目前,Domino只提供Java版本的接口。

客户端初始化

Domino提供了两种客户端的初始化方式:以ZooKeeper集群地址作为参数,或以Configuration对象作为参数,Domino对象封装了数据表管理以及获得事务句柄的接口:

  • public Domino(String zookeeperAddress) throws IOException;
  • public Domino(Configuration config) throws IOException;

Domino句柄:数据表管理与事务开始

1)表的创建,使用HBase的HTableDescriptor作为参数传递给Domino Client,在真正创建表之前,Domino Client会将Domino内置列簇加入Descriptor中,内置列簇附带In Memory属性,尽可能地提高事务元数据的存取性能;与此同时,Domino还提供了带有split参数的表创建接口:

  • public void createTable(HTableDescriptor table) throws IOException;
  • public void createTable(HTableDescriptor table, byte[][] splitKeys) throws IOException;
  • public void createTable(HTableDescriptor table, byte[] startKey, byte[] endKey, int numRegions) throws IOException;

2)表的删除:

  • public void dropTable(byte[] name) throws IOException;
  1. 开始一个事务,返回一个事务句柄,用来进行事务操作:
  • public Transaction startTransaction() throws IOException;

事务操作

事务的提交、回滚以及存取操作都被封装在Transaction类中。

  1. 读取一条记录,使用HBase的数据结构Get作为参数,返回HBase Result数据结构返回数据:
  • public Result get(Get get, byte[] table) throws IOException;
  1. 写入一条记录,使用HBase的数据结构Put作为参数,通过这个接口写入的数据被认为是无状态更新的数据,即put中的值不应该是根据任何之前在本事务中读取过的数据得来的:
  • public void put(Put put, byte[] table) throws IOException;
  1. 写入一条有状态更新的记录,若参数put中的值基于任何之前在本事务中读取过的数据,则必须使用这个接口来保证数据的完整性:
  • public void putStateful(Put put, byte[] table) throws IOException;
  1. 删除一条记录,指定Rowkey,删除整行数据:
  • public void delete(byte[] row, byte[] table) throws IOException;
  1. 扫描一段记录,使用HBase的数据结构Scan作为参数,返回一个ResultScanner句柄:
  • public ResultScanner scan(Scan scan, byte[] table) throws IOException;

scan返回的句柄是Domino实现了ResultScanner的实例,由于scan方法的特殊性,无法把scan的处理完全放在DETS中,因此,客户端在scan时会对扫描得到的数据进行简单的处理,若遇到.status数据,Domino Client会将本行记录发回DETS做单行读取的处理,保证了scan读取数据的一致性。

  1. 事务的提交:
  • public void commit() throws IOException;
  1. 事务的回滚:
  • public void rollback() throws IOException;

系统部署

Domino的部署非常简单,先决条件只有一个,就是具备一个可以运行的、版本高于0.94的HBase集群。Domino有4个库文件:

  • domino-client-.jar,Domino的客户端类库;
  • domino-common-.jar,Domino的Common类库;
  • domino-core-.jar,Domino的核心Endpoint类库;
  • domino-id-service-.jar,Domino的事务ID服务类库。

在启动HBase集群前,需要将4个Domino的库文件放入所有HBase部署目录的lib文件夹下,放置完毕后,启动HBase,Domino的服务端就全部部署完成了。

客户端在使用Domino时,只需将domino-client、domino-common及domino-id-service三个库文件包含到Java Build Path中即可。 Domino的所有内置表都会在HBase初始化时自行创建好,在使用时,需要用到Domino事务特性的表全部都要使用Domino提供的表创建接口进行。

另外,由于Domino的设计思想用到了HBase数据模型的Timestamp维度,所有数据的Timestamp维度都由Domino管理,所以任何设置了Timestamp的Get、Put和Scan参数都会被Domino重写,通过Domino进行管理的数据在使用者角度来看将失去Timestamp特性,数据模型更加类似于传统的关系模型

Clone this wiki locally