第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
- 第4章 数据访问层
- 数据访问层4
- 第4章介质访问控制子层
- 《计算机网络》第4章 介质访问控制子层
- 大型网站系统与Java中间件实践 第5章 数据访问层
- 实战 .Net 数据访问层 - 4
- 轻松搞定数据访问层4
- 收藏:数据访问层
- 数据访问层DataAccess
- 数据访问层学习
- C#数据访问层
- 数据访问层
- 数据访问层!
- 数据访问层
- C# 数据访问层
- 创建数据访问层
- 数据访问层
- 数据访问层sqlhelper
- 颜色的三要素:色调,饱和度,和亮度 HSI/HSV
- react-native遇到的坑,及解决方法
- SpringMVC-轻松上手
- 3-内核对象
- struts2和springmvc比较1
- 第4章 数据访问层
- python之sklearn
- boot loader能全部用C程序编写吗
- 手机建站的几大必备要素
- Java虚拟机(五):虚拟机类加载
- 压缩zip文件和解压zip格式的文件
- 12c trancate table cascade
- PAFA 框架报错java.io.FileNotFoundException: class path resource [log4j.properties] cannot be resolved to
- maven 分模块开发