Mysql架构学习(二)

880 阅读7分钟

复制过程搭建

异步复制

主库执行完Commit后,在主库写入Binlog日志后即可成功返回客户端,无需等Binlog日志传送给从库。

步骤:

1、确保主从库安装了相同版本的数据库。

2、在主库上,设置一个复制使用的账户,并授予 REPLICATION SLAVE权限。这里创建一个复制用户repl,可以从IP为192.169.56.103的主机进行连接:

image.png

命令文本:GRANT REPLICATION SLAVE ON . To 'rep1'@'192.168.56.103' IDENTIFIED BY '1234test';

3、修改主数据库服务器的配置文件 my.cnf,开启 BINLOG,并设置 server-id的值。这两个参数的修改需要重新启动数据库服务才可以生效

在 my cnf中修改如下:

image.png

[mysqld]

log-bin=/home/ mysql/log/mysql-bin. log

server-id= 1

注意:如果mysql目录下无log目录,请先创建log目录

4、然后得到主库上当前的二进制日志名和偏移量值。这个操作的目的是为了在从数据库启动以后,从这个点开始进行数据的恢复。

执行show master status: image.png

5、修改从数据库的配置文件 my.cnf,增加 server-id参数。注意 server-id的值必须是唯一的,不能和主数据库的配置相同,如果有多个从数据库服务器,每个从数据库服务器必须有自己唯一的 server-id值。

image.png

在 mycnf中修改如下:

[mysqld]

server-id=2

6、在从库上,使用 - -skip-slave- start选项启动从数据库,这样不会立即启动从数据库服务上的复制进程,方便我们对从数据库的服务进行进一步的配置:

image.png

操作命令:./bin/mysqld_safe  --defaults-file=/home/mysql/mysql3307/my.cnf --skip-slave-start 

7、对从数据库服务器做相应设置,指定复制使用的用户,主数据库服务器的IP、端口

以及开始执行复制的日志文件和位置等,参考代码如下:

mysql> CHANGE MASTER TO

->MASTER_HOST=master host name

->MASTER_USER=replication_user_name

-> MASTER PASSWORD=replication_password

->MASTER_LOG_FILE='recorded_log_file_name

->MASTER_LOG_POS=recorded _log_position

举例说明如下:

CHANGE MASTER TO MASTER_HOST='192.168.56.103',MASTER_PORT=3306,MASTER_USER='rep1',MASTER_PASSWORD='1234test' ,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=428;

image.png

8、在从库上,启动 slave线程:

image.png

9、这时slave上执行 show processlist命令将显示类似如下的进程:

image.png

执行show slave status;将显示:

image.png

11、在master上执行 show processlist命令将显示类似如下的进程:

image.png

12、测试一下:

在主库上新建数据库和表,并插入数据,看看从库中是否会自动创建相关的数据库和表、插入数据。

新建数据库和表之前: image.png image.png

主库中创建数据库orders和表order_exp,并插入数据

  image.png

检查从库

image.png

半同步复制

在 MySQL5.5之前, MySQL的复制是异步操作,主库和从库的数据之间存在一定的延迟,这样存在一个隐患:当在主库上写人一个事务并提交成功,而从库尚未得到主库推送的 Binlog日志时,主库宕机了,例如主库可能因磁盘损坏、内存故障等造成主库上该事务 Binlog丢失,此时从库就可能损失这个事务,从而造成主从不一致。

而半同步复制,是等待其中一个从库也接收到Binlog事务并成功写入Relay Log之后,才返回Commit操作成功给客户端;如此半同步就保证了事务成功提交后至少有两份日志记录,一份在主库Binlog上,另一份在从库的Relay Log上,从而进一步保证数据完整性;半同步复制很大程度取决于主从网络RTT(往返时延),以插件 semisync_master/semisync_slave 形式存在。

安装比较简单,在上一小节异步复制的环境上,安装半同步复制插件即可。

(1)首先,判断 MySQL服务器是否支持动态增加插件:

