关系数据库历史悠久,可以找到靠谱的DBA,保证关系数据库稳定性,安全性,完整性和性能,同时可以保证监控和分析关系数据库的瓶颈及设计的合理性。成熟的关系数据库有着自己完善的生态圈,用于保证高可用,数据备份,性能检测分析等成熟的工具。
存储引擎非常成熟,基于MVCC的数据库引擎在性能和正确性上做到了很好的平衡,通过B tree索引大幅提升了查询效率。
基于ACID的事务是关系数据库的另一个强有力保障。只有支持了事务的数据库才能最大限度保证数据的正确性和完整性。
关系数据库的性能和承载能力在企业应用时代有着很大的影响。但在访问量和数据量急剧膨胀的今天,关系数据库已经很难像以前一样做好大规模的底层支撑,甚至成为系统瓶颈。
不足之处:
- 单节点并发访问受限:在服务可以任意扩容和拆分的同时,由于数据库中存储的数据是有状态的,因此很难像服务一样任意拆分和扩容。单一的数据库节点承载大量服务节点的查询和更新,这些不是一个对等的架构部署模式。
- 单节点数据承载数据量有限:单一数据库节点对数据承载能力是有限的,数据量越大,用于查询数据所创建索引的深度就越深。索引深度决定IO访问次数,索引深度越深,查找越慢。
- 分布式事务衰退:将数据库拆分后,需要使用分布式事务代替本地事务。基于XA的分布式事务采用两阶段提交,在准备阶段锁定资源,直至整个事务结束。在系统并发度增加时,性能极具衰退。
NoSql
NoSql的出现很好的支持了分布式数据库所需要的分片和数据迁移等功能,在海量数据和大并发支持方面,强于传统关系数据库。
NoSql虽然提供良好的扩展性和灵活性,但不足是:
- 不同的NoSql都有自己的查询语言,不像SQL一样标准。NoSql无法提供ACID的事务操作,因此很多企业无法放心将NoSql应用于核心业务系统中。
NewSql
NewSql继承了NoSql对海量数据的处理能力,同时保持了关系数据库对sql和ACID事务的支持。
NewSql一般通过share-nothing的架构,支持多节点并发控制,高度容错的自动化数据副本复制,流控及分布式查询处理等特性。
由于天生面向分布式节点而设计的系统,因此对于查询优化和节点通信协议等处理更加出色。NewSql数据库的多个数据节点之间可以直接通信,无需依赖中心节点。NewSql对于查询会将请求发送到不同节点,而不是将数据复制到请求的节点,以减少网络传输。
数据分片
关系数据库将数据存储在单一数据节点,在性能和可用性两方面很难满足海量数据场景。
由于关系数据库大多采用b 树类型的索引,在数据量超过阈值情况时,索引深度增加也使得磁盘访问IO次数增加,进而导致查询性能大幅度下降。同时高并发访问请求也使得集中式数据库成为系统最大瓶颈。
数据分片是按照某种维度将单一数据库中数据分散到多个数据库或表中,以达到提升性能瓶颈及可用性的效果。数据分片的有效手段是对关系数据库进行分库或分表,分库分表均可以有效避免因为海量数据产生的查询瓶颈。
分库可以尽量将分布式事务转换为本地事务,使用多主多从的分片方式,可以有效避免数据单点,从而提升数据架构的可用性。
垂直分片
按照业务功能进行归类,分布到不同的数据库中,从而将压力分担到不同数据库之上。
水平分片
是将数据按照业务逻辑分类,将某个字段按照某种规则将数据分散到多个库或表中,每个分片仅包含一部分数据。
为解决查询海量数据过大导致的性能问题,将数据分片是有效的解决方案。
分库可以分散高并发带来的数据访问压力,分表虽然无法缓解数据库压力,但跨表的更新操作依然可以使用数据库原生的ACID事务,一旦涉及到跨库的更新操作,分布式事务问题变得无比复杂。
水平分片通常采取分库的方式,一并解决数据量和访问量巨大的问题,读写分离是另一个疏导流量的办法,但读写数据间的延迟是架构设计需要考虑的问题。
- NewSql会将同一表的数据存储在分布式文件系统中。
- 数据分片中间件可以尽量透明化分库分表所带来的影响,让使用方尽量像使用一个数据库一样使用水平分片之后的数据库。
分布式事务
基于XA的分布式事务由于性能低下,无法被互联网公司所采用,大多数采用最终一致性事务代替分布式事务。
读写分离 将单一数据库拆分为主库和从库,主库负责处理事务性增删改操作,从库处理查询操作,能够有效避免数据更新导致的行锁,使得整个系统查询性能得到极大改善。
通过一主多从配置将查询请求均匀分散到多个数据副本,能够进一步提高系统处理能力。同时可以提高可用性,当一个数据库宕机,甚至磁盘物理损坏时,仍不影响系统的正常运转。
读写分离本质是数据分片的一种,将数据根据分片建打散到多个数据节点的水平分片不同,读写分离根据sql语义分析,将读请求和写请求分别路由到从库和主库,读写分离的数据节点的数据是一致的,水平分片每个数据节点的数据内容是不同的。
但读写分离导致的明显问题是数据不一致问题,包括主库之间数据一致性及从库之间数据一致性问题。
数据分片核心是由sql解析,sql路由,sql改写,sql执行及结果归并流程组成,为保持原有应用程序实现低介入成本,需要兼容数据库访问,需要协议适配。
分布式事务 单一数据节点,事务仅限于对单一数据库资源的访问控制,成为本地事务。
XA协议 通过一个全局事务管理器和多个资源管理器交互。全局事务管理器负责管理全局事务状态和参与事务的资源,资源管理器负责具体的资源操作。
XA协议使用两阶段提交保证分布式事务原子性,将提交过程分为准备阶段和提交阶段:
- 开启XA全局事务后,所有子事务按照本地默认的隔离级别锁定资源,并记录undo和redo日志,然后由TM发起prepare投票,询问所有子事务是否可以提交,所有子事务返回yes,Tm发起commit,一个子事务返回no,tm发起rollback。如果prepare阶段反馈结果为yes,而commit过程出现宕机,则节点服务重启后,可以根据xa recover在此进行commit补偿,以保证数据一致性。
XA严格实现了ACID特性,但是事务执行过程中锁定资源,对于长事务来说,并发性存在明显问题,XA并不是分布式事务的首选。
柔性事务
ACID事务对隔离性有很高的要求,事务执行过程中必须锁定资源,柔性事务理念是通过业务逻辑将互斥锁操作从资源层面迁移到业务层面,通过放宽一致性要求,来换取系统吞吐量。
由于在分布式系统中,可能出现超时重试,因此柔性事务中操作必须幂等,需要通过幂等避免多次请求带来的问题。
柔性事务方案主要有:努力送达,Saga,TCC。
努力送达
- 适用于数据库操作最终一定能够成功的场景,NewSql可以记录失败的sql,反复尝试,直至成功。最大努力送达是没有回滚功能的。
- 优点是无资源锁定时间,性能损耗小。
- 缺点是多次重试后,无法回滚,仅适用于事务最终一定可以成功的业务场景。
Saga(论文)
- 适合使用长事务场景,由多个本地事务组成,每个事务有相应的执行模块和补偿模块,任何一个本地事务出错,可以通过调用相关的补充方法达到事务的最终一致性。
- 每个分布式事务有相应的执行模块(Transaction)和补偿模块(Compensation)。当Saga事务中任一本地事务执行失败,可以通过调用相应补偿方法恢复到之前的事务,以达到事务最终一致性。
理论上,补偿事务永不失败,在分布式世界中,服务器可能宕机,网络可能不可用,甚至数据中心停电,因此需要提供故障恢复后回退机制,比如人工干预。
Saga模型没有XA中的准备阶段,因此事务没有实现隔离性,两个Saga事务同时操作一个资源会产生更新丢失,脏数据等问题,所以需要在Saga事务的应用程序中在应用层面加入资源锁定逻辑。
TCC 对分布式事务分解的方式。
提供三段业务逻辑:
- Try:完成业务检测,预留业务所需资源。
- Comfirm:执行业务逻辑,直接使用Try阶段预留的业务资源,无需再次做业务检测。
- cancel:释放try阶段预留的业务资源。
消息驱动
是通过消息中间件保证上下文应用数据操作的一致性。
基本思路是将本地操作和发送消息放在本地事务中,下游应用像消息订阅系统订阅消息,收到消息后执行相关操作,本质上依靠消息的重试机制,达到最终一致性。
缺点是:
- 耦合度高,需要引入消息中间件,复杂度增加。