第4章 数据访问层

来源:互联网 发布:域名dns劫持检测 编辑:程序博客网 时间:2024/06/10 22:36

上一章的服务框架可以让应用从集中式走向分布式,解决了当网站功能越来越丰富、单个应用越来越庞大的问题,使系统走向服务化的架构。随着数据量和访问量的上升,应用访问数据库也会出现瓶颈,这时数据访问层出场!

4.1 数据库从单机到分布式的挑战

4.1.1 单机数据库

当网站比较小,数据库的数量和访问量都比较小时,只有一个数据库,所有的table 都在这个数据库中;这个数据库服务可能是单独一台服务器,也可能跟应用或者其他服务共用一台服务器;然后应用用数据访问层(JDBC/ODBC)来统一访问数据库;这个数据访问层可以提供给开发一套API,让开发更方便的访问数据库。


             APP ------> JDBC ---> DB


4.1.2 数据库垂直、水平拆分的困难

当网页访问量足够大时,除了简单的加机器来提高访问的效率外,还有其他的几种思路可以缓解DB的压力:

  • 应用的优化:可以通过业务的优化和访问db的优化来减少DB的访问、或者错开高峰的访问
  • 引入缓存:可以引入缓存、搜索引擎之类的来缓解DB的压力
  • 对数据库进行分库、分表;把DB 升级到分布式数据库
垂直(分库)、水平(分表) 都会带来一系列问题(相对单机的DB):
  • 打破原来的ACID 原则:比如之前单机的事务,DBMS 可以自带ACID 原则,但是分布式的DB 需要应用自己来保证事务的一致性或者引入分布式的事务
  • join 工作:因为多张table 可能不在同一个数据库里面,这时就不能简单使用DBMS的join工作,需要应用从不同的DB 不同的table 里面取出数据,然后自己代码中进行join
  • 分表的动作:唯一的id编号的key 就会遇到问题,这些问题都需要在分布式DB 中进行解决
  • 触发器、存储过程:从单机DB 迁移到分布式DB 后这些有可能都会失效,都需要重新修改后才可以使用

4.2 数据库从单机变为多机(分布式)如何处理

4.2.1 分布式事务


事务的支持对应用来说是非常重要的特性,对单台DB的DBMS 支持是非常到位的,但是在分布式的DB中事务的特性就需要应用自己来解决;下面看下怎么解决的:

  • 分布式事务(DTP):
分布式事务就是一个事务要在分布式多台机器上的数据库进行操作,在任何一个节点上事务中任何一个动作失败都会回滚这个事务; all  or nothing
  • 分布式事务的解决方案
对一个事务在不同分布式数据库的执行进行记录、维护,如果发现任何一个失败,会发送回滚操作给所有的数据库;来保证分布式事务的事务性!!!

4.2.2 多机的sqeuence 的处理

当水平分表时,单机中的sequence和自增的id做法就需要改变,在单机的DBMS中提供了一个机制来实现自增、不重复的id来完成编号;但在分布式数据库中就变的困难,这个问题主要解决的思路分为2个问题:一个是唯一性、一个是连续性;
如果2个问题单独考虑会非常简单:唯一性可以用ip、mac地址、时间等等组成唯一的数值;如果单独连续性也很简单,但要满足二者,我们采取把产生seqence的服务单独独立出来,写一个单独产生唯一、连续id的服务器,这样每次去访问他就好;但每次的改进、解决问题都会引入其他的问题;比如这个服务的稳定性、容错性


4.2.3 多机数据查询的问题(join)

  • 如果垂直分库后,查询的多张table 还在一个DB(Data Base) ,可以借助DB的DBMS 提供的join 功能可以简单的进行数据的join 动作进行查询是数据;如果查询的table 不在一个数据库中,而是分布到多台机器上;这个就不能简单的join 动作了;
  • 上面的问题解决思路有三种:
      1). 应用层分多次来查询数据库:比如我要从DB1/DB2忠诚查询手机尾号是0135的人,已经这些人的职业信息;先从DB1中查询出尾号是0135的所有人,然后根据每一个人去查询这些人对应的职业;这样自己把数据再组装起来;但这样效率就比较低
     2).  数据冗余:对比较常用的数据,可以冗余到一个DB 中的一张table里面,这样直接查询一张table;不用查询join 动作
     3). 借助外界的缓存、搜索引擎来彻底解决查多张table的问题;就是把数据从DB dump出来build 成index 通过TCP 提供给用户服务

4.2.4 跨库查询的问题和解决 【理解真正的分库、分表】

  • 数据库分库分表的演化
合并查询问题的产生根源在于我们进行水平分库分表时,把一张逻辑上的表分成了多张物理上的表,比如我们有一个用户的信息表,根据用户的id进行分库、分表后,物理上就会分成 很多用户信息表;如下图:

从上图可以看出来,最初用户信息保存在一个数据库中(最左边的),然后进行了分库,变成了2个数据库(中间的部分),这2个数据库存储的用户信息是不同的,一般按照用户id分成2个db,2个数据库的用户信息表加起来相当于最初的用户信息表;因为在一个数据库里面用户信息的表还是非常大,所以要按照一定的规则把用户信息的分到2个table里面,这样2个数据库里面就用了4张用户信息的表;这4张表加起来相当于最左边的用户信息表。
  • 从逻辑概念上用户的信息应该放到一张表中,但是随着用户信息量的增大、访问量的上升,需要经历分库、分表;此时用户的信息会分布到多个数据库的多张表中;也就是一张逻辑上的表对应了多张物理上的表。那在应用中对张逻辑表的查询就需要做跨库跨表的合并了。
  • 跨库跨表的解决方案
