面向事件数据库Event Oriented Databases: 一种新的持久范式

NoSQL数据库大比拼: Cassandra, HBase, MongoDB, Riak的测试结果可以得出:根据不同的应用特点选择不同的NoSQL,没有万能的NoSQL数据库。

主要选择依据是读写比例,但是这个读写比例操作起来有难度,很多时候只有等到系统完成上线运行后才能知道这个读写比例。但是这时更换数据库相当于更换底层设施,不亚于在新马路上打洞。

有没有在写代码前完成正确的选型呢?

使用CQRS架构。

CQRS也是一种读写分离的架构,写的操作是命令,读的操作是查询,写和读是两条路线,很显然,写路线的存储采取写能力特别快的NoSQL如HBase或Cassandra;而读路线采取读能力比较快,比如Sharded MySQL或MongoDB。

关键是两者同步,结合Event Sourcing,写路线存储的应该是写事件,那么通过一个定时Job,不断从写存储数据库中读取写事件,通过逻辑播放,将结果再写入读数据库中即可。当然逻辑播放是一种批处理机制。如下图:

可参考NOSQL存储的基于事件的事务实现:

在这种情况下,由此诞生了相对于ORM的一种新的持久化方式:面向事件数据库Event Oriented Databases,文章
Event Oriented Databases : a New Kind of Persistence Paradigm对此进行详细描述。

文章首先对描述事实的数据进行了定义:

数据是一种概念的关系和其度量measure,度量主要由一种类型组成:量化特性,比如重量 总数和年龄,以及定性描述,名称 城市和日期。几种数据的在结构上组合形成了事实的描述。

例如:用户浏览电子商务网站并与它进行交互:她增加了一些商品到购物车,并去除了其中一些商品,最后当她已经完成了购买,进行订单验证,并随后支付。每个用户的操作是由系统处理。系统必须保证用户操作的ACID属性:原子性,一致性,完整性和耐用性。

关于最后一点,耐用性,是必不可少的环节,但是在非常复杂的情况下又会失败。耐用性是通过冗余机制实现的,比如持久化写磁盘等,以防止电子设备掉电。

这个案例中有两个视角:
1. 从购物车的这个视角看: 它跟随用户的动作改变(add a product, remove one, modify its quantity, recalculate the balance, etc.). 购物车总是处于一致的状态:带有总数的商品列表,总金额等(不能增加商品,总数和总金额不变,就破坏了购物车自身的一致性)。从这点来看,这个视角根据类似OO范式中“对象和状态”视角。

2. 如果从用户的视角看:用户发生了一系列动作(add, remove, change quantity, etc.). 当然,每个动作都会被购物车接受,产生了购物车的状态变化。这个视角更加面向函数FP一些。事件触发函数,函数改变了状态。事件是一种确定性的 可定义的 具有不变性的定义,这非常类似FP范式中的 « function » 视角。

待续..

事件定义:

一个事件描述过去发生的改变,每个事件触发一个动作,这个动作修改了对象的状态,这个接受改变的对象将被切换到下一个状态,然后将被下一个事件触发的动作改变。当这个对象切换到一个特定状态,也能触发其内部事件。

举例:购物车增加了三个价格$100的商品,购物车总价变成$300.

如果这些事件从一个开始状态以同样顺序播放,那么最后的状态也是确定相同的,如果一个事件反转到其开始事件,那么状态也是能够回到其原始状态。

事件是一个衡量时间的隐式概念,事件的顺序是相对于时间的,事件某个发生的绝对点是由时钟来确定的,那也是一个绝对时间点。

可变的对象执行状态的切换,这些对象管理它们自己的生命周期,DDD取名这些对象为实体,注意不是值对象 (identity and state).

这些事件可原子性地被保存,任何时候可以获得对象状态,这种方式与传统持久化方式是不同的,传统方式持久只能保存开始或最后的状态值。

该文演示了购物车事件和状态对应关系:

该文观点和我们在事件、契约设计与BDD讨论的结果非常类似:

[该贴被banq于2012-11-06 11:48修改过]

用CQRS来来串联NOSQL,使之各自发挥最佳性能,这种架构模式确实新颖。从写数据库到读数据库这个过程本质上也是写数据到读数据库的过程,比如从hbase的数据同步到mongodb中,至少要做两件事情,1:为两个数据库定义可以转换的数据类型或者通用的DTO,2:数据到mongodb的时候要建立索引,分片等;这种同步过程是否有延迟,同步通道是否强劲,稳健,如何避免单点故障等,值得期待。

另外论读的速度,redis,Memcached,KT/TT的速度都很强的,就我目前使用的是Mongodb集群+KT+Memcached总的运行情况比较稳定。

最近在研究CQRS架构模式,期待你的下文,(:。
[该贴被bingyang于2012-11-06 15:37修改过]

这样设计,写入后如何与读取同步呢?

比如一个用户注册成功,去登录,却没有该用户?这样的问题如何解决呢?

请指教。


是否可以这样理解,写数据的时候操作内存对象状态,同时将事件流持久化,后台job将事件刷到真正的数据持久化介质上,而读数据首先来自内存的对象状态,所以不存在数据同步的问题。当内存崩溃后,又可以将事件流从恢复丢失的数据,并且通过真正的数据重建内存数据模型。不知道这样理解对吗?

我看网上有人说读数据的时候是去把事件在内存的对象上面放一遍,再读出来,应该不是吧?
[该贴被dotlife于2012-11-09 21:41修改过]

只要实现读写同步,什么方式都可以,主要是影响延迟。

事件定义:

一个事件描述过去发生的改变,每个事件触发一个动作,这个动作修改了对象的状态,这个接受改变的对象将被切换到下一个状态,然后将被下一个事件触发的动作改变。当这个对象切换到一个特定状态,也能触发其内部事件。


banq,你用这个跟我的那个示例代码对照一下
还有我之前的日记:
http://www.douban.com/note/95799451/