分布式数据库实践(一)

分布式数据库 (说明)

  1. 系统分析:OLTP 和 OLAP

海量数据的存储与访问成为系统设计与使用的瓶颈问题,对于海量数据处理,按照使用场景,主要分为两种类型:联机事务处理(OLTP)和联机分析处理(OLAP)。

OLTP也叫做面向交易的处理系统,其基本特征是原始数据可以立即传送到计算中心处理,并在很短的时间内给出处理结果。

OLAP指的是通过多维的方式对数据进行分析、查询和报表,可以同数据挖掘工具、统计分析工具配合使用,增强决策分析功能。

OLTPOLAP
系统设计日常交易处理统计、分析、报表
DB设计面向实时交易类应用面向统计分析类应用
数据处理当前的,最新的、细节的,二维的,分立的历史的,聚集的、多维的、集成的、统一的
实时性实时读写要求高实时读写要求低
事务强一致性弱事务
分析要求低、简单高、复杂

OLTP和OLAP的比较。OLTP面向实时交易类,实时要求性高,查询分析功能偏弱。OLAP面向统计分析类,实时性要求一般,涉及到大范围扫描,JOIN,聚合查询等,要求有比较高的查询分析能力。

MySQL适合OLTP类型的系统,因为MySQL的分析支持比较弱,适合高并发的小的查询操作。在进行数据库技术选型及选择解决方案之前,必须对系统是OLTP还是OLAP做出准确的判断。

技术选型:关系型数据库 or NoSQL数据库

针对上面两类系统有多种技术实现方案,存储部分的数据库主要分为两大类:关系型数据库与NoSQL数据库。

关系型数据库,是建立在关系模型基础上的数据库,其借助于集合代数等数学概念和方法来处理数据库中的数据。主流的Oracle、DB2、MS SQL Server和MysSQL都属于这类传统数据库。

NoSQL 数据库,全称为 Not Only SQL,意思就是适用关系型数据库的时候就使用关系型数据库,不适用的时候也没有必要非使用关系型数据库不可,可以考虑使用更加合适的数据存储。主要分为临时性键值存储 (memcached、Redis)、永久性键值存储(ROMA、Redis)、面向文档的数据库(MongoDB、 CouchDB)、面向列的数据库(Cassandra、HBase),每种NoSQL都有其特有的使用场景及优点。

Oracle,MySQL等传统的关系数据库非常成熟并且已大规模商用,为什么还要用NoSQL数据库呢?主要是由于随着互联网发展,数据量越来越大,对性能要求越来越高,传统数据库存在着先天性的缺陷,即单机(单库)性能瓶颈,并且扩展困难。这样既有单机单库瓶颈,却又扩展困难,自然无法满足日益增长的海量数据存储 及其性能要求,所以才会出现了各种不同的NoSQL 产品,NoSQL 根本性的优势在于在云计算时代,简单、易于大规模分布式扩展,并且读写性能非常高。

下面分析下两者的特点,及优缺点:

在这里插入图片描述

虽然在云计算时代,传统数据库存在着先天性的弊端,但是 NoSQL 数据库又无法将其替代,NoSQL 只能作为传统数据的补充而不能将其替代,所以规避传统数据库的缺点是目前大数据时代必须要解决的问题。如果传统数据易于扩展,可切分,就可以避免单机(单库)的性能缺陷,但是由于目前开源或者商用的传统数据库基本不支持大规模自动扩展,所以就需要借助第三方来做处理,那就是下面要讲的数据切分,下面就来分析一下如何进行数据切分。

数据切分,解决单库瓶颈

在同一个数据库中的数据分散存放到多个数据库(主 机)上面,以达到分散单台设备负载的效果。数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。

垂直切分

按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分。

一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同 的数据库上面,这样也就将数据或者说压力分担到不同的库上面,如下图:

在这里插入图片描述

系统被切分成了用户、订单交易、支付几个模块。

一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的数据对应到数据库中就是一个或者多个表。而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。

