1. Segment databases into functional areas.
要支持eBay的业务,不是一两个数据库所能实现的。事实上,eBay拥有超过1000台不同Logical databases部署在大约400台物理数据库服务器上。所以eBay所做的就是把这么多的数据库按照功能的不同切分成不同的种类:User Info, Item Info,Account, Billing, Product Information…每一类都有多个数据库。
紧接的一个问题就是怎么组织数据,这么多的数据库,给定一些数据,怎么知道这些数据应该存放在哪些数据库中?如果大家不遵从统一的标准,很容易造成混乱的。所以在eBay有一些统一的数据库建模标准:
2. Group data using standard data modeling techniques
这些规定主要是3个方面的:
- Cardinality(1:1,1:N,M:N):是一对一,还是1对多,还是多对对?
- Data relationships 数据之间的关系
- Usage characteristics 使用这些数据的特点
在有这么多数据库的系统中,Logical Hosts的概念非常重要,其思路很简单,但是非常有用。
3. Logical Hosts
放了方便应用程序的开发,应用程序层和数据库进行交互的时候,并不知道正在和自己交互的是哪些物理数据库,应用程序只知道逻辑数据库。而逻辑数据库和物理机器的映射是由专门的模块负责的。这样的好处就是:当我们移动物理数据库的时候,应用程序层不需要code change。比如Capacity不够的时候,我们把数据库搬到容量更大的机器或者利用Partition原理加入更多的物理数据库。又或者我们可以把多个逻辑数据库部署在同一个物理机器上。
- Abstract application’s logical representation from host’s physical location
- Support collocating and separating hosts without code change
这样一来,对于Operation Team来说,也更加方便灵活。
[Databases]:Horizontal Split
当我们提到User Host,我们不是指一台服务器,我们是指多台服务器。假设我们把所有的User信息分成20份(20个partition),第一个1/20的User放在UserHost0,第二个1/20的User信息放在UserHost2…
- Split databases horizontally along primary access path。
确定了Partition的数目,那怎么来决定哪些User是第一个1/20,哪些是第二个1/20的呢?主要是根据如何访问这些数据的,我们的希望是能把数据平均的分配到这些Partition上去,这里说的平均不单单是指数量上的平均,更重要的是访问时候流量的平均。所以设计的时候主要考虑访问方式!
- Different split strategies for different user cases。
现在主要有两类策略:
- Modulo on key(item id, user id,etc.) 根据模值进行Partition。
- Lookup or range-based。
这里我想补充一点,在这个PPT里面所提到的Partition是个通用的概念,和Split是等同的。并不是专指Oracle 里面的Partition Table。我们完全可以有多个Split的数据库,每个数据库里面的数据表再进行Partition。
- Aggregation/routing in Data Access Layer(DAL)
同样,我们不希望这样的Split会对应用程序层造成影响。应用程序不需要关心要到哪个物理机器上去哪数据,在Partition的情况下,应用程序也不需要关心我的数据到底在哪个Partition数据库上。这些繁文缛节都有DAL来处理。DAL还负责将数据库操作(Create/Read/Update/Delete) "Routes"到相应的Split(s)上去。
如果随着时间的推移,eBay发现原先的Partition并没有很好的Balance数据,需要做Rebalance,比如说本来Item是按照模10进行Split的,现在随的登录物品的增多,需要增加数据库,变成模20进行Split了,这样的改变不应该导致应用层受影响。
关于在Split情况下,应用层和数据库部署细节的隔离,Randy的原文是:
- Abstracts developers from split logic,logical-physical mapping
- Routes CRUD operation(s) to appropriate split(s)
- Supports rebalancing through config change
到这里,我以为关于数据库的Partition的介绍就完了呢。然后Randy开始介绍一段非常有eBay特色的项目,他先问了个问题,这么多分布的数据库,一个业务逻辑需要跟很多数据库打交道,牵涉到很多表,而这些数据库,数据表又分布在不同的地方,怎么保证操作的正确性,原子性。但是我没有想到Transaction在这样的环境下所面临的挑战。
今天写这篇学习笔记的时候,让我想起来几天天看数据库系统概念(Database System Concepts)里第6章开始的几句话[The Task of creating a database application involves several tasks]:
- Design of the database schema
- Design of the programs that access and update the data!
- Design of a security schema to control access to data
千万不要设计出来的东西,样子很好看,理论上很漂亮,但是在实际中不能用,或者用起来常出问题,或者维护成本很高。
Partition Everything[Databases]->No Database Transactions
我在标题上使用’->’表示No Database Transactions是一个推论(Corollary)。是因为eBay采用了前面介绍的Partition Everything的策略所造成的。这里我先列出eBay的事务策略,然后再逐一解释。
- eBay’s transaction policy
- Absolutely no client side transactions,two-phase commit etc.
- Auto-commit for vast majority of DB writes.
- Anonymous PL/SQL blocks for multi-statement transactions with single DB.
eBay不使用(explicit) transactions,准确的说是不是用distributed transactions。也就是说我们从不start an transaction, 然后做很多操作(operations),最后commit那个transaction。在eBay,大多数的情况是这样的,假设我们要做3个操作,我们做第一个操作,然后commit,我们做第二个操作,然后commit,我们做最后一个操作,然后commit,很多时候我们看到的SQL都不复杂,就是只有一个statement,所以多数情况下的DB操作就都是auto-commit的(implicit transaction)。如果碰到我们一定要把很多操作捆绑在一起放在一个事务中处理,就需要走另外的流程了。也就是上面提到的第3点–anonymous PL/SQL blocks,本质上这样的block就是在DAL层的store procedure,都是小心翼翼的手工写的,这样是为了能够让多条statements,在一个调用中由一个事务完成。
<注:关于anonymous PL/SQL block我还是不太理解,我到eBay后也从来没有碰到过,下个礼拜去问问COC的DBA,看看有什么收获。>
Randy在这个时候跟我们开玩笑说,上面提到的方法不是我们学校里面学到的方法,也不是我们在进行数据库编程的时候常用的方法,但是上面的方法是目前eBay能够进行Scale的唯一可行的方法。原因是 :(Here’s WHY)
eBay的大多数业务操作,比如用户竞价一个物品(make a bid),或者列一个新物品(list a new item),都牵涉到很多数据库实体(数据库实体指实体表或者关系表)。可能牵涉到的实体有:User, 一个item,或者是多个item,或者是多个User。而我们所要进行交互的不是一个数据库,而是多个,甚至上百个,甚至上千个。上面所提到的操作(make a bid, list new item…)常常有好几个步骤才能完成,问题是:非常非常有可能(typically),其实事实上也是,属于同一个业务操作的不同的步骤会在不同的database上执行。我是一个seller,我的信息在 seller数据库上,我卖一个物品给buyer,buyer的信息在另外一个数据库上,有人bid我的商品,item的current price要更新,这些信息在item的数据库上…如果我们要做一个distributed transaction的话,我们做的就是一个two-phase commit。这意味着一个业务操作中牵涉到的多个数据库操作要么同时成功,要么都不成功。实现这种方法的常用手段就是locking!这会降低系统并发,首先受到影响的是performance,速度会很慢。而且对availibility的影响也很大,举个例子,假设list a new item要牵涉28个数据库操作,从理论上来说,这28个操作有可能会在28个不同的数据库上执行,想象一下把这28个操作放在一个distributed transaction中执行,意味着锁住了28个数据库资源,在那么大的用户并发情况下,eBay站点会怎样,用Randy的原话是: It will go into a hell!
但是我们仍然需要保证consistency。eBay是怎么做到这一点的呢?
- Consistency without transactions
-
- Careful ordering of DB operations.
- Eventual consistency through asynchronous event or reconciliation batch.
比如list a new item。eBay不要求所有的28个数据库操作都在同一时间完成,有些步骤可以晚些执行。在没有distributed transaction的情况下保证数据一致性的第一招就是:
小心翼翼的安排多个数据库操作的执行顺序!
举个例子,如果我们有一个master record,关联着10个children records。通常情况下,我们会先插入那个master record然后再插入10个children records。因为我们学习数据库设计的时候就是这样的,children records表中还有一个到master record的外键呢!而在eBay,操作顺序是反的,先插入children records,如果都成功了,再插入master record。原因是,如果我们先插入 master record,成功了,但是在插入children record的时候出错,因为没有事务机制,数据就不完整了。但是如果先插入children records成功了,在插入master record的时候出错,由于master record没有,逻辑还是对的。唯一的问题是我们在children records表中有了垃圾数据,那怎么样呢?没有关系的!Randy的原话是:"well, that’s O.K.-:)"
第二招就是eBay使用异步事件来纠正inconsistency!比如一个业务操作有10个步骤,在进行到第5个步骤的时候出错了,就发送一个异步事件让该事件负责对前面的4个步骤进行“善后”。
这里解释一下什么叫做reconciliation。这是一个在银行系统中常用的概念:http://en.wikipedia.org/wiki/Bank_reconciliation
"Bank reconciliation allows companies or individuals to compare their account records to the bank’s records of their account balance in order to uncover any possible discrepancies."eBay也使用了这一技术,特别是在billing中。我们注意到异步事件和reconciliation batch都无法保证immediate consistency,但是我们需要immediate consistency吗?或者说eBay有能力鱼与熊掌兼得吗?
- Consistency is not always required or possible(!)
-
- To guarantee availability and partition-tolerance, we are forced to trade off consistency(Brewer’s CAP Theorem).
- Leads unavoidably to systems with BASE semantics rather than ACID guarantees.
- Consistency is a spectrum, not binary.
在计算机科学中有一个CAP理论,意思是,如果你有一个系统,你Partition了这个系统,而且你要求这个系统有High Availibility,那你就不能同时实现immediate consistency。Immediate C onsistency、High A vailibility和P artition Infrastrunture,只能同时拥有其二。
Partition Everything[Databases]->Minimize Database Resources
eBay数据库Partition的第二个推论就是:因为eBay的数据库系统使用率非常高,很多业务都严重依赖数据库系统,所以为了让数据库更好的为更多的应用服务,需要尽量减少单个应用对数据库服务器的资源消耗。
- No business logic in database
在数据库上没有业务逻辑,这就意味着:
- No stored procedures
- Only very simple triggers(default value population)
- Move CPU-intensive work to applications
将所有的大量耗费CPU资源的工作全部移到应用程序中来做,这一点上次我参加COC DBA给我们做的Basic SQL Tuning培训的时候已经听过一次,这次Randy再说一遍就理解地更深刻了。比如Join,两张表的Join本来就耗费资源,况且在Partition Everything的情况下,非常有可能这两张表是在不同的数据库服务器上的。下面3点是典型的 CPU intensive的操作:
- Referential Integrity
- Joins
- Sorting
- Extensive use of prepared statements and bind variables
这一点我想不单单是eBay,其他的网站,应用也都应该这样做的。
好,到这里,Partition Everything中关于数据库的部分就介绍完了。接下来将会介绍应用层的Partition。