跨库Join的问题
在拆分之前,系统中很多列表和详情页所需的数据是可以通过sql join来完成的。而拆分后,数据库可能是分布式在不同实例和不同的主机上,join将变得非常麻烦。
全局表
系统中所有模块都可能会依赖到的一些表在各个库中都保存。
字段冗余
“订单表”中保存“卖家Id”的同时,将卖家的“Name”字段也冗余,这样查询订单详情的时候就不需要再去查询“卖家用户表”。
数据同步
定时A库中的tbl_a表和B库中tbl_b关联,可以定时将指定的表做主从同步。
如果join很复杂,首先需要考虑分库的设计是否合理,其次可以考虑使用hadoop或者spark来解决。
垂直拆库,跨库事务的问题
跨库事务,其实就是分布式事务。
我们要了解分布式事务,可能最好还是了解一下分布式CAP理论。
CAP理论
2000年,Eric Brewer教授在PODC的研讨会上提出了一个猜想:一致性、可用性和分区容错性三者无法在分布式系统中被同时满足,并且最多只能满足其中两个!
C(一致性):所有的节点上的数据时刻保持同步。
A(可用性):每个请求都能接受到一个响应,无论响应成功或失败。
P(分区容错):系统应该有多个分区能为请求容错,即使系统内部有消息丢失。
CAP理论的简单分析
- CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。
- CP without A:如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。
- AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。
在互联网领域的绝大多数的场景,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。
跨库事务的解决
分布式事务的实现:二阶段提交
问题:支付宝转账1万块钱到余额宝如何确保事务? ①支付宝表扣除1万。 ②余额宝表增加1万。
在同一个库中利用mysql事务可以很简单解决,在分布式数据库中,可以参考二阶段提交解决。
二阶段提交具体细节
- 1.应用程序发起一个开始请求到TC;
- 2.向所有的Si发起<prepare>消息。
- TC给B的prepare消息是通知余额宝数据库相应账目增加1w。
- TC给A的prepare消息是通知支付宝数据库相应账目扣款1万
- Si收到<prepare>消息后,执行具体本机事务,但不会进行commit,锁定资源等待提交,成功返回<yes>,失败返回<no>。
- TC收集所有执行器返回的消息,
- 如果所有执行器都返回yes,那么给所有执行器发生送commit消息,执行器收到commit后执行本地事务的commit操作。
- 如果有任一个执行器返回no,那么给所有执行器发送abort消息,执行器收到abort消息后执行事务abort操作。
从上述流程中思考:
二阶段提交满足了CAP中的那两个?
- 不能满足CAP理论可用性,只能满足CAP理论的一致性和容错性。不能满足可用性,因为它把所有的资源都锁定了,这个时候,不允许其他人操作该资源了。
二阶段提交的缺点是哪些?
- 所有资源锁定,
- 并发性差,可用性差
使用消息队列来避免分布式事务
我们也可以选择牺牲一致性(保证最终一致性)来获取高性能的可用性。
- 支付宝在扣款事务提交之前,向实时消息服务请求发送消息,实时消息服务只记录消息数据,而不真正发送,只有消息发送成功后才会提交事务;
- 当支付宝扣款事务被提交成功后,向实时消息服务确认发送。只有在得到确认发送指令后,实时消息服务才真正发送该消息;
- 当支付宝扣款事务提交失败回滚后,向实时消息服务取消发送。在得到取消发送指令后,该消息将不会被发送;
- 对于那些未确认的消息或者取消的消息,需要有一个消息状态确认系统定时去支付宝系统查询这个消息的状态并进行更新。为什么需要这一步骤,举个例子:假设在第2步支付宝扣款事务被成功提交后,系统挂了,此时消息状态并未被更新为“确认发送”,从而导致消息不能被发送。
这种实现方式的思路,其实是源于ebay,后来通过支付宝等公司的布道,在业内广泛使用。其基本的设计思想是将远程分布式事务拆分成一系列的本地事务。
非patition key的查询问题
场景:
例如订单表,业务上对买家和卖家都有订单查询需求:
Order(oid, info_detail) 订单表
T(buyer_id, seller_id, oid) 关联表
如果用buyer_id来分库,seller_id的查询就需要扫描多库。
如果用seller_id来分库,buyer_id的查询就需要扫描多库。
问:如何解决?
答:没有好的方法,一般是采用冗余表的方式解决。
分布式ID的生成方式
- 每个写库设置不同的auto_increment初始值,以及相同的增长步长。
- 单点批量ID生成服务 3.UUID/GUID(一般应用程序和数据库均支持)
- 为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时。 4.Twitter的Snowflake(又名“雪花算法”)
- Twitter的Snowflake(又名“雪花算法”)
- snowflake是twitter开源的分布式ID生成算法,其核心思想是:一个long型的ID,使用其中41bit作为毫秒数,10bit作为机器编号,12bit作为毫秒内序列号。这个算法单机每秒内理论上最多可以生成1000*(2^12),也就是400W的ID,完全能满足业务的需求。