但是往往系统之有些表难以做到完全的独立,存在这扩库 join的情况,对于这类的表,就需要去做平衡,是数据库让步业务,共用一个数据源,还是分成多个库,业务之间通过接口来做调用。在系统初期,数据量比较少,或者资源有限的情况下,会选择共用数据源,但是当数据发展到了一定的规模,负载很大的情况,就需要必须去做分割。

一般来讲业务存在着复杂 join 的场景是难以切分的,往往业务独立的易于切分。如何切分,切分到何种程度是考验技术架构的一个难题。

下面来分析垂直切分的优缺点:

优点:===>

  1. 拆分后业务清晰,拆分规则明确;
  2. 系统之间整合或扩展容易;
  3. 数据维护简单。

缺点:===>

  1. 部分业务表无法 join,只能通过接口方式解决,提高了系统复杂度;
  2. 受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高;
  3. 事务处理复杂。
水平切分

另外一种则是根据 表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。

相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图:

在这里插入图片描述

拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如: 从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分,不同的数据按照会员ID做分组,这样所有的数据查询 join 都会在单库内解决;如果从商户的角度来讲,要查询某个商家某天所有的订单数,就需要按照商户 ID 做拆分;但是如果系统既想按会员拆分,又想按商家数据,则会有一定的困难。如何找到合适的分片规则需要综合考虑衡量。

几种典型的分片规则包括:

  • 按照用户ID求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中;

  • 按照日期,将不同月甚至日的数据分散到不同的库中;

  • 按照某个特定的字段求摸,或者根据特定范围段分散到不同的库中。 如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下面用用户ID求模举例:

在这里插入图片描述

优点:===>

  • 拆分规则抽象好,join 操作基本可以数据库做;
  • 不存在单库大数据,高并发的性能瓶颈;
  • 应用端改造较少;
  • 提高了系统的稳定性跟负载能力。

缺点:===>

  • 拆分规则难以抽象;
  • 分片事务一致性难以解决;
  • 数据多次扩展难度及维护量大;
  • 跨库join性能较差;

前面讲了垂直切分跟水平切分的不同跟优缺点,会发现每种切分方式都有缺点,但共同的特点缺点有:===>

  • 引入分布式事务的问题;
  • 跨节点 Join 的问题;
  • 跨节点合并排序分页问题;
  • 多数据源管理问题。

数据切分解决传统数据库的缺陷,又有了 NoSQL 易于扩展的优点。

MySQL:做分布式数据库的优势

关于Oracle和MySQL的对比。

关于这两数据库的全方位的多维度的优劣对比,比较有经验的数据库开发人员应该能列举出很多来,我身为一名普通的应用开发人员,写不出什么硬核的分析对比,只能大概表达一些我的理解。

Oracle简单概括:功能,性能,稳定,人才;MySQL简单概括:灵活,简单,便宜,开源。

Oracle的发展历史是MySQL的两倍以上,现在已经是很成熟的商业数据库系统。Oracle为用户提供了非常丰富强大的功能,可编程和分析函数的支持都做到非常完善,还有无限的行级锁,最高级别的隔离级别,完全的事务支持,成熟的热备工具,稳定可靠但复杂的高可用配置,还有大批有经验的DBA人才,总之,如果你想要一个稳定可靠并且性能强大的数据库单集群,那Oracle是完美的。But,如果使用分布式解决方案,昂贵的Oracle也会给你带来惊人的license费用,Oracle的功能看上去丰富而全面,但是那都是要钱的,有时候我们并不需要其中的很多东西,Oracle缺少可定制化的灵活性。

MySQL有开源和灵活的优势,轻便的MySQL虽然功能性没有Oracle强大,但是作为数据库,最重要和核心的功能都做的很好。MySQL非常灵活,针对每个表都能指定不同的存储引擎,InnoDB引擎支持依赖于索引的行级锁;同时MySQL提供了简单高速的主从复制配置,能提供快速冗余的专业集群服务器;MySQL的开源特性,让互联网团队可以自研支持MySQL协议的代理中间件来支持实现MySQL的分布式集群,同时可以灵活定制修改MySQL细节功能,目前基于MySQL的分布式数据库基本都是采用这一方式。


预备文,这里才正式发车,车门焊死。。。。。  

什么是分区、分表、分片、分库