mysql> select @@have_dynamic_loading;

  image.png

2)确认支持动态增加插件后,检查 MySQL的安装目录下是否存在插件:

image.png

安装插件:

在主库上安装插件semisync_master.so:

mysql> install plugin rpl_semi_sync_master SONAME 'semisync_master.so'

从库上则安装 semisync_slave.so插件:

mysql> install plugin rpl_semi_sync_slave SONAME 'semisync_slave.so';

安装完成后,从 plugin表中能够看到刚才安装的插件

mysql> select * from mysql.plugin;

3)需要分别在主库和从库上配置参数打开半同步semi-sync,默认半同步设置是不打开的,主库上配置全局参数:

mysql> set global rpl_semi_sync_master_enabled=1;

mysql> set global rpl_semi_sync_master timeout 30000;

从库上一样配置全局参数:

mysql> set global rpl_semi_sync_slave_enabled=1;

4)其他步骤同异步复制

读写分离实战

SpringBoot+MyBatis 结合MySQL读写分离

读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做。因此,一般来讲,读写分离有两种实现方式。第一种是依靠中间件帮我们做SQL分离;第二种是应用程序自己去做分离。这里我们选择程序自己来做,主要是利用Spring提供的路由数据源,以及AOP

然而,应用程序层面去做读写分离最大的弱点(不足之处)在于无法动态增加数据库节点,因为数据源配置都是写在配置中的,新增数据库意味着新加一个数据源,必然改配置,并重启应用。当然,好处就是相对简单。

主要思路:

1、选择数据库执行这个关键性问题,考察Spring的封装体系中

image.png

 的AbstractRoutingDataSource内部维护了一组目标数据源,并且做了路由key与目标数据源之间的映射,提供基于key查找数据源的方法。因此我们需要声明一个自己的的数据源MyRoutingDataSource来负责,并应该继承自AbstractRoutingDataSource,覆盖determineCurrentLookupKey方法,这个方法返回结果就是我们对数据源的选择。

2、考察AbstractRoutingDataSource的内部,有两个容器:

image.png

其中targetDataSources是指我们通过方法

image.png

设置的目标数据源,而resolvedDataSources是实际的数据源,Spring通过方法

image.png

进行了转换。

而在实际运行时,Spring通过getConnection方法获取数据库连接,在getConnection方法中最终调用 determineTargetDataSource方法来定位实际的数据源,在determineTargetDataSource方法就用到了我们前面覆盖的determineCurrentLookupKey方法来从容器resolvedDataSources获得实际的数据源

image.png

3、所以,我们先定义一个枚举类

image.png

来表示当前有几个数据源,并用这个类作为两个数据源容器的key。

4、同时为了保证线程安全,每个对数据库操作的线程当前所持有的数据源对象,我们用一个ThreadLocal来保存

image.png 然后用类DBContextHolder进行包装,并对外提供主从库切换方法、每个线程的数据源get和set方法

5、在我们自己的的数据源MyRoutingDataSource的determineCurrentLookupKey方法中,只需要即可。

image.png

6、在application.yml中配置原生数据源后通过配置类DataSourceConfig加载, image.png   image.png

7、然后在 DataSourceConfig中生成一个虚拟数据源myRoutingDataSource的实例,专门负责实际数据源的持有和路由, image.png

8、应用中的事务管理器和MyBatis均使用这个虚拟数据源myRoutingDataSource

image.png

9、为了减少对业务代码的侵入,我们定义了一个SpringAop类DataSourceAop,其中定义了两个切点,slavePointcut和masterPointcut,按照方法名进行数据源的切换,比如查询方法要求为query开头等等。

这样默认情况下,所有的查询都走从库,插入/修改/删除走主库

10、特殊情况是某些情况下我们需要强制读主库,针对这种情况,我们定义一个注解,用该注解标注的就读主库,对应的,修改下DataSourceAop中切点的定义。

image.png

11、测试下我们的代码即可,对应的代码在模块rw-separation下。