数据库优化-多机优化
课程内容
-
集群与分布式概念理解
-
CAP&Base理论
-
Mysql主从复制-集群
==================
-
分库分表理论
-
Sharding-JDBC读写分离
-
Sharding-JDBC分库分表
一.CAP&Base理论
1.集群和分布式
1.1.什么是分布式系统-拆子系统
简单理解分布式就是一个计算机软件系统有多个子系统组成,每个子系统承担着部分职责,分别部署在不同的服务器上,子系统之间通过网络进行交互,共同完成完整的工作,缺一不可。 - 拆分、组合
理解1:微服务架构天生是分布式系统 一般一个微服务对应一个数据库,也就是数据库也是分布式
1.2.什么是集群-子系统集群
简单理解集群就是把一个计算机软件系统,部署成多份(复制)来提供工作能力,多个软件系统分别部署在不同的服务器上,每个服务器我们称之为节点,服务器之间无需通信,每个软件系统(节点)都是完整的功能,部分节点挂掉(down),还有其他节点处理请求。- 复制**、减压**
同一个子系统部署多份,减轻负载压力。 微服务中集群: 直接部署多个nacos, gateway(外置nginx负载期),某微服务直接部署多份,用户数据库进行集群
2.CAP理论
2.1.什么是CAP
-
一致性(Consistency)
可以理解为分布式中存在多个数据副本,当其中某个数据副本发生数据更新需要实时同步到其他节点上。多个节点数据总是保持一致。例如:同一个数据同时存在于A服务器和B服务器,那么当A服务器的数据发生改变,B服务器的数据始终保持同步。
-
可用性(Availability)
客户端在任何时候访问集群,请求都可以正常返回结果,可以允许有一定的延迟。-不能上锁要能支持随时能用。
-
分区容错性(Partition tolerance)-部署多个节点
能够容忍当服务之间通信发生故障,整个集群被分割为多个无法相互通信的分区的情况。即当集群因为网络故障被划分为多个分区,集群仍然可用。 —重试+集群(放到不同的分区更好)
2.2.常见CAP组合
定理:任何分布式系统只可同时满足二点,没法三者兼顾。
忠告:架构师不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。
1、满足CA舍弃P,也就是满足一致性和可用性,舍弃容错性。但是这也就意味着你的系统不是分布式的了,因为涉及分布式的想法就是把功能分开,部署到不同的机器上。单个mysql服务器
2、满足CP舍弃A,也就是满足一致性和容错性,舍弃可用性。如果你的系统允许有段时间的访问失效等问题,这个是可以满足的。就好比多个人并发买票,后台网络出现故障,你买的时候系统就崩溃了。 redis.set(key,value) 当我在进行操作的时候其他的要进行等待或者超时异常:如 Redis zookeeper consol nacos
(默认ap,可以修改cp,可配置)
3、满足AP舍弃C,也就是满足可用性和容错性,舍弃一致性。这也就是意味着你的系统在并发访问的时候可能会出现数据不一致的情况。 Mysql主从同步 eureaka,
nacos默认实现也是ap
因为网络不可控问题,分区容错总是存在的,所以对于一个分布式系统来说,必须具备分区容错性,即需要在AP和CP做选择。 一般选**ap,**但是要使用最终一致性这种弱一致性
3.Base理论
3.1.强一致性和最终一致性
CAP理论
告诉我们一个分布式系统最多只能同时满足一致性( Consistency)、可用性( Availability )和分区容忍性( Partition tolerance )这三项中的两项,其中AP在实际应用中较多, AP即舍弃一致性,保证可用性和分区容忍性,但是在实际生产中很多场景都要实现最终一致性
,比如前边我们举的例子主节点向从节点同步数据,即使不要一致性,但是最终也要将数据同步成功来保证数据一致,这种一致性和CAP中的一致性不同, CAP中的一致性要求在任何时间查询每个结点数据都必须一致,它强调的是强一致性,但是最终一致性是允许可以在一段时间内每个结点的数据不一致,但是经过一段时间每个结点的数据必须一致,它强调的是最终一致性
。
1、强一致性
对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。比如小明更新V0到V1,那么小华读取的时候也应该是V1。
2、弱一致性
如果能容忍后续的部分或者全部访问不到,则是弱一致性。比如小明更新VO到V1,可以容忍那么小华读取的时候是V0。
3、最终一致性
如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。比如小明更新VO到V1,可以使得小华在一段时间之后读取的时候是V1。
分布式系统通常为了保证系统的可用性,牺牲强一致性,采用最终一致性。
3.2.Base理论
BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent (最终一致性)三个短语的缩写。BASE理论是对CAP中AP的一个扩展,通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态
。满足BASE理论的事务,我们称之为"柔性事务"。
1、基本可用
分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如,电商网站交易付款出现问题了,商品依然可以正常浏览。
2、软状态
由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态) ,这个状态不影响系统可用性,如订单的"支付中"、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。
3、最终一致
最终一致是指经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,最终会变为“支付成功”或者"支付失败" ,使订单状态与实际交易结果达成-致,但需要一定时间的延迟等待。
操作我时其他可用,但是有中间状态,并且要最终一致性。p一定要满足,c和a二选1,ap用得最多,但是我们还要使用base理论来完成最终一致性
二.Mysql集群
1.Mysql集群概述
1.1.为什么要做Mysql集群
1、提高并发能力
2、提高可用性,防止单点故障
3、防止数据丢失
1.2.集群方案
集群:多个服务器一起对外提供一个服务
Mysql集群有N种方式但是整体来看,分为主从,多主,我们这里选择主从模式,因为根据2/8原则,数据库的性能瓶颈往往都在80%的读操作上,我们可以搞多个从需要减轻读的压力
建议采纳: 双主多从
1.3.什么是Mysql主从? ap系统
一个主(Master)多从(Slave),主负责写操作,从负责读操作,从库的数据从主库同步复制,这样的集群模式就主从同步 。主从复制MySql自己就能完成,我们需要做一些配置即可。
主从同步的优点是减轻读的压力,如果主库的写并发比较高或者为了解决主库单点故障,可用做成多个主库,多个主库相互复制,这样即提高了主库写的并发能力,也解决了单节点故障问题。
做了主从同步,我们的应用就需要做读写分离,写请求访问主库,读请求访问/从库,可以借助一些框架来实现读写分离,如:msyqlproxy , mycat ,ShardingJdbc ,如下图:
主从同步(mysql自己实现),读写分离(依赖三方组件,mysqlproxy,mycat(单独服务),shardingjdbc(jar))
2.Mysql主从搭建
2.1.Mysql主从复制原理-ap系统
主从复制是通过重放binlog实现主库数据的异步复制。即当主库执行了一条sql命令,那么在从库同样的执行一遍,从而达到主从复制的效果
主从复制步骤:
-
将Master的binary-log日志文件打开,mysql会把所有的DDL,DML,TCL写入BinaryLog日志文件中
-
Master会生成一个 log dump 线程,用来给从库的 i/o线程传binlog
-
从库的i/o线程去请求主库的binlog,并将得到的binlog日志写到中继日志(relaylog)中
-
从库的sql线程,会读取relaylog文件中的日志,并解析成具体操作,通过主从的操作一致,而达到最终数据一致
2.2.准备环境
绿色版安装参考它!---------------使用这种方案
https://blog.csdn.net/u014427391/article/details/122144676
至少需要两台服务器,以后肯定是独立两台电脑,当然也可以使用虚拟机. 教学的时候**,可以安装多个服务(使用不同的端口)来代替服务器.**
1、分别构造主、从数据库并输出日志(方便定位问题)
-
关闭MySql服务
-
拷贝mysql安装目录,重命名:master
-
修改新的Mysql的my.ini配置:端口,日志目录,安装目录,数据目录
- 拷贝数据:去老的MySql的数据目录拷贝 data\mysql 和 data\performance_schema 到新的MySql的数据目录,这个是MySql的初始化数据。在my.ini中datadir就是数据目录第位置
2、安装及启动
-
修改master中的my.ini 路径改为master的路径
-
执行命令安装新的Mysql的服务 ,进入bin目录,cmd执行
mysqld --install master --defaults-file="C:\Program Files (x86)\MySQL\master\my.ini"
(install/remove of the service denied 权限不足 以管理身份运行cmd.exe)
2.3.master服务器配置
- 修改master方的my.ini,在[mysqld]下
log-bin=mysql-bin
server-id=1
innodb_flush_log_at_trx_commit=1
sync_binlog=1
binlog_ignore_db=mysql
binlog_checksum=none
- 重启master服务,登录
- 授权savle服务器的使用的账号及权限
Mysql> GRANT REPLICATION SLAVE ON *.* to 'yaosang'@'192.168.6.4'identified by '123456';
参数说明:
- lvtest:slave连接master使用的账号
- IDENTIFIED BY ‘admin’ :slave连接master使用的密码
- 192.168.77.128:slave IP
- 查询主数据库状态
Mysql> show master status;
记录 File 和 Position的值,File对应的是binlog文件 , position指的是当前slave同步数据的最新行。在slave端会使用这两个信息。
2.4.slave配置
1、修改slave服务器的配置文件my.ini将 server-id = 1修改为 server-id = 10,并确保这个ID没有被别的MySQL服务所使用。
#log-bin=mysql-bin
server-id=20
2、启动slave服务器,登录
3、在slave端,配置master链接信息 (执行语句)
Mysql> change master to
master_host='172.16.6.254', #master IP
master_user='slaveuser', #master数据库通过GRANT授权的账号
master_password='123456', #master数据库通过GRANT授权的密码
master_port=3307, #master数据库的端口
master_log_file='mysql-bin.000001',
#master数据库中通过show master status显示的File名称
master_log_pos=269
#master数据库的通过show master status显示的Position的值
4、启动同步
Mysql> start slave;
5、主从同步检查
show slave status;
其中Slave_IO_Running 与 Slave_SQL_Running 的值都必须为YES,才表明状态正常。
6、 测试
-
在master上,建库、建表、添加数据
-
刷新slave库,记录也存在
由此,整个MySQL主从复制的过程就完成了,接下来,我们进行MySQL读写分离的安装与配置。
三.分库分表实战
1.垂直分库分表-设计上的
1.1.垂直分表-宽表拆分
垂直分表可以理解成按列分表,比如一个用户表中包括了 用户登录相关信息,用户基本资料,用户账户信息等等信息,这个表字段太多变得非常庞大,查询的时候必定会有性能影响。
t_user
再加上我们在查询的时候不会把用户的所有数据都要查出来,做登录时只需要登录相关字段,查询个人资料时就只需要基本信息,加载账户的时候只需要账户相关的字段,但是所有字段都在一个表浪费了查询的性能。针对于这种情况我们可以按照业务维度进行垂直分表,每个表有不同的列,如:
t_login
t_user_info
t_account
上面的分表的方式就是垂直分表 , 多个包之间外键关联,如果表关系是一对一,使用相同的ID也可以。
把一些大字段单独设置出来!平时不用查询,如果硬是要查使用跨表查询
1.2.垂直分库-按业务分库
一个mysql服务,要安装在一台服务器上面,这台服务器资源(内存,cpu等)是有限的.当数据库的并发请求非常高。
一个mysql数据处理不了就可以考虑使用多个mysql服务来分担压力,垂直分库就是把一个数据库中的N张表,按照模块/业务 划分到多个Mysql数据库,每个数据库都有自己的服务器,进行分布式部署。
[注意
]微服务架构:天然就是分库. 它要求各自服务都要有自己数据库.
2.水平分库分表-海量表拆分小表
2.1.水平分表
水平分表可以理解成按行分表,一个表中的数据量一千万行,查询注定慢,我们可以把这个表中的数据拆分成10个小表,每个表一百万行数据,每个小表拥有相同的列,如:
水平分表会带来很多的问题?比如按什么规则来分表,场景的分表规则有:
- 按区间范围分表
一般在有严格的自增id需求上,如按照user_id水平分表:
-
table_1 user_id从1~100w
-
table_2 user_id从100W+1~200w
-
table_3 user_id从200W+1~300w
实现方式有:
- 新创建一个表,该表的自增ID作为分表的ID(性能差)
- 使用Redis的自增incr 命令
- 使用UUID(比较low)
- 雪花算法
- 按时间分表
比如一个月分一个表,那么一年也就分12张表 ,这种方式适用于时效性很强的场景,比如登录日志,一般都是看最近一个月的日志,很少去看一月前的,或者一年前的。甚至更老旧的数据可以直接删掉。
- Hash分表
通过ID或者名称通过一定的hash算法计算出数据存储表的表名然后访问相应的表,这种方法比较通用
比如:准备好100个表 : t_course_01 , t_course_02, … ,t_course_100
然后计算:t_course_hash(商品名+id) % 100 +1 ,余数就对应表名
- 雪花算法
Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割 成多个部分,每个部分代表不同的含义(41位时间戳,10位机器号,12位序列号)。而 Java中64bit的整数是Long类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 long 来存储的
分布式id生成方案 : https://blog.csdn.net/charlesyoosky/article/details/95164847
2.2.水平分库
即使做了水平分表,表太多也会影响数据库的效率,水平分库指定是把一个数据库中的表分到多个数据库中,数据库采用分布式部署,比如电商平台针对于商品库我们可以按照商户来分,如下:
最NB方案:垂直分库 ;水平分库 ;水平分表 ; 垂直分表 ;集群(主从同步&读写分离)
分库分表操作思路:
1 按照业务进行垂直分库 商品和店铺隔离开来 微服务架构都要这样干
2 把商品业务进行水平分库 多个商品库服务器
3 可以对水平分库后每一个数据库服务器进行集群. 多个商品库服务器群
4 进行垂直分表 把多字段表拆分少量字段表. 每一个库里面宽表进行拆分
5 进行水平分表 把海量数据表拆分为多个小表 把每一个大表进行拆分
数据库优化顺序 :
1)垂直分库 ,垂直分表,冗余设计(反第三范式),存储引擎,索引,SQL优化 ,缓存(热点查询),全文检索(搜索场景),页面静态化
分布式项目可以直接考虑垂直分库
2)主从(读压力大)-集群-高并发
================
3)水平分表
4)水平分库
先垂直+集群+优化技术能搞定就不要水平操作,因为操作比较复杂,要一定的分库分表策略. 实在满足不了,可以是使用水平操作.
项目100W???,从理论上支持,项目使用
1 微服务架构,每个微服务可以按需进行集群扩展,
2 并且相关组件也可以进行按需集群 mysql es redis
3 并还使用大量优化技术. 缓存,全文检索,页面静态化(pc浏览器) 数据库优化,jvm优化等!!!
其实我们测人人员做过50W性能策略,是是支持! 你用了多少设备?? 这个我不太清楚…
3.分库分表后问题
3.1.分布式事务-垂直分库
分库后会带来一些棘手的问题:一个操作设计到多个库的话需要有分布式事务的支持
1、单数据库实例:
2、多数据库实例:
3.2.跨库操作查询-垂直分库
一个查询的数据来源于两个库,那么需要从两个库进行查询
如果是同一个库,通过反第三方式,在商品表设计冗余字段店铺名称和id就ok,以后直接查询商品表就行了.
如果不同库,也可以使用反第三范式. 改了店铺信息后同步修改商品里面店铺信息,同步在执行一条sql就行,
但是如果不同库,要远程发起请求去改… 而且不能用触发器
3.3.跨节点操作排序分页-水平分库
一个分页查询,正好某一页的数据来源于两个库,那么需要先对两个库的数据进行合并之后,在再执行SQL进行查询操作(复制,性能不好)
3.4.主键重复
如果水平分表之后,每个表ID都是自动增长,就会出现 商品表A的ID和商品表B的ID出现重复,需要使用全局唯一ID即:分布式ID,比如使用雪花算法来实现。
3.5.公共表
如果两个库都有用到某个表,该如何处理?两个库都放这个表,使用ShardingJDBC设置公共表
一切以上的问题可以直接解决,当然也可以通过三方框架实现 sharding-jdbc
3.6.水平分库分表效率说明
一定能提高效率吗?? 水平分库分表在操作之前要通过分库分表策略进行计算后再来操作.这个过程也要浪费时间.如果数据量少,没有必要水平分表分库. 500W左右 5亿–>100 订单??? 商品???
4.分库分表&集群方案
4.1.集群-主从同步,读写分离
主从同步由mysql自己实现,但是读写分离需要三方组件支持。
4.2.分库分表-垂直方向-设计层面
当做一个独立的表或者库处理就ok,微服务架构中,都是进行了分库的。
4.3.分库分表-水平方向
需要计算库和计算表,也要三方组件支持。
我们只需要实现水平分表和水平分库及读写分离。
4.4.组件选择
-
sharding-sphere:jar,前身是sharding-jdbc;分库分表,读写分离,当当网开源框架,文档丰富,使用成本低。 jar
-
TDDL:jar,Taobao Distribute Data Layer;淘宝业务框架,TDDL复杂度相对较高。当前公布的文档较少,只开源动态数据源,分表分库部分还未开源,还需要依赖diamond,不推荐使用。
-
Mycat:中间件。 使用起来比较复杂,需要安装额外的环境,不稳定,第三方应用,考虑中间件备份与集群
单独部署组件,对它集群
-
Mysql-proxy(基本淘汰):mysql-proxy是官方提供的mysql中间件产品可以实现负载平衡,读写分离,failover等,但其不支持大数据量的分库分表且性能较差。
5.Sharding-sphere简介
5.1.什么是Sharding-sphere
官网:http://shardingsphere.apache.org/index_zh.html
Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy 和 Sidecar(规划中)这 3 款相互独立,却又能够混合部署配合使用的产品组成。 它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如 Java 同构、异构语言、云原生等各种多样化的应用场景。
Apache ShardingSphere 定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。 它通过关注不变,进而抓住事物本质。关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆。
Apache ShardingSphere 5.x 版本开始致力于可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。 目前,数据分片、读写分离、多数据副本、数据加密、影子库压测等功能,以及 MySQL、PostgreSQL、SQLServer、Oracle 等 SQL 与协议的支持,均通过插件的方式织入项目。 开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere 目前已提供数十个 SPI 作为系统的扩展点,仍在不断增加中。
ShardingSphere 已于2020年4月16日成为 Apache 软件基金会的顶级项目。
Sharding-sphere是一个关系型数据库的分布式中间件。有三个部分组成,sharding-jdbc(JAVA),sharding-proxy(异构系统),Sharding-Sidecar(云原生)。
5.2.sharding-jdbc
sharding-jdbc 是一个开源的适用于微服务的分布式数据访问基础类库(jar),它始终以云原生的基础开发套件为目标。只支持java语言
sharding-jdbc定位为轻量级java框架,使用客户端直连数据库,以jar包的形式提供服务,未使用中间层,无需额外部署,并无其他依赖,可以理解为增强版的JDBC驱动
sharding-jdbc完整的实现了分库分表/读写分离/分布式主键功能,并实现了柔性事务.特点如下:
-
适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
-
基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
-
支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
5.3.Sharding-Proxy-支持多语言
定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前先提供MySQL版本,它可以使用任何兼容MySQL协议的访问客户端(如:MySQL Command Client, MySQL Workbench等)操作数据,对DBA更加友好。
5.4.Sharding-Sidecar
定位为Kubernetes(k8s)或Mesos的云原生数据库代理,以DaemonSet的形式代理所有对数据库的访问。 通过无中心、零侵入的方案提供与数据库交互的的啮合层,即Database Mesh,又可称数据网格。
6.ShardingJDBC实战
见word文档
6.1.集成ShardingJDBC
配置文档:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/
导入依赖:需要排除guava会和SpringCloud中的guava包冲突
<!-- 引入shardingjdbc -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
server.port=8080
spring.shardingsphere.datasource.names=ds
spring.shardingsphere.datasource.ds.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds.jdbc-url=jdbc:mysql://localhost:3306/shardingjdbc_test
spring.shardingsphere.datasource.ds.username=root
spring.shardingsphere.datasource.ds.password=123456
不再以默认的方式配置DataSource,而是以spring.shardingsphere前缀配置datasource,项目依然可以使用datasource
6.2. 分布式id-雪花算法
配置多数据源,把配置改成如下效果:
server.port=8080
spring.shardingsphere.datasource.names=ds
spring.shardingsphere.datasource.ds.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds.jdbc-url=jdbc:mysql://localhost:3306/shardingjdbc_test
spring.shardingsphere.datasource.ds.username=root
spring.shardingsphere.datasource.ds.password=123456
spring.shardingsphere.sharding.tables.t_user.key-generator.column=id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
package cn.ronghuanet.mapper;
import cn.ronghuanet.domain.User;
import org.apache.ibatis.annotations.Insert;
public interface UserMapper {
User loadById(Long id);
void removeById(Long id); // 删除,做事务管理的
@Insert("insert into t_user(username,age) value(#{username},#{age})")
void save(User user);
}
@Test
public void testInsert()throws Exception{
User user = new User();
user.setUsername("zs");
user.setAge(10);
userMapper.save(user);
}
6.3. 读写分离
6.3.1 一主多从
spring.shardingsphere.datasource.names=master,slave
spring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://localhost:3308/crm
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=123456
spring.shardingsphere.datasource.slave.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave.jdbc-url=jdbc:mysql://localhost:3309/crm
spring.shardingsphere.datasource.slave.username=root
spring.shardingsphere.datasource.slave.password=123456
#配置读写分离
spring.shardingsphere.masterslave.name=ms
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave
spring.shardingsphere.props.sql.show=true
6.3.2 双主多从
spring:
shardingsphere:
datasource:
names: ds_master0, ds_slave00, ds_slave01, ds_master1, ds_slave10, ds_slave11
ds_master0:
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_master0
username: root
password: root
ds_slave00:
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/db_slave00
username: root
password: root
ds_slave01:
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3308/db_slave01
username: root
password: root
ds_master1:
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3310/db_master1
username: root
password: root
ds_slave10:
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3311/db_slave10
username: root
password: root
ds_slave11:
type: org.apache.commons.dbcp2.BasicDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3312/db_slave11
username: root
password: root
masterslave:
name: ms_group0
load-balance-algorithm-type: round_robin
ha-type: PRIMARIES_TAG
write: ds_master0
readwrite-splitting:
ds_master0:
name: ms0
slave-data-source-names: ds_slave00, ds_slave01
ds_master1:
name: ms1
slave-data-source-names: ds_slave10, ds_slave11
上述配置中定义了六个数据源:ds_master0 和 ds_master1 分别表示两个主库,ds_slave00、ds_slave01、ds_slave10、ds_slave11 分别表示四个从库。在 masterslave 子节点中,ha-type 属性设置为 PRIMARIES_TAG,表示基于标签的主库高可用方案。在数据源列表中,ds_master0 和 ds_slave00, ds_slave01 都有一个标签 "ds0",ds_master1 和 ds_slave10, ds_slave11 都有标签 "ds1"。这样可以保证同一标签下的主从数据库始终保持同步。
在 readwrite-splitting 子节点中,指定了读写分离的策略。在该示例中,ds_master0 与 ds_slave00、ds_slave01 组成一个名为 ms0 的读写分离组,ds_master1 与 ds_slave10、ds_slave11 组成一个名为 ms1 的读写分离组。对于读操作,可以通过 ShardingSphere-JDBC 提供的负载均衡算法(round_robin)进行路由。无论是写操作还是读操作,都可以直接使用数据源名称进行直接访问。
6.4 分库分表-水平方向
application.properties配置
spring.shardingsphere.datasource.names=ds0,ds1
spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/card_goods_1
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=123456
spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/card_goods_2
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=123456
#水平分库
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=shop_id
#通过分库策略确定数据源,进而确定那个库
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=ds$->{shop_id % 2}
#水平分表
spring.shardingsphere.sharding.tables.t_goods.actual-data-nodes=ds$->{0..1}.t_goods_$->{1..2}
spring.shardingsphere.sharding.tables.t_goods.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.t_goods.table-strategy.inline.algorithm-expression=t_goods_$->{id % 2+1}
spring.shardingsphere.sharding.tables.t_goods.key-generator.column=id
spring.shardingsphere.sharding.tables.t_goods.key-generator.type=SNOWFLAKE
mapper
public interface GoodsMapper {
@Insert("insert into t_goods(name,shop_id) value(#{name},#{shopId})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void save(Goods goods);
@Select("select * from t_goods")
List<Goods> loadAll();
}
test
package cn.ronghuanet.mapper;
import cn.ronghuanet.AppStart;
import cn.ronghuanet.domain.Goods;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppStart.class)
public class GoodsMapperTest {
@Autowired
private GoodsMapper goodsMapper;
@Test
public void save() {
//表分散不均匀,雪花算法和线程id有关系,真实环境中没有问题!! 每个请求都是一个线程,先要用多线程来模拟
for (int i = 0; i < 100; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
Goods goods;
if (finalI %2==0){
goods = new Goods(null,"goods"+ finalI,1L);
}else{
goods = new Goods(null,"goods"+ finalI,2L);
}
goodsMapper.save(goods);
}
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void loadAll() {
List<Goods> goods = goodsMapper.loadAll();
System.out.println(goods.size());
goods.forEach(System.out::println);
}
}
6.5.注意
你可能会菜的坑
1.2个库中的表都是一样的 t_order0 , t_order1
-
注意表名和库名,是中划线还是下划线
-
Insert的SQL中是不需要为分表的字段添加值的
-
配置文件不要写错,核对核对再核对 ,datasource的名字和数据库的名字保持一致
分库分表的列就不要自动递增了,也不要主键了
四.总结
1.重点内容
- CAP理论
- 分库分表概念
- Shardingjdbc实战
2.面试必备
-
什么是CAP理论
-
哪些是AP,哪些是CP?讲几个案例
-
什么是垂直分库分表
-
什么是水平分库分表
-
多少数据量适合分表
-
分表的规则有哪些,你们是按照什么规则分表的,具体算法是什么
-
你们分表后的ID怎么生成
-
你们使用什么技术分表的?
-
ShardingJDBC读写分离怎么配置
-
什么是Mysql主从复制
-
主从复制解决什么问题,不能解决什么问题?
-
主从复制的原理是什么?
-
大概说一下主从复制要做哪些事情
-
你们项目用了几个数据库