分区,局限于单一数据库节点,将一张表分散存储在不同的物理块中。MySQL5以后支持分区,但是不支持二级分区,并且单机MySQL的性能远远不如Oracle,所以分区并不能解决性能问题

在这里插入图片描述

分表,分表有两种,应用层面的分表和代理层面的分表。

应用层面的分表,人工把一张逻辑上完整的表分成若干个小表。比如T_PAYMENT表分为T_PAYMENT_0101…T_PAYMENT_1231。对代码不透明。

在这里插入图片描述

代理层面的分表,通过MERGE引擎或者代理中间件把表分成若干的字表,对应用只保留一个“表壳”,对代码透明。(MERGE引擎示意)

在这里插入图片描述

分表和分区比较类似,侧重点不同,分区侧重提高读写性能,分表侧重提高并发性能。两者不冲突,可以配合使用。

分库,单纯的分库就是垂直切分,把不同业务逻辑的表分开存储在在不同的数据库。

在这里插入图片描述

分片,分片就是分库+分表,属于水平切分,将表中数据按照某种规则放到多个库中,既分表又分库,就相当于原先一个库中的一个表,现在放到了好多个表里面,然后这好多个表又分散到了好多个库中。分片和分区也不冲突。

在这里插入图片描述

MYSQL分片
分片键

数据分片是将一张分布式表按照指定的分片键(Partition Key)和分片模式(Partition Mode)水平拆分成多个数据片,分散在多个数据存储节点中。对于分片的表,要选取一个分片键。一张分布式表只能有一个分片键,分片键是用于划分和定位表的列,不能修改。

分片模式
  • 枚举/列表List

{1 => Cluster A, 2 => Cluster B}

  • 范围Range (仅支持数字或ASCII字符串类型的分片键)

{[1 - 100] => Cluster A, [101 - 199] => Cluster B}

  • 散列Hash (仅支持数字或ASCII字符串类型的分片键)

{1024n + 1 => Cluster A, 1024n + 2 => Cluster B}

分片方式类似于分区方式,可以选择枚举,范围Range或者散列哈希。不同的是分片不支持时间range。

分片策略

在做分片的时候,选择合适的分片规则非常重要,将极大地避免后续数据的处理难度,有以下几点需要关注:

  1. 能不分就不分,对于1000万以内的表,不建议分片,通过合适的索引,读写分离等方式,可以更好地解决性能问题。

  2. 分片数量不是越多越好,并且尽量均匀分布在多个存储节点上,只在必要的时候进行扩容,增加分片数量。

  3. 分片键不能为空,不能修改,所以要选择表中中最常用且不变的字段。

  4. 分片键选择时尽量做到可以将事务控制在分片范围内,可以避免出现跨分片的操作。

  5. 选择分片规则时,要充分考虑数据的增长模式,数据的访问模式,分片关联 性问题,以及分片扩容问题。

总体上来说,分片的选择是取决于最频繁的查询 SQL 的条件。找出每个表最频繁的 SQL,分析其查询条件,以及相互的关系,并结合 ER 图,就能比较准确的选择每个表的分片策略。

MySQL分片要解决的问题

事务问题

分布式数据库分散在多个不同的物理主机,网络环境复杂,关注事务,保证数据一致性是一个问题。

先看单机环境下的事务管理,以Spring为例,利用注解的事务管理:

@Override
@Transactional
public void service(){
    ...
    UserService.updateUserByUserId(userId);
    ...
    OrderService.updateOrderByOrderNo(orderNo);
    ...
}

实际上这段代码等同于:

@Override
public void service(){
    ...
    UserService.updateUserByUserId(userId);
    ...
    OrderService.updateOrderByOrderNo(orderNo);
    ...
    try{
        commit();
    }catch(Exception e){
        rollback();
    }
}

大致的过程就是:

在这里插入图片描述

变成分布式环境,变成了这样:

在这里插入图片描述

如何给多主机发commit并且保证他们最终状态一致就是分布式事务要解决的问题。

解决这个问题有两种方案。

第一种由应用程序和数据库共同控制,将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务,并通过应用程序来总控各个小事务。