只能是在应用代码中解决,从不同的数据库查询数据,从同一个数据库不同的表中把数据统一查询出来,然后对多次查询的数据进行merger
  • 排序:根据需要进行所有结果数据的排序
  • 函数处理:各种max min sum avg 处理
  • 业务逻辑的处理
  • 分页排序的问题【数据很多,在不同的页面显示;要进行分页、排序】;也会出现统一排序后然后分页


4.3 数据访问层的设计和实现

数据访问层就是方便应用进行数据读/写访问的抽象层,我们在这个层上解决各个应用通用的访问数据库的问题;在分布式系统中,我们把数据访问层叫做分布式数据访问层,简称为数据层

4.3.1 如何对外提供数据访问层的功能

在java 应用中一般是通过JDBC 方式来访问数据库,数据层自身可以作为一个JDBC 的实现,也就是暴露出JDBC的接口给应用;这时应用迁移到分布式数据层的成本就很低了,和使用远程数据库的JDBC的驱动方式是一样的,迁移成本也非常低。我们们采用在JDBC上面包装一层可以让应用方便、快捷、低成本的分布式数据访问层

4.3.2 安装数据层流程的顺序看数据层的设计

我们在执行数据库(分布式)操作时的流程如下:

SQL 解析  ---> SQL 规则处理(根据数据的分库分表地址)  ----> SQL 改写(分解成直接访问数据库的多个sql 语句)  -----> 数据源选择(选择每一个sql的数据源)  ---->SQL 执行
-----> 查询结果返回并合并处理;

  • SQL 解析   ---> 获取这sql 要查询的表名信息
通过SQL 解析可以获取SQL 的关键信息:表名、字段、where 条件;在数据层中一个很重要的事情就是根据执行的SQL 得到要操作的表,然后根据参数及规则来确定目标数据源进行连接

  • 规则处理   ---> 根据表名查询到表所在的数据源
规则处理就是数据分配的规则,方便通过这个规则来找到数据库和表的节点地址;比如采用哈希算法,根据某一个数值进行哈希,有规则的把数据放到指定的数据库和表中;这样在查找SQL 里面的表时可以根据哈希规则找到对应的数据源;   
  • 改写SQL  --> 修改表名和信息
分库分表后一个逻辑表(应用用到的表名)对应多张物理表,这时要把SQL 改写成多个sql,每个sql 的表名要修改;其他的信息也需要修改。

  • 选择数据源  --> 确定数据源
上面的规则处理可以帮助我们确定查询数据的一组数据源(一张逻辑表分成多张物理表,规则处理能找到所有的物理表),这里是确定查询的数据具体在哪个物理表里面。
我们上面说的分库分表后,一般都会提供备库;这样就变成了多个数据库、多个表;这些数据库一般是一写多读(也存在多写多读的,但不是很多);根据当前执行SQL 的特点(读、写)是否在事务中已经各个数据库的权重规则,来选择具体是哪个DB 作为数据源来提供用户的访问
  • 执行SQL 和结果处理阶段
改写SQL 、确定数据源后就可以执行SQL 了,执行SQL 比较重要的是执行过程中的异常判断和处理,这里就不说了


4.4 独立部署的数据访问层的实现方式

数据访问层(上面5个步骤)是作为一个整体向app 提供数据访问的服务,对应用层应该是透明的;比如用户表一个逻辑表可能分库分表后分成多个数据库多个物理表,但是的应用来说,不用关心里面的细节,还是把用户表作为一张表来写程序即可。至于怎么从一张逻辑表映射到多张物理表、SQL 的解析、处理、改写、执行都是数据层完成的工作,对应用来说是透明的[mongodb 本就是把数据访问层给做到数据里面,仅仅通过配置就可以实现分库分表],下面的图就是数据访问层的示意图:


4.5 数据库读写分离的挑战和应对

随着数据量的增大(一台机器的磁盘当不下),DB 访问量增大,导致数据库压力很大;一般对一个DB里说,读的次数远远大于写的次数,而且读不会改动DB的数据,跟写的工作有明显的区别,所以我们一般就是读写分离,就形成了典型的主从分离(读写分离): 主库可写、可读,从库只能读。



数据读写分离后,数据的同步问题就变得非常重要的一个功能了,如果数据不能实时同步会造成读取数据不是最新的,会造成用户体验差或者影响业务的问题;此时我们需要做的就是数据同步。

  • 主从数据库结构一致:采用消息机制更新从数据库 [消息机制不是非常友好,最友好的是根据DB的变更日志进行跟新数据]
master 数据库必须实现读写一致,主库要分库成多个,每个主库所有的分库的备份就是一个完整的从库;所以架构如下:


  • 主从数据结构不一致:  从库根据不同的纬度进行分库



主库是买家id 的取模进行分库,这样查询买家数据就可以在一个数据库里面查询,但是卖家查询就需要在所有的数据都查询一遍,成本会比较高,所以从库采用卖家的id取模进行分库,这样卖家查询就可以查询一个库

  • 引入数据变更平台
复制数据库到其他数据库只是数据变更的一个场景,还有其他的一些数据变更的场景:比如搜索引擎索引更新、缓存数据更新等等都是数据变更的一种场景,这样的场景都需要一个数据变更,所以我们引入一个数据变更平台来实现数据的变更。


  • 数据的平滑迁移
数据迁移过程中平滑迁移是非常重要的一个问题,因为在数据迁移过程是数据是要发生变化的,迁移完成后要把这个数据的变化要跟新到数据库中。
一般采取写增量日志的形式来完成;在数据库迁移时先记录迁移开始之后数据变更的日志信息,数据迁移完成之后再通过增量日志把数据变更同步到数据库中

0 0
原创粉丝点击