sharding‐jdbc之分库分表(mysql主从同步的数据库安装和使用)

水平分表

创建基础工程..

引入sharding‐jdbc的maven依赖包  注意需要数据库连接池等依赖

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.0.0-RC1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.16</version>
</dependency>

表结构

-- 创建库
CREATE DATABASE `order_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

-- 创建表1
DROP TABLE IF EXISTS `t_order_1`;
CREATE TABLE `t_order_1` (
  `order_id` bigint(20) NOT NULL COMMENT '订单id',
  `price` decimal(10,2) NOT NULL COMMENT '订单价格',
  `user_id` bigint(20) NOT NULL COMMENT '下单用户id',
  `status` varchar(50) NOT NULL COMMENT '订单状态',
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
-- 创建表2
DROP TABLE IF EXISTS `t_order_2`;
CREATE TABLE `t_order_2` (
  `order_id` bigint(20) NOT NULL COMMENT '订单id',
  `price` decimal(10,2) NOT NULL COMMENT '订单价格',
  `user_id` bigint(20) NOT NULL COMMENT '下单用户id',
  `status` varchar(50) NOT NULL COMMENT '订单状态',
  PRIMARY KEY (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

配置文件

server.port=56081

spring.application.name = sharding-jdbc-simple-demo

server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true

spring.main.allow-bean-definition-overriding = true

mybatis.configuration.map-underscore-to-camel-case = true

#sharding-jdbc分片规则配置
#数据源
spring.shardingsphere.datasource.names = m1
# 指定数据源 m1 对应的数据库信息
spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = root

# 指定 t_order 表的数据分布情况,t_order是表的前缀, 配置数据节点 m1.t_order_1 => t_order_1 ,m1.t_order_2 => t_order_2
#  分多少表  分片键 就到多少$->{1..10}
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m1.t_order_$->{1..3}

# 指定t_order表的主键生成策略为 雪花算法 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE

# 指定t_order表的分片策略,分片策略包括分片键和分片算法;
# order_id:主键。计算表的后缀 :order_id_?;   $->{order_id % 2 + 1} = 偶数0 + 1 = 1 即 order_id_1 ;  $->{order_id % 2 + 1} = 奇数 1 + 1 = 2 即 order_id_2
#   分 几 个表  分片键就除以几  {order_id % 10 + 1}
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 3 + 1}

# 打开sql输出日志
spring.shardingsphere.props.sql.show = true

swagger.enable = true

logging.level.root = info
logging.level.org.springframework.web = info
logging.level.com.itheima.dbsharding  = debug
logging.level.druid.sql = debug

 编写插入语句

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator.
 */
@Mapper
@Component
public interface OrderDao {

    /**
     * 插入订单
     * @param price
     * @param userId
     * @param status
     * @return
     */
    @Insert("insert into t_order(price,user_id,status)values(#{price},#{userId},#{status})")
    int insertOrder(@Param("price")BigDecimal price,@Param("userId")Long userId,@Param("status")String status);

    /**
     * 根据id列表查询订单
     * @param orderIds
     * @return
     */
    @Select("<script>" +
            "select" +
            " * " +
            " from t_order t " +
            " where t.order_id in " +
            " <foreach collection='orderIds' open='(' separator=',' close=')' item='id'>" +
            " #{id} " +
            " </foreach>" +
            "</script>")
    List<Map> selectOrderbyIds(@Param("orderIds") List<Long> orderIds);
}

单元测试

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.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Administrator
 * @version 1.0
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleBootstrap.class})
public class OrderDaoTest {

    @Autowired
    OrderDao orderDao;

    @Test
    public void testInsertOrder(){
        for(int i=1;i<20;i++){
            orderDao.insertOrder(new BigDecimal(i),1L,"SUCCESS");
        }
    }

//    @Test
    public void testSelectOrderbyIds(){
        List<Long> ids = new ArrayList<>();
        ids.add(373897739357913088L);
        ids.add(373897037306920961L);

        List<Map> maps = orderDao.selectOrderbyIds(ids);
        System.out.println(maps);
    }
}

数据库 表1 插入数据

表2

流程分析

通过日志分析,Sharding-JDBC在拿到用户要执行的sql之后干了哪些事儿:
(1)解析sql,获取片键值,在本例中是order_id
(2)Sharding-JDBC通过规则配置 t_order_$->{order_id % 2 + 1},知道了当order_id为偶数时,应该往t_order_1表插数据,为奇数时,往t_order_2插数据。
(3)于是Sharding-JDBC根据order_id的值改写sql语句,改写后的SQL语句是真实所要执行的SQL语句。
(4)执行改写后的真实sql语句
(5)将所有真正执行sql的结果进行汇总合并,返回。
 

分片策略

      包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略内置的分片策略大致可分为尾数取模、哈希、范围、标签、时间等。由用户方配置的分片策略则更加灵活,常用的使用行表达式配置分片策略,它采用Groovy表达式表示,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为 t_user_0 到 t_user_7 。

SQL解析

    当Sharding-JDBC接受到一条SQL语句时,会陆续执行 SQL解析 => 查询优化 => SQL路由 => SQL改写 => SQL执行 =>结果归并 ,最终返回执行结果。

SQL解析过程分为词法解析和语法解析。 词法解析器用于将SQL拆解为不可再分的原子符号,称为Token。并根据不同数据库方言所提供的字典,将其归类为关键字,表达式,字面量和操作符。 再使用语法解析器将SQL转换为抽象语法树。
 

例如

SELECT id, name FROM t_user WHERE status = 'ACTIVE' AND age > 18

解析之后的为抽象语法树见下图:
 

SQL路由

SQL路由就是把针对逻辑表的数据操作映射到对数据结点操作的过程。
根据解析上下文匹配数据库和表的分片策略,并生成路由路径。 对于携带分片键的SQL,根据分片键操作符不同可以划分为单片路由(分片键的操作符是等号)、多片路由(分片键的操作符是IN)和范围路由(分片键的操作符是BETWEEN),不携带分片键的SQL则采用广播路由。根据分片键进行路由的场景可分为直接路由、标准路由、笛卡尔路由等

标准路由
笛卡尔路由
全库表路由


SQL改写

   工程师面向逻辑表书写的SQL,并不能够直接在真实的数据库中执行,SQL改写用于将逻辑SQL改写为在真实数据库中可以正确执行的SQL

如一个简单的例子,若逻辑SQL为:

SELECT order_id FROM t_order WHERE order_id=1;

假设该SQL配置分片键order_id,并且order_id=1的情况,将路由至分片表1。那么改写之后的SQL应该为:

SELECT order_id FROM t_order_1 WHERE order_id=1;

SQL执行

      Sharding-JDBC采用一套自动化的执行引擎,负责将路由和改写完成之后的真实SQL安全且高效发送到底层数据源执行。 它不是简单地将SQL通过JDBC直接发送至数据源执行;也并非直接将执行请求放入线程池去并发执行。它更关注平衡数据源连接创建以及内存占用所产生的消耗,以及最大限度地合理利用并发等问题。 执行引擎的目标是自动化的平衡资源控制与执行效率,他能在以下两种模式自适应切换:

内存限制模式
    使用此模式的前提是,Sharding-JDBC对一次操作所耗费的数据库连接数量不做限制。 如果实际执行的SQL需要对某数据库实例中的200张表做操作,则对每张表创建一个新的数据库连接,并通过多线程的方式并发处理,以达成执行效率最大化。
连接限制模式
    使用此模式的前提是,Sharding-JDBC严格控制对一次操作所耗费的数据库连接数量。 如果实际执行的SQL需要对某数据库实例中的200张表做操作,那么只会创建唯一的数据库连接,并对其200张表串行处理。 如果一次操作中的分片散落在不同的数据库,仍然采用多线程处理对不同库的操作,但每个库的每次操作仍然只创建一个唯一的数据库连接。


内存限制模式适用于OLAP操作,可以通过放宽对数据库连接的限制提升系统吞吐量; 连接限制模式适用于OLTP操作,OLTP通常带有分片键,会路由到单一的分片,因此严格控制数据库连接,以保证在线系统数据库资源能够被更多的应用所使用,是明智的选择。
 

结果归并

将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并。Sharding-JDBC支持的结果归并从功能上可分为遍历、排序、分组、分页和聚合5种类型,它们是组合而非互斥的关系。

归并引擎的整体结构划分如下图

结果归并从结构划分可分为流式归并、内存归并和装饰者归并。流式归并和内存归并是互斥的,装饰者归并可以在流式归并和内存归并之上做进一步的处理。

内存归并
    他是将所有分片结果集的数据都遍历并存储在内存中,再通过统一的分组、排序以及聚合等计算之后,再将其封装成为逐条访问的数据结果集返回。
流式归并
    是指每一次从数据库结果集中获取到的数据,都能够通过游标逐条获取的方式返回正确的单条数据,它与数据库原生的返回结果集的方式最为契合
装饰者归并
    是对所有的结果集归并进行统一的功能增强,比如归并时需要聚合SUM前,在进行聚合计算前,都会通过内存归并或流式归并查询出结果集。因此,聚合归并是在之前介绍的归并类型之上追加的归并能力,即装饰者模式。

1、每个表先排序
2、从第一个表里面找到最大的,和后边的比较,找到最大值100
3、然后拿第一表的第二大值90去和99、95比较大小, 找到第二大值 99
4、然后拿第二个表的第二大值89和95比较,找到第三大值 95
依次类推.....,一个一个的去比对,就像一个游标

分库

配置文件

server.port=56081

spring.application.name = sharding-jdbc-simple-demo

server.servlet.context-path = /sharding-jdbc-simple-demo
spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true

spring.main.allow-bean-definition-overriding = true

mybatis.configuration.map-underscore-to-camel-case = true

#sharding-jdbc分片规则配置
#数据源
spring.shardingsphere.datasource.names = m1,m2

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver‐class‐name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username = root
spring.shardingsphere.datasource.m1.password = root
spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver‐class‐name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username = root
spring.shardingsphere.datasource.m2.password = root


# 分库策略,以user_id为分片键,分片策略为user_id % 2 + 1,user_id为偶数操作m1数据源,否则操作m2。
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.sharding-column = user_id
spring.shardingsphere.sharding.tables.t_order.database-strategy.inline.algorithm-expression =m$->{user_id % 2 + 1 }

# 计算表(共多少表)
# 指定 t_order 表的数据分布情况,t_order是表的前缀, 配置数据节点 m1.t_order_1,m1.t_order_2,,m1.t_order_3, m2.t_order_1,m2.t_order_2,m2.t_order_3
spring.shardingsphere.sharding.tables.t_order.actual-data-nodes = m$->{1..2}.t_order_$->{1..3}
#分库策略,如何将一个逻辑表映射到多个数据源
spring.shardingsphere.sharding.tables.<逻辑表名称>.database-strategy.<分片策略>.<分片策略属性名>= #分片策略属性值
#分表策略,如何将一个逻辑表映射为多个实际表
spring.shardingsphere.sharding.tables.<逻辑表名称>.table-strategy.<分片策略>.<分片策略属性名>= #分片策略属性值
# 指定t_order表的主键 order_id 生成策略为 雪花算法 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.key-generator.column=order_id
spring.shardingsphere.sharding.tables.t_order.key-generator.type=SNOWFLAKE

# 计算数据落到哪个表里面
# 指定t_order表的分片策略,分片策略包括分片键:order_id 和 分片算法: t_order_$->{order_id % 3 + 1}
# order_id:主键。计算表的后缀 :order_id_?;   $->{order_id % 2 + 1} = 偶数0 + 1 = 1 即 order_id_1;$->{order_id % 2 + 1}=奇数 1+1=2 即 order_id_2;分几个表 分片键就除以几  {order_id % 10 + 1}
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.sharding-column = order_id
spring.shardingsphere.sharding.tables.t_order.table-strategy.inline.algorithm-expression = t_order_$->{order_id % 3 + 1}


# 打开sql输出日志
spring.shardingsphere.props.sql.show = true

swagger.enable = true

logging.level.root = info
logging.level.org.springframework.web = info
logging.level.com.itheima.dbsharding  = debug
logging.level.druid.sql = debugc

 插入数据

/**
 * 插入订单
 * @param price
 * @param userId
 * @param status
 * @return
 */
@Insert("insert into t_order(price,user_id,status)values(#{price},#{userId},#{status})")
int insertOrder(@Param("price")BigDecimal price,@Param("userId")Long userId,@Param("status")String status);

测试插入

@Test
public void testInsertOrder(){
    for(int i=1;i<=20;i++){
        orderDao.insertOrder(new BigDecimal(i),1L,"SUCCESS");
    }
}

插入数据

测试查询

@Test
public void testSelectOrderbyUserAndIds(){
    List<Long> ids = new ArrayList<>();
    ids.add(982641951578783745L);
    ids.add(982641951578783746L);
    ids.add(982641951247433729L);
    List<Map> maps = orderDao.selectOrderbyUserAndIds(2L,ids);
    System.out.println(maps);
}
 @Test
    public void testSelectOrderbyIds(){
        List<Long> ids = new ArrayList<>();
        ids.add(982641951578783745L);
        ids.add(982641951578783746L);
        ids.add(982641951247433729L);
        List<Map> maps = orderDao.selectOrderbyIds(ids);
        System.out.println(maps);
    }

dao层

/**
 * 根据id列表查询订单
 * @param orderIds
 * @return
 */
@Select("<script>" +
        "select" +
        " * " +
        " from t_order t " +
        " where t.order_id in " +
        " <foreach collection='orderIds' open='(' separator=',' close=')' item='id'>" +
        " #{id} " +
        " </foreach>" +
        "</script>")
List<Map> selectOrderbyIds(@Param("orderIds") List<Long> orderIds);

/**
 * 根据id列表和用户id查询订单
 * @param orderIds
 * @return
 */
@Select("<script>" +
        "select" +
        " * " +
        " from t_order t " +
        " where t.order_id in " +
        " <foreach collection='orderIds' open='(' separator=',' close=')' item='id'>" +
        " #{id} " +
        " </foreach>" +
        " and user_id = #{userId} " +
        "</script>")
List<Map> selectOrderbyUserAndIds(@Param("userId") Long userId,@Param("orderIds") List<Long> orderIds);

Sharding-JDBC支持以下几种分片策略:

不管理分库还是分表,策略基本一样。
    standard:标准分片策略,
        对应StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
    complex:符合分片策略
        对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。
    inline:行表达式分片策略
        对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为 t_user_0 到t_user_7 。
    hint:Hint分片策
        对应HintShardingStrategy。通过Hint而非SQL解析的方式分片的策略。对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。
    none:不分片策略
        对应NoneShardingStrategy。不分片的策略。
 

公共表

     公共表属于系统中数据量较小,变动少,而且属于高频联合查询的依赖表。参数表、数据字典表等属于此类型。可以将这类表在每个数据库都保存一份,所有更新操作都同时发送到所有分库执行。接下来看一下如何使用Sharding-JDBC实现公共表。
 

(1)创建数据库
分别在user_db、order_db_1、order_db_2中创建t_dict表:

CREATE TABLE `t_dict` (
    `dict_id` BIGINT ( 20 ) NOT NULL COMMENT '字典id',
    `type` VARCHAR ( 50 ) CHARACTER 
    SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典类型',
    `code` VARCHAR ( 50 ) CHARACTER 
    SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典编码',
    `value` VARCHAR ( 50 ) CHARACTER 
    SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典值',
    PRIMARY KEY ( `dict_id` ) USING BTREE 
) ENGINE = INNODB CHARACTER 
SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

配置文件

# 指定t_dict为公共表
spring.shardingsphere.sharding.broadcast‐tables=t_dict

添加字典

/**
 * 新增字典
 * @param type 字典类型
 * @param code 字典编码
 * @param value 字典值
 * @return
 */
@Insert("insert into t_dict(dict_id,type,code,value) value(#{dictId},#{type},#{code},#{value})")
int insertDict(@Param("dictId") Long dictId, @Param("type") String type, @Param("code")String code, @Param("value")String value);
                        

测试

@Test
public void testInsertDict(){
    dictDao.insertDict(3L,"user_type","2","超级管理员");
    dictDao.insertDict(4L,"user_type","3","二级管理员");
}

删除

@Test
public void testDeleteDict(){
    dictDao.deleteDict(3L);
    dictDao.deleteDict(4L);
}
/**
 * 删除字典
 * @param dictId 字典id
 * @return
 */
@Delete("delete from t_dict where dict_id = #{dictId}")
int deleteDict(@Param("dictId") Long dictId);

 表数据都删除了

关联查询

@Test
public void testSelectUserInfobyIds(){
    List<Long> userIds = new ArrayList<>();
    userIds.add(1L);
    userIds.add(2L);
    List<Map> users = userDao.selectUserInfobyIds(userIds);
    System.out.println(users);
}
/**
 * 根据id列表查询多个用户   用户和字典关联
 * @param userIds 用户id列表
 * @return
 */
@Select({"<script>",
        " select",
        " * ",
        " from t_user t ,t_dict b",
        " where t.user_type = b.code and t.user_id in",
        "<foreach collection='userIds' item='id' open='(' separator=',' close=')'>",
        "#{id}",
        "</foreach>",
        "</script>"
})
List<Map> selectUserInfobyIds(@Param("userIds") List<Long> userIds);

主从复制数据库安装

刚开始只有一个

一、复制一份之前安装的安装目录,并修改名称为 mysql-5.7.20-winx64-s1

修改配置文件  my.ini

修改端口号和目录 

然后使用安装脚本

管理员运行cmd(可能不行)

mysqld install mysqls1 --defaults-file="D:\RuanJianKaiFa\mysql\mysql-5.7.20-winx64-s1\my.ini" 

二、或者使用安装脚本命令

D: 
cd D:\RuanJianKaiFa\mysql\mysql-5.7.20-winx64-s1\bin
mysqld --initialize-insecure --user=mysqls1
mysqld -install
net start mysql
mysql -u root -p

  修改密码脚本命令


D:
cd D:\RuanJianKaiFa\mysql\mysql-5.7.20-winx64-s1\bin
mysqladmin -uroot -p password 

安装成功

主库配置

# 主从同步配置
#主库开启日志
log-bin = mysql-bin
#设置服务id,主从不能一致
server-id = 1
#设置需要同步的数据库
binlog-do-db=user_db
#屏蔽系统库同步
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
binlog-ignore-db=performance_schema

从库配置

#开启日志 从库配置
log-bin = mysql-bin
#设置服务id,主从不能一致
server-id = 2
#设置需要同步的数据库
replicate_wild_do_table=user_db.%
#屏蔽系统库同步
replicate_wild_ignore_table=mysql.%
replicate_wild_ignore_table=information_schema.%
replicate_wild_ignore_table=performance_schema.%

主库和从库都重启服务

连接从库

三,授权主从复制专用账号

主库操作

主库的用户

执行创建用户的命令   连接主库后执行

-- 授权主备复制专用账号
GRANT REPLICATION SLAVE ON *.* TO 'db_sync' @'%' IDENTIFIED BY 'db_sync';

-- 刷新权限
FLUSH PRIVILEGES;

主库创建用户成功

 确认位点 记录下文件名以及位点

-- 确认位点 记录下文件名以及位点
show master status;

从库操作

-- 先停止同步
STOP SLAVE;

修改从库指向到主库,使用上一步记录的文件名以及位点

#修改从库指向到主库,使用上一步记录的文件名以及位点
CHANGE MASTER TO
master_host = 'localhost',
master_user = 'db_sync',
master_password = 'db_sync',
master_log_file = 'mysql-bin.000001',
master_log_pos = 154;

执行成功

启动同步

START SLAVE;

 -- 查看从库状态Slave_IO_Runing和Slave_SQL_Runing都为Yes说明同步成功,如果不为Yes,请检查error_log,然后排查相关异常。

show slave status

都是yes才行

请注意,主从MySQL下的数据(data)目录下有个文件auto.cnf,文件中定义了uuid,要保证主从数据库实例的uuid不一样,建议直接删除掉,重启服务后将会重新生成。
 

从库目录找到auto.cnf,由于是从库是复制的主库,这里文件是一样的,里面有一个uuid

将这个文件删除后重启服务,会从新生成此文件

 重新生成文件

再次查看 都是yes 了

测试 是否同步成功

添加主库数据,去从库数据,看从库数据是否修改

同步成功

注意 注意 注意

配置的数据库和要同步的库要一样

实现sharding-jdbc读写分离

配置文件

spring.shardingsphere.datasource.names = m0,m1,m2,s0

spring.shardingsphere.datasource.s0.type = com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.s0.driver‐class‐name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.s0.url = jdbc:mysql://localhost:3307/user_db?useUnicode=true
spring.shardingsphere.datasource.s0.username = root
spring.shardingsphere.datasource.s0.password = root
 

 主库从库逻辑数据源定义

#主库
spring.shardingsphere.sharding.master‐slave‐rules.ds0.master-data-source-name=m0
#从库
spring.shardingsphere.sharding.master‐slave‐rules.ds0.slave-data-source-names=s0

t_user分表策略,固定分配至ds0的t_user真实表

# t_user分表策略,固定分配至ds0的t_user真实表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes = ds0.t_user

测试查询 走的s0库(从库)

    @Test
    public void testSelectUserbyIds(){
        List<Long> userIds = new ArrayList<>();
        userIds.add(1L);
//        userIds.add(2L);
        List<Map> users = userDao.selectUserbyIds(userIds);
        System.out.println(users);
    }
/**
 * 根据id列表查询多个用户
 * @param userIds 用户id列表
 * @return
 */
@Select({"<script>",
        " select",
        " * ",
        " from t_user t ",
        " where t.user_id in",
        "<foreach collection='userIds' item='id' open='(' separator=',' close=')'>",
        "#{id}",
        "</foreach>",
        "</script>"
})
List<Map> selectUserbyIds(@Param("userIds") List<Long> userIds);

测试添加 走的m0库(主库)

@Test
public void testInsertUser(){
    for (int i = 2 ; i<3; i++){
        Long id = i + 1L;
        userDao.insertUser(id,"姓名"+ id );
    }
}
/**
 * 新增用户
 * @param userId 用户id
 * @param fullname 用户姓名
 * @return
 */
@Insert("insert into t_user(user_id, fullname) value(#{userId},#{fullname})")
int insertUser(@Param("userId")Long userId, @Param("fullname")String fullname);

  • 34
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是详细流程: 1. 引入Sharding-JDBC依赖 在pom.xml文件中添加Sharding-JDBC的依赖: ```xml <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-core</artifactId> <version>4.1.1</version> </dependency> ``` 2. 配置数据源 在application.yml中配置数据源信息,如下所示: ```yaml spring: datasource: # 主库 master: url: jdbc:mysql://localhost:3306/db_master?serverTimezone=UTC&useSSL=false&characterEncoding=utf-8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver # 从库 slave: url: jdbc:mysql://localhost:3306/db_slave?serverTimezone=UTC&useSSL=false&characterEncoding=utf-8 username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver shardingsphere: datasource: names: master,slave # 配置主从库 master-slave-rules: ms: master-data-source-name: master slave-data-source-names: slave load-balance-algorithm-type: round_robin # 配置数据分片规则 sharding: tables: user_info: # 分库键 database-strategy: inline: sharding-column: user_id algorithm-expression: ds${user_id % 2} # 分表键 table-strategy: inline: sharding-column: order_id algorithm-expression: t_order_${order_id % 2} actual-data-nodes: master.user_info_$->{0..1} key-generator: column: id type: SNOWFLAKE ``` 3. 创建数据表 创建两个数据库db_master和db_slave,每个数据库中创建user_info表,表结构如下: ```sql CREATE TABLE `user_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `user_id` bigint(20) NOT NULL COMMENT '用户ID', `order_id` bigint(20) NOT NULL COMMENT '订单ID', `name` varchar(50) NOT NULL COMMENT '姓名', `age` int(11) NOT NULL COMMENT '年龄', `address` varchar(200) DEFAULT NULL COMMENT '地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表'; ``` 4. 测试分库分表 在代码中使用Sharding-JDBC进行数据库操作,如下所示: ```java @Service public class UserService { @Autowired private JdbcTemplate jdbcTemplate; public void addUser(UserInfo userInfo) { String sql = "INSERT INTO user_info (user_id,order_id,name,age,address) VALUES (?,?,?,?,?)"; Object[] params = new Object[]{userInfo.getUserId(), userInfo.getOrderId(), userInfo.getName(), userInfo.getAge(), userInfo.getAddress()}; jdbcTemplate.update(sql, params); } public List<UserInfo> getUserList() { String sql = "SELECT * FROM user_info"; List<UserInfo> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(UserInfo.class)); return userList; } } ``` 以上就是使用Sharding-JDBC实现单库分表的详细流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值