@Override
public void service(){
    ...
    userService();
    ...
    try{
        orderService();
    }catch(Exception e){
        rollback();
    }
    ...
    }


@Override
@Transactional
public void userService() throws RuntimeException{
    ...
    UserService.updateUserByUserId(userId);
    ...
}

@Override
@Transactional
public void orderService() throws RuntimeException{
    ...
    OrderService.updateOrderByOrderNo(orderNo);
    ...
}
}

这个方法是性能上比较高效,缺点就是需要应用程序在事务控制上做灵活设计。如果使用 了Spring的事务管理,改动起来会面临一定的困难。

第二种是交由数据库管理,简单有效。比较常见的方案有两阶段提交2PC、补偿事务TCC,MQ事务消息等。下图演示了2PC的过程。

在这里插入图片描述

除了图中提到的三个缺点,两阶段提交的方案影响的整体性能。

根据CAP原理,分布式数据库无法做到完全的ACID原则,几乎所有分布式解决方案都牺牲了部分一致性或者可用性。比如用最终一致性代替强一致性。

跨节点Join的问题

只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。

如果频次较高,可以考虑添加一张关联表,规避掉join操作,空间换时间。

在交易时直接将记录插入关联表,即可在查询时查询关联表获得想要的结果。

跨节点的count,order by,group by以及聚合函数问题

这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。

解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。

值得提醒的是,如果你的应用中包含大量的此类分析及聚合操作,考虑是否你对系统类型判断有误,是OLAP而非OLTP系统。对于OLAP系统不推荐使用MySQL更不推荐使用DRDS分布式数据库存储。

数据迁移,容量规划,扩容等问题

在前期如果对系统数据仓库的容量没有正确的预估,会遇到扩容问题。

