文章目录
1.ShardingSphere的三部曲
ShardingSphere的三部曲
- ShardingSphere - JDBC ( 可理解为增强版的 JDBC 驱动 )
- ShardingSphere - Proxy( sharding-proxy 相当于 mycat,单独一个服务)
- ShardingSphere - Sidecar(TODO)
2.搭建MySQL读写分离(Linux安装mysql)
2.1 普通安装mqsql搭建(方式一)
## 1.配置mysql扩展源
rpm -ivh http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql57-community-release-el7-10.noarch.rpm
## 2.yum安装mysql
yum install mysql-community-server -y
## 3.启动mysql,并加入开机自启
systemctl start mysqld
systemctl stop mysqld
systemctl enable mysqld -- 设置mysql服务开机自启动
systemctl status mysqld -- 查看mysql服务启动状态
## 4.使用msql初始密码登录数据库
mysql -uroot -p$(awk '/temporary password/{print $NF}' /var/log/mysqld.log)
## 5.修改密码
-- 降低mysql设置密码的难度(不更改密码需要包含大小写等字符)
set global validate_password_policy=0;
set global validate_password_length=1;
-- 修改密码
ALTER USER 'root'@'localhost' IDENTIFIED BY 'root';
-- 退出mysql服务
quit;
-- 命令行到登录mysql
mysql -uroot -proot
-- 授权远程登录
grant all on *.* to root@'%' identified by 'root';
-- 涮新
flush privileges;
## 6.本地远程连接云服务器
## 本地远程连接linux服务器
注:
主从复制 --> 从数据库层面说的
读写分离 --> 代码业务层面说的
Master节点修改配置(master节点的 “/etc/my.cnf”)
①需改配置文件保存二进制文件
>vim /etc/my.cnf
## 找到"[mysqld]"下面复制下面的配置
[mysqlId]
## 唯一id
server-id=100
## 开启二进制日志功能,名字可以随便取
log-bin=mysql-bin
## 复制过滤:不需要备份的数据库(mysql的mysql库一般不需要同步)
binlog-ignore-db=mysql
## 下面的这些配置可以不需要写
## 为每个session分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size=1M
## 主从复制的格式(mixed,statement,row,默认格式是statement)
binlog_format=mixed
注:配置完需要重启
②mater服务器给slave服务器授权
mysql -uroot -proot
## 授予slave服务器可以同步master服务
grant replication slave,replication client on *.* to 'root'@'slave服务的ip' identified by 'slave服务器的密码';
## 刷新
flush privileges;
## 查看mysql现在有哪些用户及对应的ip权限(可以不执行,只是一个查看)
select user,host from mysql.user;
③查询master服务器的binlog文件名和位置
show master status;
Slave节点修改配置(slave节点的 “/etc/my.cnf”)
①需改配置文件保存二进制文件
>vim /etc/my.cnf
## 找到"[mysqld]"下面复制下面的配置
[mysqlId]
## 唯一id
server-id=102
## 开启二进制日志功能,以备slave作为其他slave的master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志
relay_log=edu-mysql-relay-bin
## 复制过滤:不需要备份的数据库(mysql的mysql库一般不需要同步)
binlog-ignore-db=mysql
## 如果需要同步函数或者存储过程
log_bin_trust_function_creators=true
## 为每个session分配的内存,在事务过程中用来存储二进制日志的缓存
binlog_cache_size=1M
## 主从复制的格式(mixed,statement,row,默认格式是statement)
binlog_format=mixed
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断
## 如:1062错误是指一些主键重复。1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
注:配置完需要重启
②slave服务器进行关联master服务器
mysql>mysql -uroot -proot
-- "master_log_file"和"master_log_pos" 在master机上"show master status;"
change master to master_host='master服务器ip',master_user='root',master_password='master密码',master_port=3306,master_log_file='master查到的',master_log_pos=master查到的;
③在slave节点查看主从同步状态
## 启动主从复制
start slave;
## 再查看主从同步状态
show slave status\G;
## 停止复制
stop slave;
主从复制报错
①从服务器没有同步主服务器的数据
②输入主服务器的密码错误
## 原因1:复制位置发生改变(这种可能性比较大)
## 操作从服务器
## 停止复制
stop slave;
## "master_log_file"和"master_log_pos" 在master机上"show master status;"
change master to master_host='master服务器ip',master_user='root',master_password='master密码',master_port=3306,master_log_file='master查到的',master_log_pos=master查到的;
## 启动主从复制
start slave;
## 原因2:更改服务器了
## 操作从服务器
## 停止复制
stop slave;
set global sql_slave_skip_counter=1;
## 启动主从复制
start slave;
## 再查看主从同步状态
show slave status\G;
2.2使用Docker安装mysql(我使用的方式)
注:如果上面安装过了就跳过这一步,我懒得配置,下载也比较麻烦,所以直接使用docker安装的mysql,大家不会docker的可以去看下我的
docker快速入门:https://blog.csdn.net/wang121213145/category_11575654.html?spm=1001.2014.3001.5482
docker中安装mysql并配置主从复制:
[TODO]
3.代码实战读写分离
概述:定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。
在主数据库创建数据库以及数据库表:
创建一个springboot项目:
添加pom依赖:
<groupId>com.xii</groupId>
<artifactId>shardingjdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shardingjdbc</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<shardingsphere.version>4.0.0-RC1</shardingsphere.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mysql5.7驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.4</version>
<scope>runtime</scope>
</dependency>
<!-- jsonson处理,服务于jsonutil工具和springmvc的json返回 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-avro</artifactId>
<version>2.10.0</version>
</dependency>
<!-- md5密码加密的时依赖的包 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
<!--mybatis-plus整合mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
application.yml配置
server:
port: 8999
logging:
level:
root: info
spring:
profiles:
active: dev
main: #允许相同bean名称覆盖
allow-bean-definition-overriding: true
application:
name: shardingsphere-demo
jackson:
# 日期进行格式化处理
date-format: yyyy/MM/dd HH:mm:ss
# 解决json转换少8个小时的问题
time-zone: GMT+8
# 解决json返回过程中long的精度丢失问题
generator:
write-numbers-as-strings: true
write-bigdecimal-as-plain: true
# mybatis-plus配置
#mybatis-plus:
# mapper-locations: classpath*:/mappers/*.xml
# configuration:
# call-setters-on-nulls: true
# type-aliases-package: com.thh.entity
application-dev.yml配置
spring:
shardingsphere:
#配置数据源
datasource:
#每个数据源的别名 主库从库
names: ds0,ds1
#主库
ds0:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.50.99:3340/master_slave_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useSSL=false
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
username: root
#从库
ds1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.50.99:3341/master_slave_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useSSL=false
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
username: root
#配置默认数据源
sharding:
#默认数据源 主要用于写 这就是配置读写分离 如果不配置 会把三个节点都当做slave 写数据的时候会报错
default-data-source-name: ds0
#配置从节点 不配置的话 所有读操作也会在主节点
masterslave:
name: ms
#主库
master-data-source-name: ds0
#从库
slave-data-source-names: ds1
#从节点负载均衡使用轮询机制
load-balance-algorithm-type: round_robin
#参数配置 显示sql
props:
sql:
show: true
UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserMapper userMapper;
@RequestMapping("addUser")
public String insertUser(){
User user = new User();
user.setNickname("wxt"+ new Random().nextInt());
user.setPassword(123456);
user.setAge(19);
user.setSex(0);
userMapper.addUser(user);
return "success";
}
@GetMapping("getUser")
public List<User> getUser(){
return userMapper.getUserList();
}
}
User
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@TableName("t_user")
public class User {
// 主键
@TableId
private Integer id;
// 用户名
private String nickname;
private Integer password;
// 年龄
private Integer age;
/**
* 性别 0代表女 1代表男
*/
private Integer sex;
}
UserMapper
public interface UserMapper {
@Insert("insert t_user(nickname,password,age,sex) value(#{nickname},#{password},#{age},#{sex})")
void addUser(User user);
@Select("select * from t_user")
List<User> getUserList();
}
启动类:
@SpringBootApplication
@MapperScan("com.xii.shardingjdbc.mapper")
public class ShardingjdbcApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingjdbcApplication.class, args);
}
}
测试:
当执行添加数据的时候,一直走的是ds0节点:
查询的时候:
一直为从节点ds1:
4.分库分表
必要知识:
分库分表实战:
注意:分库分表只可以对主库进行分库分表操作,从库只有读,我仍然是用的上面主从分离的时候使用的库的名称,但都是主库,不要搞混。
两个库建数据库表:t_user_0,t_user_1
项目还是之前的项目,在配置文件中
标准分片策略 1
spring:
shardingsphere:
#配置数据源
datasource:
#每个数据源的别名 主库
names: ds0,ds1
#主库
ds0:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.50.99:3340/master_slave_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useSSL=false
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
username: root
#主库
ds1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.50.99:3341/master_slave_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useSSL=false
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
username: root
#配置默认数据源
sharding:
#默认数据源
default-data-source-name: ds0
#配置分表规则
tables:
#逻辑表名
kst_user:
#数据节点 数据源$->{0..N}.逻辑表名$->{0..N}
actual-data-nodes: ds$->{0..1}.t_user_$->{0..1}
#拆分库策略 什么样子数据放入哪个数据库
database-strategy:
inline:
#分片算法
algorithm-expression: ds$->{age % 2}
#分片字段
sharding-column: age
#拆分表策略
table-strategy:
inline:
#分片算法
algorithm-expression: t_user_$->{age % 2}
#分片字段
sharding-column: age
#参数配置 显示sql
props:
sql:
show: true
连接的两个数据库都要是主库
解释:
#数据节点 数据源$->{0..N}.逻辑表名$->{0..N}
actual-data-nodes: ds$->{0..1}.t_user_$->{0..1}
#拆分库策略 什么样子数据放入哪个数据库
database-strategy:
inline:
#分片算法
algorithm-expression: ds$->{age % 2}
#分片字段
sharding-column: age
actual-data-nodes: ds\$->{0..1}.t_user_$->{0..1}
对数据库分库 数据库有ds0和ds1后面的.数据表同样也是有t_user_0,t_user_1
algorithm-expression: ds$->{age % 2}
age对2取余,结果只会为0或者1,正对应两个数据库
测试结果:
age为奇数,进入的是ds1库中的t_user_1表
age为0的时候进入的是ds0库中的t_user_0表
标准分片策略 2
配置文件:
spring:
shardingsphere:
#配置数据源
datasource:
#每个数据源的别名 主库
names: ds0,ds1
#主库
ds0:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.50.99:3340/master_slave_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useSSL=false
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
username: root
#主库
ds1:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.50.99:3341/master_slave_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT&useSSL=false
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
username: root
#配置默认数据源
sharding:
# #默认数据源 主要用于写
default-data-source-name: ds0
# #配置从节点 不配置的话 所有读操作也会在主节点
# masterslave:
# name: ms
# #主库
# master-data-source-name: ds0
# #从库
# slave-data-source-names: ds1
# #从节点负载均衡使用轮询机制
# load-balance-algorithm-type: round_robin
# binding-tables: t_user
# default-database-strategy:
# inline:
# algorithm-expression: ds0
# sharding-column: user_id
#配置分表规则
tables:
#逻辑表名
kst_user:
#数据节点 数据源$->{0..N}.逻辑表名$->{0..N}
actual-data-nodes: ds$->{0..1}.t_user_$->{0..1}
#拆分库策略 什么样子数据放入哪个数据库
database-strategy:
standard:
shardingColumn: birthday
preciseAlgorithmClassName: com.xii.shardingjdbc.config.BirthdayAlgorithm
#拆分表策略
table-strategy:
inline:
#分片算法
algorithm-expression: t_user_$->{age % 2}
#分片字段
sharding-column: age
#参数配置 显示sql
props:
sql:
show: true
这里面主要是对生日进行划分,在配置类中配置:
对生日进行判断,写一个配置类
public class BirthdayAlgorithm implements PreciseShardingAlgorithm<Date> {
List<Date> dateList = new ArrayList<>();
{
//设置日期
Calendar instance = Calendar.getInstance();
instance.set(2000,1,1,0,0,0);
Calendar instance2 = Calendar.getInstance();
instance2.set(2022,1,1,0,0,0);
dateList.add(instance.getTime());
dateList.add(instance2.getTime());
}
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
//获取数据真实属性
Date value = shardingValue.getValue();
//获取数据源名称列表
Iterator<String> iterator = availableTargetNames.iterator();
String target = "";
for (Date s : dateList) {
target = iterator.next();
//数据早于指定日期直接返回 1999 早于2000 返回 所以1999会进入ds0库
if(value.before(s)){
break;
}
}
return target;
}
}
测试:
成功:
分布式主键配置
因为不同的表使用自增id的话,会造成id冲突,所以在这里使用雪花算法id,配置:
#配置主键策略 避免主键重复
key-generator:
column: id
#雪花算法
type: SNOWFLAKE
注意id类型 是long
定义返回主键:
测试:
成功!
日期分表
定义配置文件:
controller
这样就可以按照传过来的日期分表,但是注意如果没有命中条件的话,会报错。
另外如果sql没有对应的字段的话,
他会将所有的表都插进去数据,这也是一个问题。
分页统计和实现
虽然分表了,但是我们看到查询分页的时候也是没有问题的,因为shardingjdbc已经帮我们做好了,形成一个笛卡尔乘积进行搜索,但是可能会稍微影响点性能
5 shardingjdbc的事务管理
6 总结