精华内容
下载资源
问答
  • 在数据库下如何定义架构
    千次阅读
    2020-12-31 16:42:11

    SQL语句

    数据库的定义

    1. 数据库的创建

      CREATE DATABASE <数据库名>
      [ON [PRIMARY] <文件> [, ...n]
      	[, <文件组> [, ...n]]
      	[ LOG ON <文件> [, ...n]]]
      [COLLATE <校验方式名>]
      [WITH <选项> [, ...n]][;]
      
      # 创建一个StudentCourse数据库
      CREATE DATABASE StudentCourse
      
    2. 数据库修改

      ALTER DATABASE <数据库名>
      	ADD FILE <文件名> [, ...n]
      		[ TO FILEGROUP {文件组} ]
      	| ADD LOG FILE <文件> [, ...n]
      	| REMOVE FILE <文件> [, ...n]
      	| MODIFY FILE <文件> [, ...n]
      
      /*
      将一个大小为10M的数据文件  student_Data.mdf 添加到 StudentCourse 数据库中,
      该文件的大小为10M,最大的文件大小为100M,增长速度为2MB,物理地址为D盘
      */
      ALTER DATABASE StudentCourse
      ADD FILE
      (
      	Name=student_Data,
          FILENAME='D:\sql\student_Data.mdf',
          Size=100M,
          Maxsize=100MB,
          Filegrowth=2MB
      )
      
    3. 数据库删除

      DROP DATABASE <数据库名> [, ...n][;]
      
      # 将 StudentCourse 数据库删除
      DROP DATABASE StudentCourse
      

    架构的定义

    架构(SCHEMA,也称模式)是数据库下的一个逻辑命名空间,可以存放表、视图等数据对象,它是数据库对象的容器。

    1. 架构创建

      CREATE SCHEMA { <架构名> 
      	| AUTHORIZATION <所有者名>
      	| <架构名> AUTHORIZATION <所有者名>
      }
      [{表定义预计 | 视图定义语句 | 授权语句 | 拒绝权限语句 }][;]
      # 执行架构语句的用户必须具有数据库管理员权限,
      # 或者是获得了数据库管理员授予的 CREATE SCHEMA 的权限
      
      # 为用户 “HMT” 定义一个架构,架构名为 “S_C”
      CREATE SCHEMA S_C AUTHORIZATION HMT
      
    2. 架构修改

      # 架构修改使用 ALTER SCHEMA 语句,可用于在同一数据库中的架构之间移动安全对象
      ALTER SCHEMA <架构名>
      TRANSFER <对象名> [;]
      
      # 将表 Address 从架构 Person 传输到 HumanResources 架构
      ALTER SCHEMA HumanResources 
      TRANSFER Person.Address
      
    3. 删除架构

      DROP SCHEMA <架构名> [;]
      
      # 删除S_C 架构
      DROP SCHEMA S_C
      

    表的定义

    1. 表的创建

      CREATE TABLE [<架构名>.]<表名>
      (
      	{<列名> <数据类型> [列级完整性约束定义 [, ...n]]}
          [表级完整性约束 ][, ...n]
      )
      

      注意:默认时SQL语言不区分大小写。

      参数说明:

      • <表名>是所要定义表的名字。
      • <列名>是表中所包含的属性列的名字
      • 在定义表的同时还可以定义与表有关的完整性约束,这些完整性约束条件都会存储在系统的数据字典中。如果完整性约束只设计表中的一个列,则这些约束条件可以在**“列级完整性约束定义”处定义,也可以在“表级完整性约束定义”处定义;但某些涉及表中多个属性列的约束,必须在“表级完整性约束定义”**处定义。

      在“列级完整性约束定义”处可以定义如下约束:

      • NOT NULL:非空约束,限制列取值非空。
      • PRIMARY KEY:主键约束,指定本列为主键。
      • FOREIGN KEY:外建约束,定义本列为引用其他表的外建。
      • UNIQUE:唯一值约束,限制列取值不能重复。
      • DEFAULT:默认值约束,指定列的默认值。
      • CHECK:列取值范围约束,限制列的取值范围。

      各约束具体语法:

      # 1. 主键约束
      PRIMARY KEY [(<列名> [, ...n])]
      # 如果在列级完整性约束处,可以省略方括号中内容
      
      # 2. 外建约束
      [FOREIGN KEY (<列名>)] REFERENCES <外表名> (<外表列名>)
      # 如果在列级完整性约束处,可省略 "FOREIGN KEY (<列名>)"
      
      # 3. 唯一值约束
      UNIQUE [(<列名> [,...n])]
      /*
      注意:
      1. 唯一值约束允许有一个空值(NULL)
      2. 在一个表中可以有多个唯一值约束
      3. 可以在一个列或多个列上定义UNIQUE约束
      */
      
      /*
      4. 默认值约束。一个默认值约束只能为一个列提供默认值,
      且默认值约束必须是列级约束
      有两种,
      1.在定义表时制定默认约束,
      2.在修改表时添加默认值约束
      */
      
      # 在创建表时定义默认值约束
      DEFAULT 常量表达式
      
      # 为已经创建好的表添加默认值约束
      DEFAULT 常量表达式 FOR 列名
      
      # 5. 列取值范围约束
      CHECK (逻辑表达式)
      

      使用SQL语句创建一个Student表,结构如下:

      列明含义数据类型约束
      Sno学号char(7)主键
      Sname姓名nchar(20)非空
      Ssex性别nchar(2)非空;默认值:男;
      检查:只能是’男’或’女’
      Sbirthday出生日期smalldatetime
      Sdept所在系nvarchar(20)
      CREATE TABLE Student
      (
      	Sno CHAR(7) PRIMARY KEY,
          Sname NCHAR(20) NOT NULL,
          Ssex NCHAR(2) NOT NULL DEFAULT '男' CHECK (Ssex in ('男', '女')),
          Sbirthday DATETIME,
          Sdept NVARCHAR(20)
      )
      
    2. 表的修改

      ALTER TABLE [<架构名>.]<表名>
      {
      	ALTER COLUMN <列名> <新数据类型> -- 修改表定义
      	| ADD <列名> <数据类型> [约束] -- 添加新列
      	| DROP COLUMN <列名> -- 删除列
      	| ADD [CONSTRAINT <约束名>] 约束定义 -- 添加约束
      	| DROP <约束名> -- 删除约束
      }
      
      -- 为Student表添加备注(Memo)列,此列名为Memo,数据类型为text
      ALTER TABLE Student
      	ADD Memo text
      	
      -- 将Student表的Sname列的数据类型修改为 NVARCHAR(40)
      ALTER TABLE Student
      	ALTER COLUMN Sname NVARCHAR(40) -- MySQL用的是 MODIFY 而不是 ALTER
      
    3. 表的删除

      DROP TABLE <表名> 
      
      -- 删除Student表
      DROP TABLE Student
      
    更多相关内容
  • 分布式数据库架构

    千次阅读 2022-04-08 23:22:54
    分布式数据库架构 1、MySQL常见架构设计 对于mysql架构,一定会使用到读写分离,此基础上有五种常见架构设计:一主一从或多从、主主复制、级联复制、主主与级联复制结合。 1.1、主从复制 这种架构设计是使用的最多...

    分布式数据库架构

    1、MySQL常见架构设计

    对于mysql架构,一定会使用到读写分离,在此基础上有五种常见架构设计:一主一从或多从、主主复制、级联复制、主主与级联复制结合。

    1.1、主从复制

    这种架构设计是使用的最多的。在读写分离的基础上,会存在一台master作为写机,一个或多个slave作为读机。因为在实际的情况下,读的请求量一般是远远大于写请求的。
    在这里插入图片描述
    采用这种架构之后,当应用写入输入时,会把数据写入到master节点,然后由master节点将写入数据复制到slave节点上。

    缺点:

    • master单机故障
    • 对master进行维护时,无法接收写请求
    • master复制延迟,查询数据延迟
    • slave提升为master后,可能会发生数据丢失(数据不一致)

    1.1.1、 主从复制搭建

    1、首先需要在两台机器上安装mysql镜像以及创建mysql容器

    docker pull mysql:5.7
    
    docker run --name mysql3307 -p 3307:3306 --privileged=true -ti -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -v /home/mysql/docker-data/3307/conf:/etc/mysql/conf.d -v /home/mysql/docker-data/3307/data/:/var/lib/mysql -v /home/mysql/docker-data/3307/logs/:/var/log/mysql -d mysql:5.7
    

    2、需要在两台机器上的/home/mysql/docker-data/3307/conf目录下,需要创建mysql的配置文件my.cnf

    my.cnf配置文件内容如下:

    # For advice on how to change settings please see
    # http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html
    
    [mysqld]
    #
    # Remove leading # and set to the amount of RAM for the most important data
    # cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
    # innodb_buffer_pool_size = 128M
    #
    # Remove leading # to turn on a very important data integrity option: logging
    # changes to the binary log between backups.
    # log_bin
    #
    # Remove leading # to set options mainly useful for reporting servers.
    # The server defaults are faster for transactions and fast SELECTs.
    # Adjust sizes as needed, experiment to find the optimal values.
    # join_buffer_size = 128M
    # sort_buffer_size = 2M
    # read_rnd_buffer_size = 2M
    #datadir=/home/mysql/docker-data/3307/data
    #socket=/home/mysql/docker-data/3307/mysql.sock
    
    character_set_server=utf8
    init_connect='SET NAMES utf8'
    
    # Disabling symbolic-links is recommended to prevent assorted security risks
    symbolic-links=0
    
    #log-error=/home/mysql/docker-data/3307/logs/mysqld.log
    #pid-file=/home/mysql/docker-data/3307/mysqld.pid
    lower_case_table_names=1
    #指定主机号,不允许出现重复
    server-id=423307
    #开启binlog
    log-bin=mysql-bin
    auto_increment_increment=2
    auto_increment_offset=1
    
    #rpl_semi_sync_master_enabled=1
    #rpl_semi_sync_master_timeout=10000
    

    3、在master的docker容器中添加mysql权限,开启备份机复制,并且设置备份用户信息。

    #添加权限
    GRANT REPLICATION SLAVE,FILE,REPLICATION CLIENT ON *.* TO 'repluser'@'%' IDENTIFIED BY '123456';
    
    #刷新权限
    FLUSH PRIVILEGES;
    

    在这里插入图片描述
    4、设置并刷新权限后,重启mysql服务器,可以查看master上的binlog信息。

    show master status;
    

    在这里插入图片描述
    注意:至此上述步骤两台机器上都需要执行,参数上的设置按实际情况来定。

    5、接着在slave中进入到mysql容器,设置master信息,用于标注当前slave的master是谁。

    change master to master_host='localhost',master_port=3307,master_user='repluser',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=154;
    

    参数解析

    change master to master_host='master的ip',
    master_port=master的端口号,
    master_user='repluser',master_password='123456',
    master_log_file='master中的binlob文件',
    master_log_pos=master中的position位置信息;
    

    在这里插入图片描述
    6、设置完成后,还要开启slave中的IOSQL线程,这两个线程主要用于slave中进行数据备份,可以先查看slave中这两个线程的状态。

    show slave status\G
    

    在这里插入图片描述
    开启slave中的IOSQL线程

    start slave;
    

    在这里插入图片描述
    至此mysql主从复制搭建完成。

    7、 相关状态信息的查看

    查看slave中的binlog是否已经开启

    show global variables like "%log%";
    

    在这里插入图片描述
    接着还可以查看master、slave中的进程信息

    在master mysql中输入:

    show processlist;
    

    在这里插入图片描述
    从上图中可以看出:master已经把所有的binlog发送给slave,并且等待更多的更新操作。

    在slave mysql中输入:

    show processlist;
    

    在这里插入图片描述
    从上图可以看出:在slave它已经连接到了master,正在等待master发送事件,并且slave已经读取了所有的relay log信息,并且正在等待更多的更新操作。

    8、测试验证

    连接主库,并在主库中创建数据库,创建数据库表以及添加行记录。此时会发现从库中也会创建相应的数据库和数据库表和行记录。

    1.1.2、 MySQL复制原理

    在mysql中,其有两种复制机制,分别是:异步复制半同步复制。默认采用异步复制。(上述主从复制操作为异步复制)

    异步复制执行流程

    在这里插入图片描述

    • 1、应用事务提交到master
    • 2、master接收到应用事务提交请求后,会更新内部的binlog日志,接着让mysql引擎执行事务操作,并返回给客户端执行结果信息。同时在master中会存在一个事件监听,其会一直监听master中binlog日志文件的改变,一旦发现日志文件发生改变,则会触发dump线程。
    • 3、dump线程被触发后,会通知slave中的IO线程现在有事务操作需要进行同步。
    • 4、slave中IO线程接收到通知后,会从slave中relay-log.info文件中获取slave中的binlog日志文件和pos位置信息。接着会把这部分信息发送给master的dump线程。
    • 5、master的dump线程接收到这些信息后,会根据slave发送的binlog日志文件和pos位置,将最新的binlog日志和pos位置后面的内容同步给slave的IO线程。
    • 6、slave的IO线程接收到这些信息后,会将这部分内容同步到slave的relay-bin文件中。
    • 7、当relay-bin文件发生改变后,会触发slave线程执行sql操作。(异步操作)
    • 8、当slave向relay-bin写入完成后,还会向master返回一个ACK消息,通知slave已经执行成功。

    总结:对于这一系列操作,可以发现master和slave在进行同步时是以异步的方式完成的,master写入完binlog后,会马上通过引擎进行事务提交并向客户端返回响应,对于与slave同步的操作,则是异步完成的。

    虽然这种方式的RT很快,但是很容易出现数据不一致的情况。

    半同步复制执行流程

    在这里插入图片描述

    • 半同步复制与异步复制的工作流程大体相似,但不同的是,当master中的binlog日志写入完成后,其不会马上通过引擎进行事务提交,而会处于等待,等到slave同步完成向master返回ACK通知后,才会唤醒等待,继续向下执行。
    • 等待的时长,默认为10秒,但该时间可以配置。
    • 半同步复制尽量的避免的主从数据不一致的情况,但是会造成吞吐量的降低。

    对于这个问题,mysql也进行了解决,假设使用半同步复制进行备份时,slave节点挂掉了,那么当master等待10秒后,仍然会进行引擎提交,同时会将半同步复制切换为异步复制。等到slave节点重启后,又会自动的从异步复制切换到半同步复制。

    主从异步复制日志效果

    Mysql在进行复制操作时,默认是基于异步复制完成的。那为了更好的体会异步复制的效果,可以通过mysql日志来查看具体的复制过程效果。

    启动主从两台Mysql服务器。

    查看master的Mysql日志信息

    docker logs -f mysql3307 | grep binlog_dump
    

    在这里插入图片描述
    ​根据当前查看的日志信息,在master中已经开启了dump线程连接到了id为273307的slave节点,并且该id就是在slave的mysql配置文件中设置的id。

    同时pos内容包括当前的binlog日志和pos位置。

    查看slave的mysql日志信息
    在这里插入图片描述
    根据slave中的日志信息,可以看到,当前slave中已经开启了relay-log日志,其对应文件信息就是xxxxx-relay-bin。其内部保存的就是slave中的相关binlog信息和pos位置信息。

    同时在slave中也已经开启了SQL Thread,并且根据信息可以,它会从xxxx-relay-bin.000001文件的4位置开始复制。

    同时在slave中也开启了IO Thread,其已经连接到master,并且会从master的binlog日志的154的位置开启复制。

    查看master当前的binlog日志信息

    #确定当前master正在使用的binlog日志文件
    cat mysql-bin.index
    
    #查看当前binlog日志文件内容
    tail -f mysql-bin.000002
    

    在这里插入图片描述
    查看slave当前的日志信息

    cat relay-log.info
    
    cat master.info
    

    在这里插入图片描述
    在这里插入图片描述

    cat xxxxxxxxxx-relay-bin.index
    

    在这里插入图片描述
    在这里插入图片描述
    监控slave日志信息

    tail -f 8122977f8b0a-relay-bin.000002
    

    在这里插入图片描述
    master中新增数据,触发主从同步

    • 查看master修改前后的binlog日志
    cat mysql-bin.000002 
    

    在这里插入图片描述

    • 查看slave复制前后的relay-bin日志
     tail -f 41dc8a520939-relay-bin.000002
    

    在这里插入图片描述

    1.1.3、 主从半同步复制搭建

    1、配置
    进入mysql容器,加载lib,主从节点都要配置,因为主从节点间会存在切换。

    install plugin rpl_semi_sync_master soname 'semisync_master.so';
    
    install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
    

    在这里插入图片描述
    查看插件信息

    show plugins;
    

    在这里插入图片描述
    2、启用半同步(务必先启用从库,再启用主库

    #先启用从库,再启用主库
    
    #从库:
    set global rpl_semi_sync_slave_enabled= {0|1};  # 1:启用,0:禁止
    
    #主库:
    set global rpl_semi_sync_master_enabled= {0|1}; # 1:启用,0:禁止
    
    set global rpl_semi_sync_master_timeout=10000;  # 单位为ms
    

    在这里插入图片描述
    在这里插入图片描述
    3、重启从库IO Thread

    stop slave io_thread;
    start slave io_thread;
    

    4、截止到此已经完成半同步开启配置,可以查看主库状态信息和参数信息

    #查询状态信息
    show global status like "%sync%";
    
    #查询参数信息
    show global variables like '%sync%';
    
    

    show global status like “%sync%”;
    在这里插入图片描述
    show global variables like ‘%sync%’;
    在这里插入图片描述
    根据上述的配置,当前主从两台服务器的复制方式已经改为半同步复制。接下来就可以来查看具体的效果。

    • 正常的向master中添加数据,slave可以进行正常数据更新。

    master打印日志信息如下: 开启半同步复制,关闭异步复制
    在这里插入图片描述

    • 关闭slave的IO Thread或者停止salve服务

    再次向master中添加数据。此时可以发现,当进行数据提交时,会出现等待,过了十秒后,会对数据进行保存。同时slave中不会同步的进行数据更新。
    在这里插入图片描述
    在这里插入图片描述
    如上图所示,超过时间后,半同步复制会转化为异步复制。此时复制机制就会由半同步复制转换为异步复制,当再次向master中添加数据,不会再次出现等待。

    • slave中重新开启IO Thread。

    异步复制会再次转换为半同步复制,其次,在slave IO Tthread关闭这段时间内的数据,会同步到slave中,不会出现数据丢失。

    1.2、主主复制

    对于主从复制来说,其内部会存在一台master以及一台或多台slave。但有一个非常明显的问题,master是单点存在。一旦master宕机,则无法进行数据的写入。为了解决这个问题,可以使用主主复制架构。

    在主主复制架构中,会存在两台master,没有slave。并且会对这两台master进行读写分离,两台master会进行相互的复制。

    主主复制架构图
    在这里插入图片描述
    在此架构中,两台master会进行双向复制,为什么这么做呢? 因为假设现在负责写的master宕机了,那么写的工作则会交给之前负责读的服务器来完成,相当于它即负责写又负责读。等到原先负责写的master恢复了,其在继续负责写工作。 反之亦然。因此才需要两者间进行双向复制。

    此时缺点也非常明显,虽然master不存在单点了,但是对于读来说,如果并发量大的话,它肯定扛不住。对于主主复制架构来说,应用较少。

    1.2.1、主主复制搭建

    主主复制的搭建和主从非常类似,只不过主主复制会进行互指。

    1、参照主从完成搭建。(按照上述主从复制结构搭建)

    2、原slave端也要开启权限

    #添加权限
    GRANT REPLICATION SLAVE,FILE,REPLICATION CLIENT ON *.* TO 'repluser'@'%' IDENTIFIED BY '123456';
    
    #刷新权限
    FLUSH PRIVILEGES;
    
    #重启mysql服务并查看binlog信息
    show master status
    

    3、在master这一端也要配置slave的相关配置

    change master to master_host='localhost',master_port=3308,master_user='repluser',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=154;
    
    start slave;
    

    4、查看master和slave的进程列表:show processlist。可以发现他们现在互为主备。

    master
    在这里插入图片描述
    slave在这里插入图片描述

    5、测试
    当在两台服务器中添加数据,都可以完成双向同步。

    1.3、级联复制架构

    当读压力现在增大并且还想减小主从复制的性能消耗,可以采用级联复制架构。
    在这里插入图片描述
    写请求的入口仍为一个,但当master向slave进行复制时,对于slave可以分为多层, master只要向其中两台slave复制即可,然后再由slave将其数据复制到后面更多的slave中。

    通过这种方式可以减轻master向slave复制的IO压力。

    但是这种架构也存在一个弊端:slave的延迟会加大。

    1.4、双主与级联复制结合架构

    对于master在前面几种架构设计中,都存在单点问题, 对于master单点问题的解决,可以采用当前的架构。通过这种架构不仅可以解决master单点的问题,也可以解决slave延迟的问题。
    在这里插入图片描述

    2、Mysql高可用实践

    以主主架构为例,现在不管写或者读,只要其中一个宕机,则会把它本身工作交给另外一台服务器完成。此时就需要对IP进行一个自动的指向。而且这种服务器IP切换,对于上层应用来说,应该是完全隐藏的,其无需知道当前是由谁来完成具体工作,其只需要来连接一个IP就可以。

    对于这种需求,就需要通过keepAlived来完成IP的自动切换。
    在这里插入图片描述
    对于keepalived会在多台mysql服务器进行安装, 同时keepalived间也分为master和slave, 同时master会虚拟化一个VIP供应用进行连接。 如果一旦master挂掉后,会由slave节点继续工作,同时slave节点也会虚拟出相同VIP,供应用进行连接。

    2.1、keepAlived高可用配置

    1、安装keepalived

    1. 下载keepalied安装包 http://www.keepalived.org/download.html
    2. yum -y install openssl-devel gcc gcc-c++
    3. mkdir /etc/keepalived
    4. 上传安装包并解压  tar -zxvf keepalived-2.0.18.tar.gz
    5. mv keepalived-2.0.18 /usr/local/keepalived
    6. cd /usr/local/keepalived
    7. ./configure && make && make install
    8.创建启动文件
    cp  -a /usr/local/etc/keepalived   /etc/init.d/
    cp  -a /usr/local/etc/sysconfig/keepalived    /etc/sysconfig/
    cp  -a /usr/local/sbin/keepalived    /usr/sbin/
    

    2、编写执行shell脚本

    进入/etc/keepalived。创建chk.sh,同时赋予执行权限:chmod +x chk.sh

    #! /bin/bash
    mysql -h 127.0.0.1 -u root -p123456 -P 3312 -e "show status;" >/dev/null 2>&1
    if [ $? == 0 ]
    then
        echo " $host mysql login successfully "
        exit 0
    else
        echo "  mysql login faild"
        killall keepalived
        exit 2
    fi
    

    3、编写keepAlived配置文件

    cd /etc/keepalived
    
    vi keepalived.conf
    
    ! Configuration File for keepalived
    #简单的头部,这里主要可以做邮件通知报警等的设置,此处就暂不配置了;
    global_defs {
        #notificationd LVS_DEVEL
        router_id MYSQL_4   #唯一标识不允许出现重复
        script_user root
        enable_script_security
    }
    #预先定义一个脚本,方便后面调用,也可以定义多个,方便选择;
    vrrp_script chk_haproxy {
        script "/etc/keepalived/chk.sh"
        interval 2  #脚本循环运行间隔
    }
    #VRRP虚拟路由冗余协议配置
    vrrp_instance VI_1 {   #VI_1 是自定义的名称;
        state BACKUP    #MASTER表示是一台主设备,BACKUP表示为备用设备【我们这里因为设置为开启不抢占,所以都设置为备用】
        nopreempt      #开启不抢占
        interface ens33   #指定VIP需要绑定的物理网卡
        virtual_router_id 11   #VRID虚拟路由标识,也叫做分组名称,该组内的设备需要相同
        priority 130   #定义这台设备的优先级 1-254;开启了不抢占,所以此处优先级必须高于另一台
    
        advert_int 1   #生存检测时的组播信息发送间隔,组内一致
        authentication {    #设置验证信息,组内一致
            auth_type PASS   #有PASS 和 AH 两种,常用 PASS
            auth_pass 111111    #密码
        }
        virtual_ipaddress {
            192.168.200.200    #指定VIP地址,组内一致,可以设置多个IP
        }
        track_script {    #使用在这个域中使用预先定义的脚本,上面定义的
            chk_haproxy
        }
    }
    

    4、启动keepAlived

    systemctl start keepalived
    

    5、查看keepAlived执行状态

    ps -ef|grep keepalived
    

    在这里插入图片描述
    6、可以通过tail -f /var/log/messages

    7、查看ip信息,此时可以发现出现了配置的虚拟ip

    ip a
    

    8、测试
    通过navicat使用虚拟IP连接mysql,当前连接IP为VIP。可以连接成功。

    3、数据切分核心思想

    3.1、为什么要进行数据切分?

    当前微服务架构非常流行,很多都会采用微服务架构对其系统进行拆分。 而虽然产生了多个微服务,但因为其用户量和数据量的问题,很有可能仍然使用的是同一个数据库。
    在这里插入图片描述
    但是随着用户量和数据量增加,就会出现很多影响数据库性能的因素,如:数据存储量、IO瓶颈、访问量瓶颈等。此时就需要将数据进行拆分,从一个库拆分成多个库。

    3.2、数据拆分方式

    垂直拆分

    垂直拆分是按照业务将表进行分类并分布到不同的数据节点上。在初始进行数据拆分时,使用垂直拆分是非常直观的一种方式。
    在这里插入图片描述
    垂直拆分的优点:

    • 拆分规则明确,按照不同的功能模块或服务分配不同的数据库。
    • 数据维护与定位简单。

    垂直拆分的缺点:

    • 对于读写极其频繁且数据量超大的表,仍然存在存储与性能瓶颈。简单的索引此时已经无法解决问题。
    • 会出现跨库join。
    • 需要对代码进行重构,修改原有的事务操作。
    • 某个表数据量达到一定程度后扩展起来较为困难。

    水平拆分

    ​为了解决垂直拆分出现的问题,可以使用水平拆分继续横向扩展,首先,可以如果当前数据库的容量没有问题的话,可以对读写极其频繁且数据量超大的表进行分表操作。由一张表拆分出多张表。

    在一个库中,拆分出多张表,每张表存储不同的数据,这样对于其操作效率会有明显的提升。而且因为处于同一个库中,也不会出现分布式事务的问题。
    在这里插入图片描述

    而拆分出多张表后,如果当前数据库的容量已经不够了,但是还要继续拆分的话,就可以进行分库操作,产生多个数据库,然后在扩展出的数据库中继续扩展表。
    在这里插入图片描述

    水平拆分的优点:

    • 尽量的避免了跨库join操作。
    • 不会存在超大型表的性能瓶颈问题。
    • 事务处理相对简单。
    • 只要拆分规则定义好,很难出现扩展性的限制。

    水平拆分的缺点:

    • 拆分规则不好明确,规则一定会和业务挂钩,如根据id、根据时间等。
    • 不好明确数据位置,难以进行维护。
    • 多数据源管理难度加大,代码复杂度增加。
    • 也会存在分布式事务问题
    • 数据库维护成本增加

    数据切分带来的问题

    • 按照用户ID求模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中。
    • 按照日期,将不同月甚至日的数据分散到不同的库中。
    • 按照某个特定的字段求模,或者根据特定范围段分散到不同的库中。

    数据切分带来的核心问题

    • 产生引入分布式事务的问题。
    • 跨节点 Join 的问题。
    • 跨节点合并排序分页问题。

    3.3、Mycat中间件使用

    当对数据拆分后会产生诸多的问题,对于这些问题的解决,可以借助于数据库中间件来进行解决,现在时下比较流行的是使用Mycat。

    Mycat是一款数据库中间件,对于应用程序来说是完全透明化的,不管底层的数据如何拆分,应用只需要连接Mycat即可完成对数据的操作。同时它还支持MySQL、SQL Server、Oracle、DB2、PostgreSQL等主流数据库。但是Mycat不会进行数据存储,它只是用于数据的路由。

    其底层是基于拦截思想实现,其会拦截用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。
    在这里插入图片描述

    Mycat特性

    • 支持SQL92标准
    • 遵守Mysql原生协议,跨语言,跨平台,跨数据库的通用中间件代理。
    • 基于心跳的自动故障切换,支持读写分离,支持MySQL主从,以及galera cluster集群。
    • 支持Galera for MySQL集群,Percona Cluster或者MariaDB cluster
    • 基于Nio实现,有效管理线程,高并发问题。
    • 支持数据的多片自动路由与聚合,支持sum,count,max等常用的聚合函数。
    • 支持单库内部任意join,支持跨库2表join。
    • 支持通过全局表,ER关系的分片策略,实现了高效的多表join查询。
    • 支持多租户方案。
    • 支持分布式事务(弱xa)。
    • 支持全局序列号,解决分布式下的主键生成问题。
    • 分片规则丰富,插件化开发,易于扩展。
    • 强大的web,命令行监控。
    • 支持前端作为mysq通用代理,后端JDBC方式支持Oracle、DB2、SQL Server 、 mongodb 。
    • 支持密码加密
    • 支持服务降级
    • 支持IP白名单
    • 支持SQL黑名单、sql注入攻击拦截
    • 支持分表(1.6)
    • 集群基于ZooKeeper管理,在线升级,扩容,智能优化,大数据处理(2.0开发版)。

    Mycat源码的本地部署运行

    **源码下载:**https://codeload.github.com/MyCATApache/Mycat-Server/zip/Mycat-server-1675-release

    默认端口:8066

    配置启动参数:

    -DMYCAT_HOME=D:\workspace\Mycat-Server-Mycat-server-1675-release\src\main
    #设置堆外内存大小
    -XX:MaxDirectMemorySize=512M 
    

    注意:为什么要设置堆外内存:当使用mycat对非分片查询时,会把所有的数据查询出来,然后把这部分数据放在堆外内存中

    在Mycat有核心三个配置文件,分别为:sever.xml、schema.xml、rule.xml

    • server.xml:是Mycat服务器参数调整和用户授权的配置文件。
    • schema.xml:是逻辑库定义和表以及分片定义的配置文件
    • rule.xml:是分片规则的配置文件,分片规则的具体一些参数信息单独存放为文件,也在这个目录下,配置文件修改需要重启MyCAT。

    MyCat核心概念

    在学习Mycat首先需要先对其内部一些核心概念有足够的了解。

    • 逻辑库:Mycat中的虚拟数据库。对应实际数据库的概念。在没有使用mycat时,应用需要确定当前连接的数据库等信息,那么当使用mycat后,也需要先虚拟一个数据库,用于应用的连接。
    • 逻辑表:mycat中的虚拟数据表。对应时间数据库中数据表的概念。
    • 非分片表:没有进行数据切分的表。
    • 分片表:已经被数据拆分的表,每个分片表中都有原有数据表的一部分数据。多张分片表可以构成一个完整数据表。
    • ER表:子表的记录与所关联的父表记录存放在同一个数据分片上,即子表依赖于父表,通过表分组(Table Group)保证数据Join不会跨库操作。表分组(Table Group)是解决跨分片数据join的一种很好的思路,也是数据切分规划的重要一条规则
    • 全局表:可以理解为是一张数据冗余表,如状态表,每一个数据分片节点又保存了一份状态表数据。数据冗余是解决跨分片数据join的一种很好的思路,也是数据切分规划的另外一条重要规则。
    • 分片节点(dataNode):数据切分后,每一个数据分片表所在的数据库就是分片节点。
    • 节点主机(dataHost):数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。
    • 分片规则(rule):按照某种业务规则把数据分到某个分片的规则就是分片规则。
    • 全局序列号(sequence):也可以理解为分布式id。数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用,因此需要引入外部机制保证数据唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号(sequence),如UUID、雪花算法等。

    4、Mycat企业级应用实践

    4.1、环境参数配置

    在server.xml 文件中的system标签下配置所有的参数,全部为环境参数,可以根据当前需要进行开启和配置,如:设置mycat连接端口号

    <property name="serverPort">8066</property>
    

    在这里插入图片描述

    4.2、数据非分片

    4.2.1、配置初始化信息

    应用连接mycat的话,也需要设置用户名、密码、被连接数据库信息,要配置这些信息的话,可以修改server.xml,在其内部添加内容如下:

    <!--配置自定义用户信息-->
    <!--连接用户名-->
    <user name="mycat">
        <!--连接密码-->
        <property name="password">mycat</property>
        <!--创建虚拟数据库-->
        <property name="schemas">userdb</property>
        <!--指定该库是否只读-->
        <!--<property name="readOnly">true</property>-->
    </user>
    

    4.2.2、配置虚拟数据库&表

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
    
    	<!--配置虚拟数据库-->
    	<!--name:虚拟逻辑数据库名称,对应server.xml中的schemas属性值-->
    	<!--dataNode:逻辑库中逻辑表的默认数据节点-->
    	<!--sqlMaxLimit:类似于SQL上添加limit,如schema为非分片库,则该属性无效-->
    	<schema name="userdb" checkSQLschema="true" dataNode="localdn" sqlMaxLimit="500">
    		<!--配置虚拟逻辑表-->
    		<!--name:逻辑表名称,必须唯一-->
    		<!--dataNode:逻辑表所处的数据节点,值必须与dataNode标签中的name属性对应。如果值过多可以用$连接,如:dn$1-99,dn$200-400-->
    		<!--primaryKey:逻辑表对应的真实表的主键id的字段名-->
    		<table name="tb_user" dataNode="localdn" primaryKey="user_id"/>
    	</schema>
    
    	<!--配置dataNode信息-->
    	<!--name:当前datanode名称-->
    	<!--dataHost:分片节点所处的节点主机,该值必须与dataHost标签中的name属性对应-->
    	<!--database:当前数据节点所对应的实际物理数据库-->
    	<dataNode name="localdn" dataHost="localdh" database="user"/>
    
    	<!--配置节点主机-->
    	<!--balance:用于进行读操作指向,有三个值可选
    		0:所有读操作都发送到当前可用的writeHost上
    		1:所有读操作都随机的发送到readHost上
    		2:所有读操作都随机发送在writeHost与readHost上
    	-->
    	<!--maxCon:指定每个读写实例连接池的最大连接。也就是说,标签内嵌套的writeHost、readHost标签都会使用这个属性的值来实例化出连接池的最大连接数-->
    	<!--minCon:指定每个读写实例连接池的最小连接,初始化连接池的大小-->
    	<!--name:当前节点主机名称,不允许出现重复-->
    	<!--dbType:当时使用的数据库类型-->
    	<!--dbDriver:当前使用的数据库驱动-->
    	<!--writeType:用于写操作指向,有三个值可选
    		0:所有写操作都发送到可用的writeHost上
    		1:所有写操作都随机发送到readHost上
    		2:所有写操作都随机发送在writeHost与readHost上
    	-->
    	<!--readHost是从属于writeHost的,即意味着它从那个writeHost获取同步数据。
    		因此,当它所属的writeHost宕机了,则它也不会再参与到读写分离中来,即“不工作了”。这是因为此时,它的数据已经“不可靠”了。
    		基于这个考虑,目前mycat 1.3和1.4版本中,若想支持MySQL一主一从的标准配置,并且在主节点宕机的情况下,从节点还能读取数据。
    		则需要在Mycat里配置为两个writeHost并设置banlance=1。”-->
    	<!--switchType:设置节点切换操作,有三个值可选
    		-1:不自动切换
    		1:自动切换,默认值
    		2:基于mysql主从同步的状态决定是否切换
    	-->
    	<!--slaveThreshold:主从同步状态决定是否切换,延迟超过该值就不切换-->
    	<dataHost balance="0" maxCon="100" minCon="10" name="localdh" dbType="mysql" dbDriver="jdbc" writeType="0" switchType="1" slaveThreshold="1000">
    		<!--查询心跳-->
    		<heartbeat>select user()</heartbeat>
    		<!--配置写节点实际物理数据库信息-->
    		<writeHost url="jdbc:mysql://localhost:3306" host="host1" password="root" user="root"></writeHost>
    	</dataHost>
    </mycat:schema>
    

    4.2.3 测试

    通过navicat创建本地数据库连接并创建对应数据库,同时创建mycat连接。 在mycat连接中操作表,添加数据,可以发现,本地数据库中同步的也新增了对应的数据。

    4.3、根据ID取模数据分片

    当一个数据表中的数据量非常大时,就需要考虑对表内数据进行分片,拆分的规则有很多种,比较简单的一种就是,通过对id进行取模,完成数据分片。

    1)修改schema.xml

    table标签新增属性:subTables、rule

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
    
    	<!--配置虚拟数据库-->
    	<!--name:虚拟逻辑数据库名称,对应server.xml中的schemas属性值-->
    	<!--dataNode:逻辑库中逻辑表的默认数据节点-->
    	<!--sqlMaxLimit:类似于SQL上添加limit,如schema为非分片库,则该属性无效-->
    	<schema name="userdb" checkSQLschema="true" dataNode="localdn" sqlMaxLimit="500">
    		<!--配置虚拟逻辑表-->
    		<!--name:逻辑表名称,必须唯一-->
    		<!--dataNode:逻辑表所处的数据节点,值必须与dataNode标签中的name属性对应。如果值过多可以用$连接,如:dn$1-99,dn$200-400-->
    		<!--primaryKey:逻辑表对应的真实表的主键id的字段名-->
    		<!--subTables:分表的名称。可以存在多个,tb_user1,tb_user2,tb_user3.如果分表较多,可以通过$连接:tb_user$1-3-->
    		<!--rule:分片规则,对应rule.xml中配置-->
    		<table name="tb_user" dataNode="localdn" primaryKey="user_id" subTables="tb_user$1-3" rule="mod-long"/>
    	</schema>
    
    	<!--配置dataNode信息-->
    	<!--name:当前datanode名称-->
    	<!--dataHost:分片节点所处的节点主机,该值必须与dataHost标签中的name属性对应-->
    	<!--database:当前数据节点所对应的实际物理数据库-->
    	<dataNode name="localdn" dataHost="localdh" database="user"/>
    
    	<!--配置节点主机-->
    	<!--balance:用于进行读操作指向,有三个值可选
    		0:所有读操作都发送到当前可用的writeHost上
    		1:所有读操作都随机的发送到readHost上
    		2:所有读操作都随机发送在writeHost与readHost上
    	-->
    	<!--maxCon:指定每个读写实例连接池的最大连接。也就是说,标签内嵌套的writeHost、readHost标签都会使用这个属性的值来实例化出连接池的最大连接数-->
    	<!--minCon:指定每个读写实例连接池的最小连接,初始化连接池的大小-->
    	<!--name:当前节点主机名称,不允许出现重复-->
    	<!--dbType:当时使用的数据库类型-->
    	<!--dbDriver:当前使用的数据库驱动-->
    	<!--writeType:用于写操作指向,有三个值可选
    		0:所有写操作都发送到可用的writeHost上
    		1:所有写操作都随机发送到readHost上
    		2:所有写操作都随机发送在writeHost与readHost上
    	-->
    	<!--readHost是从属于writeHost的,即意味着它从那个writeHost获取同步数据。
    		因此,当它所属的writeHost宕机了,则它也不会再参与到读写分离中来,即“不工作了”。这是因为此时,它的数据已经“不可靠”了。
    		基于这个考虑,目前mycat 1.3和1.4版本中,若想支持MySQL一主一从的标准配置,并且在主节点宕机的情况下,从节点还能读取数据。
    		则需要在Mycat里配置为两个writeHost并设置banlance=1。”-->
    	<!--switchType:设置节点切换操作,有三个值可选
    		-1:不自动切换
    		1:自动切换,默认值
    		2:基于mysql主从同步的状态决定是否切换
    	-->
    	<!--slaveThreshold:主从同步状态决定是否切换,延迟超过该值就不切换-->
    	<dataHost balance="0" maxCon="100" minCon="10" name="localdh" dbType="mysql" dbDriver="jdbc" writeType="0" switchType="1" slaveThreshold="1000">
    		<!--查询心跳-->
    		<heartbeat>select user()</heartbeat>
    		<!--配置写节点实际物理数据库信息-->
    		<writeHost url="jdbc:mysql://localhost:3306" host="host1" password="root" user="root"></writeHost>
    	</dataHost>
    </mycat:schema>
    

    2)修改rule.xml

    在schema.xml中已经指定规则为mod-long。因此需要到该文件中修改对应信息。

    <tableRule name="mod-long">
        <rule>
            <!--当用用于id取模的字段-->
            <columns>user_id</columns>
            <algorithm>mod-long</algorithm>
        </rule>
    </tableRule>
    
    <!--修改当前的分片数量-->
    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
    		<!-- how many data nodes -->
    		<!-- 根据datanode数量进行取模分片,也就是要模几。 -->
    		<property name="count">3</property>
    	</function>
    

    3)测试

    • 向数据库中插入一千条数据,可以发现,其会根据id取模,放入不同的三张表中。

    • 当根据id查询时,会通过对id的取模,确定当前要查询的分片。并且首先会先查询mycat中的ehcache缓存,再来查询数据分片。

    • 当查询所有数据时,会查询所有数据分片。

    4)缺陷

    通过id取模分片这种方式实际中应用较少。主要因为两点问题:

    根据id取模,1)散列不均匀,出现数据倾斜。2)动态扩容时,存在rehash,出现数据丢失。

    1)数据散列不均匀,容易出现数据倾斜。每张表中的数据量差距较大。

    2)动态扩容后,当需要新增表时,需要对模数修改,有可能就会造成当查询某个分片时,在该分片中找不到对应数据。

    3)动态扩容后,要进行rehash操作。

    4.4、全局序列号

    当进行数据切分后,数据会存放在多张表中,如果仍然通过数据库自增id的方式,就会出现ID重复的问题,造成数据错乱。所以当拆分完数据后,需要让每一条数据都有自己的ID,并且在多表中不能出现重复。比较常见的会使用雪花算法来生成分布式id。

    在Mycat中也提供了四种方式来进行分布式id生成:基于文件、基于数据库、基于时间戳和基于ZK。

    4.4.1、基于本地文件方式生成

    优点:本地加载,读取速度较快。

    缺点:当MyCAT重新发布后,配置文件中的sequence会恢复到初始值。

    ​生成的id没有含义,如时间。

    ​MyCat如果存在多个,会出现id重复问题。

    1)修改sequence_conf.properties

    USER.HISIDS=  #使用过的历史分段,可不配置
    USER.MINID=1  #最小ID值
    USER.MAXID=200000  #最大ID值
    USER.CURID=1000  #当前ID值
    

    2)修改server.xml

    <!--设置全局序号生成方式
       0:文件
       1:数据库
       2:时间戳
       3:zookeeper
      -->
    <property name="sequnceHandlerType">0</property>
    <!--进入序列匹配流程, 必须带有MYCATSEQ_或者 mycatseq_-->
    <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|\)|\s)*)+</property>
    <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
    

    3)测试

    重启mycat,并查询是否修改成功

    show @@sysparam
    

    在这里插入图片描述

    通过navicat插入数据

    insert into tb_user(user_id,user_name) values('next value for MYCATSEQ_USER','wangwu')
    

    通过程序插入数据

    @Insert("insert into tb_user(user_id,user_name) values('next value for MYCATSEQ_USER',#{userName})")
    void addUser(User user);
    

    4.4.2、基于数据库生成

    优点:能够进行id批量生成,在分布式下,可以避免id重复问题。

    缺点:ID没有意义,对数据库有压力。

    1)在实际数据库执行dbseq.sql中的sql语句,执行完毕后,会创建一张表。
    在这里插入图片描述

    2)修改sequence_db_conf.properties

    TB_USER=localdn
    

    3)修改server.xml文件,修改全局序列号生成方式为数据库方式

    <property name="sequnceHandlerType">1</property>
    

    4)修改schema.xml。在table中添加自增属性

    <table name="tb_user" dataNode="localdn" primaryKey="id" subTables="tb_user$1-3" rule="mod-long" autoIncrement="true"/>
    

    5)测试

    通过navicat新增记录

    insert into tb_user(user_id,user_name) values('next value for MYCATSEQ_TB_USER','wangwu')
    

    在这里插入图片描述

    4.4.3、基于zookeeper生成

    1)修改server.xml,更改生成模式

    <property name="sequenceHandlerType">3</property>
    

    2)修改myid.properties,配置zk连接信息

    loadZk=true
    zkURL=192.168.200.131:2181
    clusterId=01
    myid=mycat_fz_01
    clusterNodes=mycat_fz_01
    #server  booster  ;   booster install on db same server,will reset all minCon to 1
    #type=server
    #boosterDataHosts=localhost1
    

    3)修改sequence_distributed_conf.properties

    INSTANCEID=ZK #声明使用zk生成
    CLUSTERID=01
    

    4)测试

    启动mycatServer后,通过zk客户端查看节点信息。会发现新增了一个mycat节点

    ./zkCli.sh
    
    ls /
    

    在这里插入图片描述
    插入数据

    insert into tb_user(user_id,user_name) values('n
    ext value for MYCATSEQ_TB_USER12','heima')
    

    next value for MYCATSEQ_ 后的内容可以随意指定。
    在这里插入图片描述

    5)特性:

    ID 结构:long 64 位,ID 最大可占 63 位

    * |current time millis(微秒时间戳 38 位,可以使用 17 年)|clusterId(机房或者 ZKid,通过配置文件配置 5位)|instanceId(实例 ID,可以通过 ZK 或者配置文件获取,5 位)|threadId(线程 ID,9 位)|increment(自增,6 位)

    * 一共 63 位,可以承受单机房单机器单线程 1000*(2^6)=640000 的并发。

    * 无悲观锁,无强竞争,吞吐量更高

    7.4.4)基于时间戳生成

    优点:不存在上面两种方案因为mycat的重启导致id重复的现象,ID= 64 位二进制 (42(毫秒)+5(机器 ID)+5(业务编码)+12(重复累加),每毫秒可以并发 12 位二进制的累加。

    缺点:数据类型太长,建议采用bigint(最大取值18446744073709551615)

    1)修改server.xml。更改生成方式

    <property name="sequenceHandlerType">2</property>
    

    2)修改sequence_time_conf.properties

    #sequence depend on TIME
    #WORKID与DATAACENTERID: 0-31 任意整数。多mycat节点下,每个节点的WORKID、DATAACENTERID不能重复,组成唯一标识,总共支持32*32=1024 种组合
    WORKID=01
    DATAACENTERID=01
    

    3)测试

    新增数据

    insert into tb_user(user_id,user_name) values('next value for MYCATSEQ_TB_USER12','heima')
    

    next value for MYCATSEQ_ 后的内容可以随意指定。
    在这里插入图片描述

    5、MyCat分库&读写分离

    之前已经基于id取模完成了分表操作,但是一个数据库的容量毕竟是有限制的,如果数据量非常大,分表已经满足不了的话,就会进行分库操作。

    ​当前分库架构如下:
    在这里插入图片描述

    现在存在两个主库,并且各自都有从节点。 当插入数据时,根据id取模放入不同的库中。同时主从间在进行写时复制的同时,还要完成主从读写分离的配置。

    1)修改schema.xml。配置多datenode与datahost。同时配置主从读写分离。

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
    
        <schema name="userdb" checkSQLschema="true" dataNode="dn09" sqlMaxLimit="500">
            <table name="tb_user" dataNode="dn09,dn10" primaryKey="user_id" rule="mod-long"/>
        </schema>
    
        
        <dataNode name="dn09" dataHost="dh09" database="user"/>
        <dataNode name="dn10" dataHost="dh10" database="user"/>
    
        <dataHost name="dh09" balance="1" maxCon="100" minCon="10"  dbType="mysql" dbDriver="jdbc" writeType="0" switchType="1" slaveThreshold="1000">
            <!--查询心跳-->
            <heartbeat>select user()</heartbeat>
            <!--配置写节点实际物理数据库信息-->
            <writeHost url="jdbc:mysql://192.168.200.142:3309" host="host1"  user="root" password="123456">
                <!--配置读节点实际物理数据库信息-->
                <readHost host="host2" url="jdbc:mysql://192.168.200.145:3309" user="root" password="123456" ></readHost>
            </writeHost>
        </dataHost>
    
        <dataHost name="dh10" balance="1" maxCon="100" minCon="10"  dbType="mysql" dbDriver="jdbc" writeType="0" switchType="1" slaveThreshold="1000">
            <!--查询心跳-->
            <heartbeat>select user()</heartbeat>
            <!--配置写节点实际物理数据库信息-->
            <writeHost url="jdbc:mysql://192.168.200.142:3310" host="host1"  user="root" password="123456">
                <!--配置读节点实际物理数据库信息-->
                <readHost host="host2" url="jdbc:mysql://192.168.200.145:3310" user="root" password="123456" ></readHost>
            </writeHost>
        </dataHost>
    </mycat:schema>
    

    2)修改rule.xml。配置取模时的模数

    <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
        <!-- how many data nodes -->
        <!-- 根据datanode数量进行取模分片,也就是要模几。 -->
        <property name="count">2</property>
    </function>
    

    3)进行批量数据添加,可以发现数据落在了不同的库中。
    在这里插入图片描述
    在这里插入图片描述

    4)读写分离验证

    设置log4j2.xml的日志级别为DEBUG

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="DEBUG">
    		........
            <asyncRoot level="DEBUG" includeLocation="true">
    			........
            </asyncRoot>
        </Loggers>
    </Configuration>
    

    基于mysql服务进行数据查看,观察控制台信息,可以看到对于read请求的数据源,分别使用的是配置文件的配置。

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 分布式系统数据库系统原理(第三版)中的描述:“我们把分布式数据库定义为一群分布计算机网络上、逻辑上相互关联的数据库。分布式数据库管理系统(分布式DBMS)则是支持管理分布式数据库的软件系统,它使得分布对于...
  • 数据库架构设计——分布式数据库设计

    千次阅读 多人点赞 2022-01-16 07:55:16
    摘要 现在互联网应用已经普及,数据量不断增大。...Wiki 官方对分布式数据库定义为: A distributed database is a database in which data is stored across different physical locations. It may

    摘要

    现在互联网应用已经普及,数据量不断增大。对淘宝、美团、百度等互联网业务来说,传统单实例数据库很难支撑其性能和存储的要求,所以分布式架构得到了很大发展。一定要认识到数据库技术正在经历一场较大的变革,及早掌握好分布式架构设计。

    一、分布式数据库概念

    Wiki 官方对分布式数据库的定义为:

    A distributed database is a database in which data is stored across different physical locations. It may be stored in multiple computers located in the same physical location (e.g. a data centre); or maybe dispersed over a network of interconnected computers.

    从定义来看,分布式数据库是一种把数据分散存储在不同物理位置的数据库。对比我们之前学习的数据库,数据都是存放在一个实例对应的物理存储上,而在分布式数据库中,数据将存放在不同的数据库实例上。

    分布式数据库的架构

    从图中我们可以看到,在分布式数据库下,分布式数据库本身分为计算层、元数据层和存储层:

    • 计算层就是之前单机数据库中的 SQL 层,用来对数据访问进行权限检查、路由访问,以及对计算结果等操作。
    • 元数据层记录了分布式数据库集群下有多少个存储节点,对应 IP、端口等元数据信息是多少。当分布式数据库的计算层启动时,会先访问元数据层,获取所有集群信息,才能正确进行 SQL 的解析和路由等工作。另外,因为元数据信息存放在元数据层,那么分布式数据库的计算层可以有多个,用于实现性能的扩展。
    • 存储层用来存放数据,但存储层要和计算层在同一台服务器上,甚至不求在同一个进程中。

    分布式数据库的优势是把数据打散到不同的服务器上,这种横向扩展的 Scale Out 能力,能解决单机数据库的性能与存储瓶颈。

    • 从理论上来看,分布式数据库的性能可以随着计算层和存储层的扩展,做到性能的线性提升。
    • 从可用性的角度看,如果存储层发生宕机,那么只会影响 1/N 的数据,N 取决于数据被打散到多少台服务器上。所以,分布式数据库的可用性对比单机会有很大提升,单机数据库要实现99.999% 的可用性或许很难,但是分布式数据库就容易多了。
    • 当然,分布式数据库也存在缺点:正因为数据被打散了,分布式数据库会引入很多新问题,比如自增实现、索引设计、分布式事务等

    二、分布式MySQL架构

    学习分布式 MySQL 架构前,我们先看一下原先单机 MySQL 架构是怎样的。

    可以看到,原先客户端是通过 MySQL 通信协议访问 MySQL 数据库,MySQL 数据库会通过高可用技术做多副本,当发生宕机进行切换。

    那么对于分布式 MySQL 数据库架构,其整体架构如下图所示:

    从上图可以看到,这时数据将打散存储在下方各个 MySQL 实例中,每份数据叫“分片(Shard)”。在分布式 MySQL 架构下,客户端不再是访问 MySQL 数据库本身,而是访问一个分布式中间件。这个分布式中间件的通信协议依然采用 MySQL 通信协议(因为原先客户端是如何访问的MySQL 的,现在就如何访问分布式中间件)。分布式中间件会根据元数据信息,自动将用户请求路由到下面的 MySQL 分片中,从而将存储存取到指定的节点。

    另外,分布式 MySQL 数据库架构的每一层都要由高可用,保证分布式数据库架构的高可用性。对于上层的分布式中间件,是可以平行扩展的:即用户可以访问多个分布式中间件,如果其中一个中间件发生宕机,那么直接剔除即可。因为分布式中间件是无状态的,数据保存在元数据服务中,它的高可用设计比较容易。对于元数据来说,虽然它的数据量不大,但数据非常关键,一旦宕机则可能导致中间件无法工作,所以,元数据要通过副本技术保障高可用。最后,每个分片存储本身都有副本,通过我们之前学习的高可用技术,保证分片的可用性。也就是说,如果分片 1 的 MySQL 发生宕机,分片 1 的从服务器会接替原先的 MySQL 主服务器,继续提供服务。

    但由于使用了分布式架构,那么即使分片 1 发生宕机,需要 60 秒的时间恢复,这段时间对于业务的访问来说,只影响了 1/N 的数据请求。可以看到,分布式 MySQL 数据库架构实现了计算层与存储层的分离,每一层都可以进行 Scale Out 平行扩展,每一层又通过高可用技术,保证了计算层与存储层的连续性,大大提升了MySQL 数据库的性能和可靠性,为海量互联网业务服务打下了坚实的基础。

    三、数据库的分库分表

    学习了分布式数据库的架构,知道各类分布式数据库都离不开计算层、存储层、元数据层这三层关系。另外,很重要的一点是,知道分布式数据库是把数据打散存储在一个个分片中。在基于MySQL 的分布式数据库架构中,分片就存在于 MySQL 实例中。学习分布式数据库中,一个非常重要的设计:正确地把数据分片,充分发挥分布式数据库架构的优势。

    3.1 选出分片键

    对于类似淘宝、京东、拼多多这样业务体量的应用来说,单实例 MySQL 数据库在性能和存储容量上肯定无法满足“双 11、618 ”大促的要求,所以要改造成分布式数据库架构。

    在对表中的数据进行分片时,首先要选出一个分片键(Shard Key),即用户可以通过这个字段进行数据的水平拆分。对于我们之前使用的电商业务的订单表orders,其表结构如下所示:

    CREATE TABLE `orders` (
    
      `O_ORDERKEY` int NOT NULL,
      `O_CUSTKEY` int NOT NULL,
      `O_ORDERSTATUS` char(1) NOT NULL,
      `O_TOTALPRICE` decimal(15,2) NOT NULL,
      `O_ORDERDATE` date NOT NULL,
      `O_ORDERPRIORITY` char(15) NOT NULL,
      `O_CLERK` char(15) NOT NULL,
      `O_SHIPPRIORITY` int NOT NULL,
      `O_COMMENT` varchar(79) NOT NULL,
      PRIMARY KEY (`O_ORDERKEY`),
      KEY `idx_custkey_orderdate` (`O_CUSTKEY`,`O_ORDERDATE`),
      KEY `ORDERS_FK1` (`O_CUSTKEY`),
      KEY `idx_custkey_orderdate_totalprice` (`O_CUSTKEY`,`O_ORDERDATE`,`O_TOTALPRICE`),
      KEY `idx_orderdate` (`O_ORDERDATE`),
      KEY `idx_orderstatus` (`O_ORDERSTATUS`),
      CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`O_CUSTKEY`) REFERENCES `customer` (`C_CUSTKEY`)
    ) ENGINE=InnoDB

    对于上面的表orders,可以选择的分片键有:o_orderkey、o_orderdate、也可以是o_custkey。在选出分片键后,就要选择分片的算法,比较常见的有 RANGE 和 HASH 算法。

    比如,表 orders,选择分片键 o_orderdate,根据函数 YEAR 求出订单年份,然后根据RANGE 算法进行分片,这样就能设计出基于 RANGE 分片算法的分布式数据库架构:

    从图中我们可以看到,采用 RANGE 算法进行分片后,表 orders 中,1992 年的订单数据存放在分片 1 中、1993 年的订单数据存放在分片 2 中、1994 年的订单数据存放在分片 3中,依次类推,如果要存放新年份的订单数据,追加新的分片即可。

    不过,RANGE 分片算法在分布式数据库架构中,是一种非常糟糕的算法,因为对于分布式架构,通常希望能解决传统单实例数据库两个痛点:

    • 性能可扩展,通过增加分片节点,性能可以线性提升;
    • 存储容量可扩展,通过增加分片节点,解决单点存储容量的数据瓶颈。

    那么对于订单表 orders 的 RANGE 分片算法来说,你会发现以上两点都无法实现,因为当年的数据依然存储在一个分片上(即热点还是存在于一个数据节点上)。如果继续拆细呢?比如根据每天进行 RANGE 分片?这样的确会好一些,但是对“双 11、618”这样的大促来说,依然是单分片在工作,热点依然异常集中。所以在分布式架构中,RANGE 分区算法是一种比较糟糕的算法。但它也有好处:可以方便数据在不同机器间进行迁移(migrate),比如要把分片 2 中 1992 年的数据迁移到分片 1,直接将表进行迁移就行。

    而对海量并发的 OLTP 业务来说,一般推荐用 HASH 的分区算法。这样分片的每个节点都可以有实时的访问,每个节点负载都能相对平衡,从而实现性能和存储层的线性可扩展。我们来看表 orders 根据 o_orderkey 进行 HASH 分片,分片算法如下:

    在上述分片算法中,分片键是 o_orderkey,总的分片数量是 4(即把原来 1 份数据打散到 4 张表中),具体来讲,分片算法是将 o_orderkey 除以 4 进行取模操作。最终,将表orders 根据 HASH 算法进行分布式设计后的结果如下图所示:

    可以看到,对于订单号除以 4,余数为 0 的数据存放在分片 1 中,余数为 1 的数据存放在分片 2 中,余数为 2 的数据存放在分片 3 中,以此类推。这种基于 HASH 算法的分片设计才能较好地应用于大型互联网业务,真正做到分布式数据库架构弹性可扩展的设计要求。

    但是,表 orders 分区键选择 o_orderkey 是最好地选择吗?并不是。我们看一下库中的其他表,如表 customer、lineitem,这三张表应该是经常一起使用的,比如查询用户最近的订单明细。如果用 o_orderkey 作分区键,那么 lineitem 可以用 l_orderkey 作为分区键,但这时会发现表customer 并没有订单的相关信息,即无法使用订单作为分片键。如果表 customer 选择另一个字段作为分片键,那么业务数据无法做到单元化,也就是对于表customer、orders、lineitem,分片数据在同一数据库实例上。所以,如果要实现分片数据的单元化,最好的选择是把用户字段作为分区键,在表 customer 中就是将 c_custkey 作为分片键,表orders 中将 o_custkey 作为分片键,表 lineitem 中将 l_custkey 作为分片键:

    这样做的好处是:根据用户维度进行查询时,可以在单个分片上完成所有的操作,不用涉及跨分片的访问,如下面的 SQL:

    SELECT * FROM orders
    
    INNER JOIN lineitem ON o_orderkey = l_orderkey
    
    INNER JOIN customer ON o_custkey = c_custkey
    
    WHERE o_custkey = 1
    
    ORDER BY o_orderdate DESC LIMIT 10

    所以,分布式数据库架构设计的原则是:选择一个适合的分片键和分片算法,把数据打散,并且业务的绝大部分查询都是根据分片键进行访问

    那为什么互联网业务这么适合进行分布式架构的设计呢?因为互联网业务大部分是 To C 业务,分片键就是用户的 ID,业务的大部分访问都是根据用户 ID 进行查询,比如:

    • 查看某个用户下的微博/短视频;
    • 查看某个用户的商品信息/购买记录;
    • 查看某个用户自己的余额信息。

    学完分片键的选择后,接着就是规划分片,也就我们经常提到的分库分表。

    3.2 分库分表

    分片到底是什么呢?其实,前面说的分片本质是一张张表,而不是数据库实例,只是每个分片是在 MySQL 数据库实例中,严格来说:

    分片 = 实例 + 库 + 表 = ip@port:db_name:table_name

    对于前面的表orders,假设根据 HASH 算法进行分片,那么可以进行如下的分库分表设计:

    1. 每个分片的表名库名都一样,如库 tpch,表名 orders;
    2. 每个分片的库名不一样,表名一样,如库名 tpch01、tpch02、tpch03、tpch04,表名orders;
    3. 每个分片的表名不一样,库名一样,如库名 tpch,表名分别为 orders01、orders02、orders03、orders04;
    4. 每个分片的库名不一样,表名也不一样,如分片 1 的表在库名 tpch01下,表名为oders01;分片 2 的表名在库名 tpch02,表名为 orders02;分片 3 的表名在库名tpch03,表名为 orders03;分片 3 的表名在库名 tpch04,表名为 orders04。

    在这 4 种分库分表规则中,最推荐的是第 4 种,也是我们通常意义说的分库分表,这样做的好处有以下几点:

    • 不同分片的数据可以在同一 MySQL 数据库实例上,便于做容量的规划和后期的扩展;
    • 同一分片键的表都在同一库下,方便做整体数据的迁移和扩容。

    如果根据第 4 种标准的分库分表规范,那么分布式 MySQL 数据库的架构可以是这样:

     有没有发现,按上面这样的分布式设计,数据分片完成后,所有的库表依然是在同一个 MySQL实例上。牢记,分布式数据库并不一定要求有很多个实例,最基本的要求是将数据进行打散分片。接着,用户可以根据自己的需要,进行扩缩容,以此实现数据库性能和容量的伸缩性。这才是分布式数据库真正的魅力所在

    对于上述的分布式数据库架构,一开始我们将 4 个分片数据存储在一个 MySQL 实例上,但是如果遇到一些大促活动,可以对其进行扩容,比如把 4 个分片扩容到 4 个MySQL实例上:

    如果完成了大促活动,又可以对资源进行回收,将分片又都放到一台 MySQL 实例上,这就是对资源进行缩容。总的来说,对分布式数据库进行扩缩容在互联网公司是一件常见的操作,比如对阿里来说,每年下半年 7 月开始,他们就要进行双 11 活动的容量评估,然后根据评估结果规划数据库的扩容。

    3.3 数据库的扩缩容

    在 HASH 分片的例子中,我们把数据分片到了 4 个节点,然而在生产环境中,为了方便之后的扩缩容操作,推荐一开始就把分片的数量设置为不少于 1000 个。不用担心分片数量太多,因为分片 1 个还是 1000 个,管理方式都是一样的,但是 1000 个,意味着可以扩容到 1000 个实例上,对于一般业务来说,1000 个实例足够满足业务的需求了(BTW,网传阿里某核心业务的分布式数据库分片数量为 10000个)。如果到了 1000 个分片依然无法满足业务的需求,这时能不能拆成 2000 个分片呢?从理论上来说是可以的,但是这意味着需要对一张表中的数据进行逻辑拆分,这个工作非常复杂,通常不推荐。所以,一开始一定要设计足够多的分片。在实际工作中,我遇到很多次业务将分片数量从 32、64 拆成 256、512。每次这样的工作,都是扒一层皮,太不值得。所以,做好分布式数据库设计的工作有多重要!

    那么扩容在 MySQL 数据库中如何操作呢?其实,本质是搭建一个复制架构,然后通过设置过滤复制,仅回放分片所在的数据库就行,这个数据库配置在从服务器上大致进行如下配置:

    # 分片1从服务器配置
    
    replicate_do_db ="tpch01"

    所以在进行扩容时,首先根据下图的方式对扩容的分片进行过滤复制的配置:

    然后再找一个业务低峰期,将业务的请求转向新的分片,完成最终的扩容操作:

    至于缩容操作,本质就是扩容操作的逆操作

    3.4 分库分表的总结

    • 分布式数据库数据分片要先选择一个或多个字段作为分片键;
    • 分片键的要求是业务经常访问的字段,且业务之间的表大多能根据这个分片键进行单元化;
    • 如果选不出分片键,业务就无法进行分布式数据库的改造;
    • 选择完分片键后,就要选择分片算法,通常是 RANGE 或 HASH 算法;
    • 海量 OLTP 业务推荐使用 HASH 算法,强烈不推荐使用 RANGE 算法;
    • 分片键和分片算法选择完后,就要进行分库分表设计,推荐不同库名表名的设计,这样能方便后续对分片数据进行扩缩容;
    • 实际进行扩容时,可以使用过滤复制,仅复制需要的分片数据。

    四、分布式数据库索引设计

    在分布式数据库架构下,索引的设计也需要做调整,否则无法充分发挥分布式架构线性可扩展的优势

    4.1 主键选择

    对主键来说,要保证在所有分片中都唯一,它本质上就是一个全局唯一的索引。如果用大部分同学喜欢的自增作为主键,就会发现存在很大的问题。因为自增并不能在插入前就获得值,而是要通过填 NULL 值,然后再通过函数 last_insert_id()获得自增的值。所以,如果在每个分片上通过自增去实现主键,可能会出现同样的自增值存在于不同的分片上。

    比如,对于电商的订单表 orders,其表结构如下(分片键是o_custkey,表的主键是o_orderkey):

    CREATE TABLE `orders` (
      `O_ORDERKEY` int NOT NULL auto_increment,
      `O_CUSTKEY` int NOT NULL,
      `O_ORDERSTATUS` char(1) NOT NULL,
      `O_TOTALPRICE` decimal(15,2) NOT NULL,
      `O_ORDERDATE` date NOT NULL,
      `O_ORDERPRIORITY` char(15) NOT NULL,
      `O_CLERK` char(15) NOT NULL,
      `O_SHIPPRIORITY` int NOT NULL,
      `O_COMMENT` varchar(79) NOT NULL,
      PRIMARY KEY (`O_ORDERKEY`),
      KEY (`O_CUSTKEY`)
      ......
    ) ENGINE=InnoDB

    如果把 o_orderkey 设计成上图所示的自增,那么很可能 o_orderkey 同为 1 的记录在不同的分片出现,如下图所示:

    所以,在分布式数据库架构下,尽量不要用自增作为表的主键,这也是我们在第一模块“表结构设计”中强调过的:自增性能很差、安全性不高、不适用于分布式架构。讲到这儿,我们已经说明白了“自增主键”的所有问题,那么该如何设计主键呢?依然还是用全局唯一的键作为主键,比如 MySQL 自动生成的有序 UUID;业务生成的全局唯一键(比如发号器);或者是开源的 UUID 生成算法,比如雪花算法(但是存在时间回溯的问题)。总之,用有序的全局唯一替代自增,是这个时代数据库主键的主流设计标准,如果你还停留在用自增做主键,或许代表你已经落后于时代发展了。

    4.2 索引设计

    通过分片键可以把 SQL 查询路由到指定的分片,但是在现实的生产环境中,业务还要通过其他的索引访问表。还是以前面的表 orders 为例,如果业务还要根据 o_orderkey 字段进行查询,比如查询订单 ID 为 1 的订单详情:

    SELECT * FROM orders WHERE o_orderkey = 1

    我们可以看到,由于分片规则不是分片键,所以需要查询 4 个分片才能得到最终的结果,如果下面有 1000 个分片,那么就需要执行 1000 次这样的 SQL,这时性能就比较差了。

    但是,我们知道 o_orderkey 是主键,应该只有一条返回记录,也就是说,o_orderkey 只存在于一个分片中。这时,可以有以下两种设计:

    • 同一份数据,表 orders 根据 o_orderkey 为分片键,再做一个分库分表的实现;
    • 在索引中额外添加分片键的信息。

    这两种设计的本质都是通过冗余实现空间换时间的效果,否则就需要扫描所有的分片,当分片数据非常多,效率就会变得极差。而第一种做法通过对表进行冗余,对于 o_orderkey 的查询,只需要在 o_orderkey = 1 的分片中直接查询就行,效率最高,但是设计的缺点又在于冗余数据量太大。所以,改进的做法之一是实现一个索引表,表中只包含 o_orderkey 和分片键 o_custkey,如:

    CREATE TABLE idx_orderkey_custkey (
      o_orderkey INT
      o_custkey INT,
      PRIMARY KEY (o_orderkey)
    )

    如果这张索引表很大,也可以将其分库分表,但是它的分片键是 o_orderkey,如果这时再根据字段 o_orderkey 进行查询,可以进行类似二级索引的回表实现:先通过查询索引表得到记录 o_orderkey = 1 对应的分片键 o_custkey 的值,接着再根据 o_custkey 进行查询,最终定位到想要的数据,如:

    SELECT * FROM orders WHERE o_orderkey = 1
    
    *************************************************************************
    
    # step 1
    
    SELECT o_custkey FROM idx_orderkey_custkey 
    
    WHERE o_orderkey = 1
    
    # step 2
    
    SELECT * FROM orders 
    
    WHERE o_custkey = ? AND o_orderkey = 1

    这个例子是将一条 SQL 语句拆分成 2 条 SQL 语句,但是拆分后的 2 条 SQL 都可以通过分片键进行查询,这样能保证只需要在单个分片中完成查询操作。不论有多少个分片,也只需要查询 2个分片的信息,这样 SQL 的查询性能可以得到极大的提升。

    通过索引表的方式,虽然存储上较冗余全表容量小了很多,但是要根据另一个分片键进行数据的存储,依然显得不够优雅。因此,最优的设计,不是创建一个索引表,而是将分片键的信息保存在想要查询的列中,这样通过查询的列就能直接知道所在的分片信息。

    如果我们将订单表 orders 的主键设计为一个字符串,这个字符串中最后一部分包含分片键的信息,如:

    o_orderkey = string(o_orderkey + o_custkey)

    那么这时如果根据 o_orderkey 进行查询:

    SELECT * FROM Orders
    
    WHERE o_orderkey = '1000-1';

    由于字段 o_orderkey 的设计中直接包含了分片键信息,所以我们可以直接知道这个订单在分片1 中,直接查询分片 1 就行。同样地,在插入时,由于可以知道插入时 o_custkey 对应的值,所以只要在业务层做一次字符的拼接,然后再插入数据库就行了。这样的实现方式较冗余表和索引表的设计来说,效率更高,查询可以提前知道数据对应的分片信息,只需 1 次查询就能获取想要的结果。

    这样实现的缺点是,主键值会变大一些,存储也会相应变大。只要主键值是有序的,插入的性能就不会变差。而通过在主键值中保存分片信息,却可以大大提升后续的查询效率,这样空间换时间的设计,总体上看是非常值得的。当然,这里我们谈的设计都是针对于唯一索引的设计,如果是非唯一的二级索引查询,那么非常可惜,依然需要扫描所有的分片才能得到最终的结果,如:

    SELECT * FROM Orders
    
    WHERE o_orderate >= ? o_orderdate < ?

    因此,再次提醒你,分布式数据库架构设计的要求是业务的绝大部分请求能够根据分片键定位到 1 个分片上。如果业务大部分请求都需要扫描所有分片信息才能获得最终结果,那么就不适合进行分布式架构的改造或设计。

    最后,我们再来回顾下淘宝用户订单表的设计:

    上图是我的淘宝订单信息,可以看到,订单号的最后 6 位都是 308113,所以可以大概率推测出:

    • 淘宝订单表的分片键是用户 ID;
    • 淘宝订单表,订单表的主键包含用户 ID,也就是分片信息。这样通过订单号进行查询,可以获得分片信息,从而查询 1 个分片就能得到最终的结果。

    4.3 全局表设计

    在分布式数据库中,有时会有一些无法提供分片键的表,但这些表又非常小,一般用于保存一些全局信息,平时更新也较少,绝大多数场景仅用于查询操作。例如 tpch 库中的表 nation,用于存储国家信息,但是在我们前面的 SQL 关联查询中,又经常会使用到这张表,对于这种全局表,可以在每个分片中存储,这样就不用跨分片地进行查询了。如下面的设计:

    4.4 唯一索引设计

    唯一索引的设计,与主键一样,如果只是通过数据库表本身唯一约束创建的索引,则无法保证在所有分片中都是唯一的。所以,在分布式数据库中,唯一索引一样要通过类似主键的 UUID 的机制实现,用全局唯一去替代局部唯一,但实际上,即便是单机的 MySQL 数据库架构,我们也推荐使用全局唯一的设计。因为你不知道,什么时候,你的业务就会升级到全局唯一的要求了。

    4.5 分布式数据库索引总结

    • 分布式数据库主键设计使用有序 UUID,全局唯一;
    • 分布式数据库唯一索引设计使用 UUID 的全局唯一设计,避免局部索引导致的唯一问题;
    • 分布式数据库唯一索引若不是分片键,则可以在设计时保存分片信息,这样查询直接路由到一个分片即可;
    • 对于分布式数据库中的全局表,可以采用冗余机制,在每个分片上进行保存。这样能避免查询时跨分片的查询。

    五、分布式数据库架构选型

    学习了分布式数据库的分片设计、表结构设计、索引设计等,相信你已经有能力构建一个分布式数据库系统了。但现在数据分好了,索引也设计好了,但是如果访问这些数据和索引呢?

    访问分布式数据库有两种模式:

    • 业务直接根据分库分表访问 MySQL 数据库节点;
    • 根据中间件访问。

    5.1 分库分表直接访问

    在设计分片时,我们已经明确了每张表的分片键信息,所以业务或服务可以直接根据分片键对应的数据库信息,直接访问底层的 MySQL 数据节点,比如在代码里可以做类似的处理:

    void InsertOrders(String orderKey, int userKey...) {
    
      int shard_id = userKey % 4;
    
      if (shard_id == 0) {
    
        conn = MySQLConncetion('shard1',...);
    
        conn.query(...);
    
      } else if (shard_id == 1) {
    
        conn = MySQLConncetion('shard2',...);
    
        conn.query(...);   
    
      } else if (shard_id == 2) {
    
        conn = MySQLConncetion('shard3',...);
    
        conn.query(...);   
    
      } else if (shard_id == 3) {
    
        conn = MySQLConncetion('shard4',...);
    
        conn.query(...);   
    
      }
    
    }

    从这段代码中我们可以看到,在业务代码中会嵌入分库分表的路由逻辑,在业务层计算出对应分片的信息,然后访问数据库:

    • 这种处理方式的好处是与单实例数据库没有太大的不同,只是多了一次计算分片的操作,没有额外的开销,性能非常好(我听说支付宝的分布式数据库为了最求极致的性能,用的就是直接访问分片的方式)。
    • 这种处理逻辑的缺点是业务需要知道分片信息,感知分片的变化。对于上面的例子,如果分片 shard1 发生变化,又或者进行了扩容,业务就需要跟着修改。

    为了解决这个缺点,比较好的处理方式是使用名字服务,而不要直接通过 IP 访问分片。这样当分片发生切换,又或者扩容缩容时,业务也不需要进行很大的改动。

    又因为业务比较多,需要访问分布式数据库分片逻辑的地方也比较多。所以,可以把分片信息存储在缓存中,当业务启动时,自动加载分片信息。比如,在 Memcached 或 Redis 中保存如下的分片信息,key 可以是分库分表的表名,value通过 JSON 或字典的方式存放分片信息:

    {
      'key': 'orders',
      'shard_info' : {
        'shard_key' : 'o_custkey',
        'shard_count' : 4,
        'shard_host' : ['shard1.xxx.com','shard2.xxx.com','...'],
        ‘shard_table' : ['tpch00/orders01','tpch01/orders02','...'],
      }
    }

    如果要进行跨分片的访问,则需要业务自己处理相关逻辑。不过我们前面已经说过,分布式数据库设计要求单元化,绝大部分操作需要在一个分片中完成。如果不能,那么可能都不推荐分布数据库的改造。总之,分库分表的直接访问方式,要求业务控制一切有关分布式数据库的操作,需要明确每个分片的具体信息,做好全流程的把控。

    5.2 使用中间件技术

    另一种比较流行的分布式数据库访问方式是通过分布式数据库中间件。数据库中间件本身模拟成一个 MySQL 数据库,通信协议也都遵循 MySQL 协议:业务之前怎么访问MySQL数据库的,就如何访问MySQL分布式数据库中间件。

    这样做的优点是:业务不用关注分布式数据库中的分片信息,把它默认为一个单机数据库使用就好了。这种模式也是大部分同学认为分布式数据库该有的样子,如下面的图:

    可以看到,通过分布式 MySQL 中间件,用户只需要访问中间件就行,下面的数据路由、分布式事务的实现等操作全部交由中间件完成。所以,分布式数据库中间件变成一个非常关键的核心组件。业界比较知名的 MySQL 分布式数据库中间件产品有:ShardingShpere、DBLE、TDSQL 等。

    • ShardingSphere于 2020 年 4 月 16 日成为 Apache 软件基金会的顶级项目、社区熟度、功能支持较多,特别是对于分布式事务的支持,有多种选择(ShardingSphere 官网地址)。
    • DBLE 是由知名 MySQL 服务商爱可生公司开源的 MySQL 中间件产品,已用于四大行核心业务,完美支撑传统银行去 IOE,转型分布式架构的探索。除了中间件技术外,爱可生公司还有很多关于 MySQL 数据库、分布式数据库设计等方面的综合经验。
    • TDSQL MySQL 版(TDSQL for MySQL)是腾讯打造的一款分布式数据库产品,具备强一致高可用、全球部署架构、分布式水平扩展、高性能、企业级安全等特性,同时提供智能 DBA、自动化运营、监控告警等配套设施,为客户提供完整的分布式数据库解决方案。

    目前 TDSQL 已经为超过500+的政企和金融机构提供数据库的公有云及私有云服务,客户覆盖银行、保险、证券、互联网金融、计费、第三方支付、物联网、互联网+、政务等领域。TDSQL MySQL 版亦凭借其高质量的产品及服务,获得了多项国际和国家认证,得到了客户及行业的一致认可。

    你要注意,使用数据库中间件虽好,但其存在一个明显的缺点,即多了一层中间层的访问,单个事务的访问耗时会有上升,对于性能敏感的业务来说,需要有这方面的意识和考虑。重要的一点是,虽然使用分布式数据库中间件后,单个事务的耗时会有所上升,但整体的吞吐率是不变的,通过增大并发数,可以有效提升分布式数据库的整体性能。

    5.3 分布式数据库架构选型

    那么,选择业务直连分布式数据库?还是通过数据库中间件访问?这是一个架构选型要考虑的问题。根据我的经验来说,对于较小业务(高峰期每秒事务不超过 1000 的业务),选择通过数据库中间件访问分布式数据库是比较优的方式。

    • 因为这样的业务通常处于爬升期,满足业务的各项功能或许是业务的主要目标。通过分布式中间件屏蔽下面的分片信息,可以让开发同学专注于业务的开发。
    • 另一方面,通过使用中间件提供的分布式事务就能满足简单的跨分片交易,解决分布式数据库中最难的问题。
    • 但如果你的业务是一个海量互联网业务,中间件的瓶颈就会显现,单个事务的耗时会上升,低并发下,性能会有一定下降。而且中间件提供的 2PC 分布式事务性能就更不能满足业务的需求了。所以类似支付宝、阿里这样的业务,并没有使用分布式数据库中间件的架构,而是采用了业务直连的模式。

    很多同学或许会问,如果不用数据库中间件,怎么解决 JOIN 这些问题呢?业务层去实现还是很麻烦的。的确,中间件可以完成这部分的功能。但如果真是数据量比较大,跨分片的场景,相信我,中间件也不能满足你的要求。所以,使用分布式数据库架构是一种折中,你要学会放弃很多,从而才能得到更多。

    六、全链路的条带化设计

    学习了分布式数据库架构的基本设计,完成了数据分片、表结构、索引的设计,相信学完这几讲之后,你已经基本了解分布式数据库了,也能够设计出一个分布式数据库的基础架构。但这些远远不够,因为当我们提到分布式架构时,除了数据库要完成分布式架构的改造,业务层也要完成分布式架构的改造,最终完成条带化的设计。

    6.1 什么是条带化

    条带化是存储的一种技术,将磁盘进行条带化后,可以把连续的数据分割成相同大小的数据块,简单的说,条带化就是把每段数据分别写入阵列中不同磁盘上的方法。可以看到,条带化的本质是通过将数据打散到多个磁盘,从而提升存储的整体性能,这与分布式数据库的分片理念是不是非常类似呢?下图显示了 RAID0 的条带化存储:

    从图中可以看到,进行 RAID 条带化后,数据存放在了三块磁盘上,分别是磁盘 1、磁盘 2、磁盘 3,存储的数据也进行了打散,分别存储在了条带 1、条带 2、条带 3 上。这样一来,当访问某一个数据的时候,可以并行地从 3 个磁盘上取出数据,写入也可以同时写入 3 个磁盘,提升了存储的性能。

    6.2 全链路的条带化设计

    分布式数据库的本质是:将数据根据某个或几个列(称之为“分片键”),然后依据预先设定的算法(分片算法)进行打散,形成一个个的分片。更重要的是,分布式数据库中的表,要能选出一个统一的分片键,即大部分表都能根据这个分片键打散数据,这样当后续业务进行访问数据时,可以在一个分片中完成单元化的闭环操作,不用涉及跨分片的访问。下图显示了对于 tpch 分布式架构改造后的分片效果:

    从图中我们可以看到,这与我们之前所提倡的条带化的思想比较类似,即数据打散,性能得到提升,对于分布式数据库来说,分片越多,性能上限也就越高。

    但是,这只是对数据库层做了条带化,没有站在全链路的角度上进行条带化设计。我们来看一个例子,假设是电商中比较重要的订单服务,并且对表 orders 进行了分布式的条带化设计:

    可以看到,订单服务可以根据字段 o_custkey 访问不同分片的数据,这也是大部分业务会进行的设计(由于服务层通常是无状态的,因此这里不考虑高可用的情况)。但是,这样的设计不符合全链路的条带化设计思想。全链路的设计思想,要将上层服务也作为条带的一部分进行处理,也就是说,订单服务也要跟着分片进行分布式架构的改造。所以,如果进行全链路的条带化设计,那么上面的订单服务应该设计成:

    可以看到,如果要进行分布式的条带化设计时,上层业务服务也需要进行相应的分布式改造,将1个“大”订单服务层也拆分成多个“小”订单服务,其中每个订单服务访问自己分片的数据。

    这样设计的好处在于:

    • 安全性更好,每个服务可以校验访问用户是否本分片数据;
    • 上层服务跟着数据分片进行条带化部署,业务性能更好;
    • 上层服务跟着数据分片进行条带化部署,可用性更好;

    第1点通常比较好理解,但是 2、3点 就不怎么好理解了。为什么性能也会更好呢?这里请你考虑一下业务的部署情况,也就是,经常听说的多活架构设计。

    6.3 多活架构

    对于高可用的架构设计要做到跨机房部署,实现的方式是无损半同复制,以及最新的 MySQL Group Rreplication 技术。数据库实例通过三园区进行部署。这样,当一个机房发生宕机,可以快速切换到另一个机房。我们再来回顾下三园区的架构设计:

    图中显示了通过无损半同步复制方式进行的三园区高可用架构设计,从而实现同城跨机房的切换能力。但这只是单实例 MySQL 数据库架构,如果到分布式架构呢?所有分片都是在一个机房吗?

    如果所有分片都在一个机房,你会发现,这时机房 2、机房3 中的数据库都只是从机,只能进行读取操作,而无法实现写入操作,这就是我们说的单活架构。与单活架构不同,多活架构是指不同地理位置上的系统,都能够提供业务读/写服务。这里的“活”是指实时提供读/写服务的意思,而不仅仅只是读服务。多活架构主要是为了提升系统的容灾能力,提高系统的可用性,保障业务持续可用。

    要实现多活架构,首先要进行分布式数据库的改造,然后是将不同数据分片的主服务器放到不同机房,最后是实现业务条带化的部署。如下面的这张图:

    可以看到,对于上一节的订单服务和订单数据分片,通过将其部署在不同的机房,使得订单服务1 部署在机房 1,可以对分片1进行读写;订单服务 2 部署在机房 1,可以对分片 2 进行读写;订单服务 3 部署在机房 3,可以对分片 3 进行读写。这样每个机房都可以有写入流量,每个机房都是“活”的,这就是多活架构设计。若一个机房发生宕机,如机房 1 宕机,则切换到另一个机房,上层服务和数据库跟着一起切换,切换后上层服务和数据库依然还是在一个机房,访问不用跨机房访问,依然能提供最好的性能和可用性保障。

    七、分布式事务解决方案

    学习了分布式数据库中数据的分片设计、索引设计、中间件选型,全链路的条带化设计分布式数据库中最令人头疼的问题,那就是分布式事务。详细的分布式事务解决方案在:高并发项目设计——分布式事务解决方案_庄小焱-CSDN博客

    7.1 分布式事务概念

    事务的概念相信你已经非常熟悉了,事务就是要满足 ACID 的特性,总结来说。

    • A(Atomicity) 原子性:事务内的操作,要么都做,要么都不做;
    • C(Consistency) 一致性:事务开始之前和事务结束以后,数据的完整性没有被破坏;如唯一性约束,外键约束等;
    • I(Isolation)隔离性:一个事务所做的操作对另一个事务不可见,好似是串行执行;
    • D(Durability)持久性:事务提交后,数据的修改是永久的。即使发生宕机,数据也能修复;

    特别需要注意的是,当前数据库的默认事务隔离级别都没有达到隔离性的要求,MySQL、Oracle、PostgreSQL等关系型数据库都是如此。大多数数据库事务隔离级别都默认设置为 READ-COMMITTED,这种事务隔离级别没有解决可重复度和幻读问题(除了Mysql以外,mysql已经在可重复读中解决了数据的幻读问题)。

    但由于在绝大部分业务中,都不会遇到这两种情况。若要达到完全隔离性的要求,性能往往又会比较低。因此在性能和绝对的隔离性前,大多数关系型数据库选择了一种折中。那什么是分布式事务呢?简单来说,就是要在分布式数据库的架构下实现事务的ACID特性。

    分布式数据库架构设计的一个原则,即大部分的操作要能单元化。即在一个分片中完成。如对用户订单明细的查询,由于分片键都是客户ID,因此可以在一个分片中完成。那么他能满足事务的ACID特性。

    但是,如果是下面的一个电商核心业务逻辑,那就无法实现在一个分片中完成,即用户购买商品,其大致逻辑如下所示:

    START TRANSATION;
    
    INSERT INTO orders VALUES (......);
    
    INSERT INTO lineitem VALUES (......);
    
    UPDATE STOCK SET COUNT = COUNT - 1 WHERE sku_id = ?
    
    COMMIT;

    可以看到,在分布式数据库架构下,表orders、linitem的分片键是用户ID。但是表stock是库存品,是商品维度的数据,没有用户ID的信息。因此stock的分片规则肯定与表orders和lineitem不同。所以,上述的事务操作大部分情况下并不能在一个分片中完成单元化,因此就是一个分布式事务,它要求用户维度的表 orders、lineitem 和商品维度的表 stock 的变更,要么都完成,要么都完成不了。常见的分布式事务的实现就是通过 2PC(two phase commit 两阶段提交)实现,接着我们来看下 2PC。

    7.2 2PC的分布式事务实现

    2PC 是数据库层面实现分布式事务的一种强一致性实现。在 2PC 中,引入事务协调者的角色用于协调管理各参与者(也可称之为各本地资源)的提交和回滚。而 2PC 所谓的两阶段是指parepare(准备)阶段和 commit(提交)两个阶段。在 2PC 的实现中,参与者就是分钟的 MySQL 数据库实例,那事务协调者是谁呢?这取决于分布式数据库的架构。若分布式数据库的架构采用业务通过分库分表规则直连分片的话,那么事务协调者就是业务程序本身。如下图所示:

     若采用数据库中间件的模式,那么事务协调者就是数据库中间件。如下图所示:

    从上图可以发现,使用分布式数据库中间件后,可以对上层服务屏蔽分布式事务的实现,服务不需要关心下层的事务是本地事务还是分布式事务,就好像是单机事务本身一样。2PC 要求第一段 prepare 的操作都成功,那么分布式事务才能提交,这样最终能够实现持久化,2PC 的代码逻辑如下所示:

    上面就是 2PC 的 Java 代码实现,可以看到只有2个参与者第一阶段 prepare 都成功,那么分布式事务才能提交。但是 2PC 的一个难点在于 prepare 都成功了,但是在进行第二阶段 commit 的时候,其中一个节点挂了。这时挂掉的那个节点在恢复后,或进行主从切换后,节点上之前执行成功的prepare 事务需要人为的接入处理,这个事务就称之为悬挂事务。

    用户可以通过命令 XA_RECOVER 查看节点上事务有悬挂事务:

    如果有悬挂事务,则这个事务持有的锁资源都是没有释放的。可以通过命令SHOW ENGINE INNODB STATUS 进行查看:

    从上图可以看到,事务 5136 处于 PREPARE状态,已经有 218 秒了,这就是一个悬挂事务,并且这个事务只有了两个行锁对象。

    可以通过命令 XA RECOVER 人工的进行提交:

    同学们应该都了了分布式事务的 2PC 实现和使用方法。它是一种由数据库层实现强一致事务解决方案。其优点是使用简单,当前大部分的语言都支持 2PC 的实现。若使用中间件,业务完全就不用关心事务是不是分布式的。

    然而,他的缺点是,事务的提交开销变大了,从 1 次 COMMIT 变成了两次 PREPARE 和COMMIT。而对于海量的互联网业务来说,2PC 的性能是无法接受。因此,这就有了业务级的分布式事务实现,即柔性事务。

    7.3 柔性事务

    柔性事务是指分布式事务由业务层实现,通过最终一致性完成分布式事务的工作。可以说,通过牺牲了一定的一致性,达到了分布式事务的性能要求。业界常见的柔性事务有 TCC、SAGA、SEATA 这样的框架、也可以通过消息表实现。它们实现原理本身就是通过补偿机制,实现最终的一致性。柔性事务的难点就在于对于错误逻辑的处理。

    为了讲述简单,这里用消息表作为柔性事务的案例分享。对于上述电商的核心电商下单逻辑,用消息表就拆分为 3 个阶段:

    阶段1:
    
    START TRANSACTION;
    
    # 订单号,订单状态
    
    INSERT INTO orders VALUES (...) 
    
    INSERT INTO lineitem VALUES (...)
    
    COMMIT;
    
    **********************************************************************
    
    阶段2:
    
    START TRANSACTION;
    
    UPDATE stock SET count = count -1 WHERE sku_id = ?
    
    # o_orderkey是消息表中的主键,具有唯一约束
    
    INSERT INTO stock_message VALUES (o_orderkey, ... )  
    
    COMMIT;
    
    **********************************************************************
    
    阶段3:
    
    UPDATE orders SET o_orderststus = 'F' WHERE o_orderkey = ?
    • 上面的柔性事务中,订单表中的列 o_orderstatus 用于记录柔性事务是否完成,初始状态都是未完成。表 stock_message 记录对应订单是否已经扣除过相应的库存。若阶段 2 完成,则柔性事务必须完成。阶段 3 就是将柔性事务设置为完成,最终一致性的确定。
    • 接着我们来下,若阶段 2 执行失败,即执行过程中节点发生了宕机。则后台的补偿逻辑回去扫描订单表中 o_orderstatus 为未完成的超时订单有哪些,然后看一下是否在对应的表stock_message 有记录,若有,则执行阶段 3。若无,可选择告知用户下单失败。
    • 若阶段 3 执行失败,处理逻辑与阶段 2 基本一致,只是这时 2 肯定是完成的,只需要接着执行阶段 3 即可。

    所以,这里的补偿逻辑程序就是实时/定期扫描超时订单,通过消息表判断这个柔性事务是继续执行还是执行失败,执行失败又要做哪些业务处理。上面介绍的框架实现的柔性事务原理大致如此,只不过对于补偿的逻辑处理有些不同,又或者使用上更为通用一些。对于海量的互联网业务来说,柔性事务性能更好,因此支付宝、淘宝等互联网业务都是使用柔性事务完成分布式事务的实现。

    博文参考

    展开全文
  • 例如,两张表只有不同 的架构下才能取相同的名字,也就是说,同一个架构下不能有两张名字一样的表。我 们可以把架构看作对象的容器(在数据库工具的上下文中,架构也是指描述架构和数据 库中对象的目录信息。SQL...
  • 数据库的三层架构

    千次阅读 2021-01-20 01:03:38
    三层模式体系结构和数据独立性第1.3节...这一节中,我们将为数据库系统指定一个体系结构,这称为三层模式体系结构(three-schema architecture)⑧,提出这个体系结构的目的在于帮助实现这些特点,并使这些特点更为...

    三层模式体系结构和数据独立性

    第1.3节中列出了数据库方法的4个重要特点,其中有以下3个特点:(1)程序与数据分离(程序-数据独立性和程序-操作独立性);(2)支持多用户视图;(3)使用编目存储数据库描述(模式)。在这一节中,我们将为数据库系统指定一个体系结构,这称为三层模式体系结构(three-schema architecture)⑧,提出这个体系结构的目的在于帮助实现这些特点,并使这些特点更为直观。在此之后将进一步讨论数据独立性的概念。

    2.2.1  三层模式体系结构

    如图2.2所示,三层模式体系结构的目的是将用户应用与物理数据库分离开来。在这个体系结构中,可以将模式定义在如下3个层次:

    1. 内部层(internal level)有一个内模式(internal schema),它描述的是数据库的物理存储结构。内模式使用一个物理数据模型,全面描述了数据库中数据存储的全部细节和存取路径。

    2. 概念层(conceptual level)有一个概念模式(conceptual schema),它为用户群体描述了整个数据库的结构。概念模式隐藏了物理存储结构的细节,并重点描述实体、数据类型、关系、用户操作和约束。通常,实现了一个数据库系统之后,会使用一个表示数据模型来描述概念模式。这种实现概念模式通常以高级数据模型中的一个概念模式设计为基础。

    3. 外部层(external level)或视图层(view level)包括许多外模式(external schema)或用户视图(user view)。每个外模式描述的是一个特定用户组所感兴趣的那部分数据库,而对该用户组隐藏了数据库的其他部分。与第2点中介绍的一样,各个外模式通常都用一个表示数据模型实现,并且可能以高级数据模型的一个外模式设计为基础。

    0e34442ea06b0e3682e397afe8a58f2e.png

    图2.2 三层模式体系结构

    三层模式体系结构是一个非常方便的工具,通过这个结构,用户就可以直观地了解数据库系统中的模式层次。大多数DBMS并不是将这三层模式完全分离开来,而只是在一定程度上支持三层模式体系结构。有些DBMS可能在概念模式中还包括一些物理层的细节。在大多数支持用户视图的DBMS中,外模式也是用描述概念层信息的同一数据模型来指定的。当然,有些DBMS也允许在概念层和外部层使用不同的数据模型。

    请注意,这3个模式只是数据的描述,而实际存在的数据都在物理层中。在一个基于三层模式体系结构的DBMS中,每个用户组只引用其自己的外模式。因此,DBMS必须将指定给外模式的请求转换为一个面向概念模式的请求,然后再转换为一个内模式上的请求,以处理存储数据库。如果一个请求要求检索数据库,那么对从存储数据库中抽取出来的数据必须重新格式化,以便与用户外部视图相匹配。在各层间完成请求和结果转换的过程称为映射(mapping)。映射可能很耗时,所以有些DBMS(特别是那些用于支持小型数据库的DBMS)并不支持外部视图。但即便是在这样的系统中,还是需要一定的映射来在概念层和内部层间转换请求。

    展开全文
  • OceanBase的定义;OceanBase的发展历程;互联网对传统关系型数据库的挑战可扩展性;互联网对传统关系型数据库的挑战可扩展性;互联网对传统关系型数据库的挑战可靠性;OceanBase的目标;OceanBase主要面临的问题;交易库每...
  • 微服务架构下数据库拆分

    千次阅读 2021-08-26 09:31:46
    微服务架构下,我们将一个大型系统分为三部分:容器、发布和测试是独立的,但原始数据库仍然是一个(如下图)。现在我们需要拆分数据库三个系统A、B、C拥有各自的数据库后,我们的微服务最终才可以的部署、...
  • 用生动的PPT动画演示和配套总结性文字,讲述分布式事务数据库的教科书式技术架构、三种主流金融行业技术架构、金标委定义技术架构、行业专业术语,及重点围绕分布式事务数据库的核心技术之分布式事务的原理、功能和...
  • 数据库实验报告1数据库定义实验

    千次阅读 2020-04-29 11:18:58
    (1)理解和掌握数据库DDL语言,能熟练地使用SQL DDL语句创建、修改和删除数据库、模式和基本表。 (2)掌握SQL语句常见语法错误的调试方法。 二、 实验内容: 教材3.3数据定义中例3.1至例3.11的要求操作,并截取...
  • 系统架构设计-数据库系统知识点

    千次阅读 2021-07-04 13:53:03
    概念模式(模式、逻辑模式)用以描述整个数据库数据库的逻辑结构,描述现实世界中的实体及其性质与联系,定义记录、数据项、数据的完整性约束条件及记录之间的联系,是数据项值的框架。 数据库系统概念模式通常还...
  • Snowflake数据库调研及架构介绍

    千次阅读 2020-11-03 20:46:07
    简介 ...Snowflake的数据仓库不是建立现有数据库或Hadoop等“大数据”软件平台上的,Snowflake数据仓库使用新的SQL数据库引擎,该引擎具有为云设计的独特架构。对于用户而言,Snowflake与其他企业数
  • 数据库的基本架构

    千次阅读 2021-12-14 17:31:26
    定义:长期存储计算机内,有组织,可共享的数据集合。 数据库中数据的特征:较小的冗余度,较高的数据独立性;易扩展性;为各个用户所共享。 冗余:重复,多余的意思 扩展性:指的是可修改的 3.数据库管理系统...
  • 全面介绍了如何设计和管理安全的...本书适合数据库管理员,也适合需要设计安全数据库解决方案、定义高可用性解决方案、巩固数据库架构、计划并设计数据库部署、设计备份和恢复策略、或需要优化数据库数据库专家。
  • 【摘要】 本文提出了一种通过引入内存数据库层,建立两层多分区分布式数据库架构。此方案用于解决海量高并发系统的数据存储和访问问题,尤其适用于电子商务等数据模型复杂且业务复杂的互联网站。 这些年互联网站...
  • 系统架构师(3)数据库技术

    千次阅读 2021-08-09 11:45:41
    数据库(统一管理、长期存储计算机内的,有组织的相关数据的集合) 硬件(构成计算机系统包括存储数据所需的外部设备) 软件(操作系统、数据库管理系统及应用程序) 人员(系统分析和数据库设计人员、应用程序员、最终...
  • 本文为大家介绍了云溪数据库基于 RocksDB 的存储引擎架构,以及云溪数据库团队针对 RocksDB 的局限性,通过开发软硬件结合的 SPDK+ZNS SSD 存储引擎、大内存场景的准内存引擎,大大提高了存储引擎的读写性能。
  • PostgreSQL的架构

    2021-02-23 22:25:15
    PostgreSQL的物理架构非常简单,它由共享内存、一系列后台进程和数据文件组成。(如图)共享内存是服务器服务器为数据库缓存和事务日志缓存预留的内存缓存...存储WALBuffer中的内容会根据提前定义好的时间点参数要
  • oracle数据库体系架构详解

    万次阅读 多人点赞 2018-08-31 19:10:41
    学习oracle中,体系结构是重中之重,一开始从宏观上掌握它...你首先应该以图纸的方式把整个大楼的体系架构描述出来。然后一点点的往里面填充东西。下面我们先以一个图解的方式对oracle体系结构有一个基本了解   ...
  • 实时数据库 架构

    千次阅读 2016-01-22 09:44:06
    数据服务器、Web服务器、设备驱动、人机界面、实用工具可任意组合,分别运行相同或不同机器上。实时数据,历史数据,过程报警,事件等作为整个系统的共有资源,可为其他客户端和数据库共享。     紫金...
  • 高性能架构-数据库分库分表-mycat

    千次阅读 2020-12-17 15:53:15
    数据库分库分表后,需要对数据库进行处理,使得我们应用层的编程DAO操作,能像正常单一数据源一样,不影响应用DAO层的业务逻辑。已有很方便的框架帮我们处理此事,比较好用的有mycat和shardingsphere.本文介绍...
  • (1)DDL(Data Definition Language,数据定义语⾔) 用于完成对数据库对象(数据库数据表、视图、索引等)的创建、删除、修改 (2)DML(Data Manipulation Language,数据操作/操纵语⾔) ⽤于完成对数据表中...
  • 1.一个数据库用户可以对应多个架构架构是表容器)。架构里面包含的是数据库表。 2.一个数据库角色有可能涉及多个架构数据库角色对应的是权限。 3.一个用户对应一个数据库角色。 4.登录名与数据库用户服务器...
  • sql数据库内容替换(sql数据中马的朋友请用,输入中马字符,全库替换)
  • MySQL分布式事务数据库金融级灾备双活的指标要求与技术架构 金标委定义的 分布式数据库技术金融标准的数据容灾 金标委定义的分布式事务数据库灾备能力等级划分 金融级分布式事务数据库灾难恢复能力应至少达到GB/T ...
  • 微服务架构概述与应用 开发科:史超吕格利 微服务架构概述 微服务封装 点击此处添加节标题 微服务应用 微服务架构概述 传统架构的问题 负载均衡( Load balancer) WEB UI WEB UI 模块A 模块B 模块C 模块B 模块C 数据库 ...
  • 嵌入式SQLite数据库架构和设计

    千次阅读 2017-05-30 20:19:22
    SQLite既是一个数据库,一个程序库,一个命令行工具,也是一个学习关系型数据库的很好的工具。确实有很多途径可以把它使用到内嵌环境、网站、操作系统服务、脚本语言和应用程序。对于程序员来说,SQLite就像一个数据...
  • -DBA 数据库管理员 JAVA 程序员架构师必看 数据库基础(面试常见题) 一 数 据 库基础 数据抽象物理抽象概念抽象视图级抽象,内模式模式外模式 SQL 语言包括数据定义数据操纵(Data Manipulation,数据控制(Data Control)...
  • 非集群数据库架构中,数据库数据库实例存在一 一对应关系,在数据库集群中,可能存在多个数据库实例操作一个数据库情况,即多对一关系。 二 MySQL基架 对于MySQL来说,虽然经历了多个版本迭代(MySQL5.5,MySQL ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 382,489
精华内容 152,995
热门标签
关键字:

在数据库下如何定义架构