利用对2的倍数取余具有向前兼容的特性(如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但是依然需要进行表级别的迁移,同时对扩容规模和分表数量都有限制。

举例说明,扩容前有两个集群作为存储节点,现要扩容为四个。

在这里插入图片描述

在这里插入图片描述

将原先的备机(A1,B1)升级为主机,然后再分别给他们配置备机,即完成了两倍的扩容。根据前面说的向前兼容的特性,分片规则不需要变化,是目前比较常用的扩容方案。

这部分实际和开发人员关系不大,了解即可。

ID问题

一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键生成机制。

一方面,某个分区数据库自生成的ID无法保证在全局上是唯一的;

另一方面,应用程序在插入数据之前需要先获得ID,以便进行SQL路由。

比较常见的ID解决方案是应用生成UUID,或者在数据库中维护一个Sequence表。

跨分片的排序分页

一般来讲,分页时需要按照指定字段进行排序。当排序字段就是分片字段的时候,我们通过分片规则可以比较容易定位到指定的分片,而当排序字段非分片字段的时候,情况就会变得比较复杂了。为了最终结果的准确性,我们需要在不同的分片节点中将数据进行排序并返回,并将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。如下图所示:

在这里插入图片描述

上面图中所描述的只是最简单的一种情况(取第一页数据),看起来对性能的影响并不大。但是,如果想取出第10页数据,情况又将变得复杂很多,如下图所示:

在这里插入图片描述

有些读者可能并不太理解,为什么不能像获取第一页数据那样简单处理(排序取出前10条再合并、排序)。其实并不难理解,因为各分片节点中的数据可能是随机的,为了排序的准确性,必须把所有分片节点的前N页数据都排序好后做合并,最后再进行整体的排序。很显然,这样的操作是比较消耗资源的,用户越往后翻页,系统性能将会越差。

那如何解决分库情况下的分页问题呢?有以下几种办法:

如果是在前台应用提供分页,则限定用户只能看前面n页,这个限制在业务上也是合理的,一般看后面的分页意义不大(如果一定要看,可以要求用户缩小范围重新查询)。

如果是后台批处理任务要求分批获取数据,则可以加大page size,比如每次获取5000条记录,有效减少分页数(当然离线访问一般走备库,避免冲击主库)。

分库设计时,一般还有配套大数据平台汇总所有分库的记录,有些分页查询可以考虑走大数据平台。

分库策略

比较常见的两种分库策略,按照范围分库或者按照Mod分库,各有侧重点。

按照范围分库一般在前期数据量较小,在使用过程中用户数据不断增长,在数据库数量少时全库查询消耗小,后期调整数据库数量也比较容易。

按照Mod分库在初期就要规划好数据库数量,单库查询性能好,如果调整Mod因子会导致数据库迁移,后期增加数据库数量也比较麻烦。

实际使用中为了处理简单,一般选mod分库,数据库数量选择2的倍数,方便扩容。

分库数量

分库数量首先和单库能处理的记录数有关,一般来说,Mysql 单库超过5000万条记录,Oracle单库超过1亿条记录,DB压力就很大(当然处理能力和字段数量/访问模式/记录长度有进一步关系)。

在满足上述前提下,如果分库数量少,达不到分散存储和减轻DB性能压力的目的;如果分库的数量多,好处是每个库记录少,单库访问性能好,但对于跨多个库的访问,应用程序需要访问多个库,如果是并发模式,要消耗宝贵的线程资源;如果是串行模式,执行时间会急剧增加。

最后分库数量还直接影响硬件的投入,一般每个分库跑在单独物理机上,多一个库意味多一台设备。所以具体分多少个库,要综合评估,一般初次分库建议分4-8个库。

路由透明

分库从某种意义上来说,意味着DB schema改变了,必然影响应用,但这种改变和业务无关,所以要尽量保证分库对应用代码透明,分库逻辑尽量在数据访问层处理。

历史数据清理

历史数据的清理是一个比较棘手的问题。

假设我有一张订单表,联机系统只保存最近五天的订单,之前的要删除掉。

我们没办法根据日期进行分片,因为日期每天都在变化,按照日期分片意味着分片规则每天都在变化。这和常见的在Oracle定时任务删除分区和新建分区是不一样的。

假使我们根据设备进行分片,那么常规情况下,同一天的订单会均匀分布在各个分片上。最起码的原则是避免跨分片操作,因为跨分片操作性能极其低而且很多时候你也不知道会发生什么未知的事情。

所以比较容易想到的方法就是找到每个分片然后再每个分片上进行单独的数据清理操作。

如果是自研的分布式架构,应该可以通过脚本将清理语句分别路由到各个分片。如果是使用的开源中间件,大部分都会提供分片操作的方法(如果没有,那只能另寻他法了。)。如果是使用的商业服务的分布式数据库,也会提供分片数据清理操作的方法。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分布式数据库环境搭建可以参考以下步骤: 1. 首先,了解分布式数据库的基本概念和原理,可以阅读引用中的文章《分布式数据库-CrateDB架构分析与源码阅读之最佳实践》和引用中的文章《分布式数据库-CrateDB架构分析与源码阅读之搭建部署》。这些文章将帮助你了解CrateDB的架构和部署方式。 2. 确保你的服务器满足CrateDB的系统要求。通常,CrateDB可以在各种操作系统上运行,如Linux、Windows和Mac OS。你需要确保服务器上有足够的硬件资源(CPU、内存、磁盘空间等)来支持你的数据库规模和负载。 3. 下载和安装CrateDB。你可以从CrateDB的官方网站上下载最新版本的软件包。根据引用中的文章,你可以按照里面的步骤进行安装和部署。 4. 配置CrateDB集群。一般来说,一个分布式数据库环境包含多个节点,你需要配置这些节点之间的通信和数据同步。引用中的文章会给出一些关于配置CrateDB集群的指导。 5. 启动CrateDB集群。完成配置后,你可以启动CrateDB集群并监控其运行状态。引用中的文章会告诉你如何启动集群以及如何查看集群状态。 6. 使用常用命令进行数据库操作。一旦CrateDB集群成功搭建和启动,你可以使用引用中的文章《分布式数据库-CrateDB架构分析与源码阅读之常用命令》中提到的常用命令来进行数据库操作,如创建表、插入数据、查询数据等。 总结起来,分布式数据库环境搭建的步骤包括了了解基本概念和原理、准备服务器环境、下载和安装CrateDB、配置和启动CrateDB集群以及使用常用命令进行数据库操作。阅读引用、和中的文章将为你提供更详细的指导和实践经验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值