Mysql主从架构搭建、及读写分离
单机存储MySQL/Oracle有哪些缺点?
1)数据存储不安全-缺少备份机制
2)单个节点-连接数有限(修改-插入|更新|删除、查询)
连接分流:将修改操作发送给指定一个机器(称为主机master、Leader)、其他机器(slave、follower)负责同步master|Leader的操作。
Master数据和Slave节点数据 最终一定是一致的。
注意:Leader或者Master 可以读、可以写
slave或者follower 只可以读
优点:对主机实行 连接分流(将读操作从Master分离)
从机 负责系统所有的读操作(多个从机共同承担 读 负载)
系统而言:读、写
(*本次搭建结构为一主二从)
192.168.199.128 master
192.168.199.129 slave
192.168.199.130 slave
搭建步骤:
1)主机和从机安装mysql(centos为例)
安装mysql
[root@CentOSX ~]# yum install -y mysql-server
附:MySQL卸载步骤
[root@CentOSX ~]# yum remove mysql-server
[root@CentOSX ~]# rm -rf /var/lib/mysql
配置用户root
[root@CentOSX ~]# service mysqld start
[root@CentOSX ~]# mysqladmin -u root password 'root'
远程登录授权
[root@CentOSX ~]# mysql -u root -proot
mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT
OPTION;
mysql> FLUSH PRIVILEGES;
mysql> use mysql;
mysql> delete from user where password ='';
mysql>commit;
mysql>exit
说明:机器A作为MySQL的主机(Master )、机器B作为MySQL的从机(Slave),两台机 器均已经关闭防火墙。
2)主从配置
机器A(主机)配置
修改/etc/my.cnf文件
[root@CentOSA ~]# vim /etc/my.cnf
server-id=1
log-bin=mysql-bin
binlog-do-db=test
binlog-ignore-db=mysql
expire_logs_days=10
auto_increment_increment=2
auto_increment_offset=1
...
重启mysql服务
[root@CentOSA ~]# service mysqld restart
登录MySQL查看主机状态
[root@CentOSA ~]# mysql -u root -proot
mysql> show master status\G;
*************************** 1. row ***************************
File: mysql-bin.000001
Position: 106
Binlog_Do_DB: test
Binlog_Ignore_DB: mysql
1 row in set (0.00 sec)
ERROR:
No query specified
注意:这里需要记录 File: mysql-bin.000001
Position: 106
机器B(从机)配置(C机器同B机器,只需要向server-id修改为3)
修改/etc/my.cnf文件
[mysqld]
server-id=2
log-bin=mysql-bin
replicate-do-db=test
replicate-ignore-db=mysql
auto_increment_increment=2
auto_increment_offset=2
...
重启MySQL服务
[root@CentOSB ~]# service mysqld restart
登录MySQL配置从机查看状态(使用刚才记录的值)
[root@CentOSB ~]# mysql -u root -proot
mysql> stop slave;
mysql>change master to master_host='192.168.199.128',master_user='root',master_password='root',master_log_file='mysql-bin.000001',master_log_pos=106;
mysql> start slave;
mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.199.128
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 106
......
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: test
Replicate_Ignore_DB: mysql
......
在查看从机状态的时候如 果处出现了红色字体的信息,说明MySQL的主从搭建成功了。
路由中间件设计(实现读写分离)(示意)
<bean id ="masterDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.199.128:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id = "slaveDataSource1" class= "org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.199.129:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id = "slaveDataSource2" class= "org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.199.130:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
//设置状态标识类 用来表示读写状态
public interface Status {
String READ = "read";
String WRITE = "write";
}
public class MyThreadLocal {
private static final ThreadLocal<String> TL = new ThreadLocal<String>();
public static void setStatus(String str){
TL.set(str);
}
public static String getStatus(){
return TL.get();
}
public static void remove(){
TL.remove();
}
}
利用Spring AOP通过方法环绕通知获取调用的方法
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class ReadWriteMethodInterceptor implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
Method method = arg0.getMethod();
String name = method.getName();
//匹配以query开头的方法
if(name.matches("^query.*")){
MyThreadLocal.setStatus(Status.READ);
}else{
MyThreadLocal.setStatus(Status.WRITE);
}
Object proceed = arg0.proceed();
MyThreadLocal.remove();
return proceed;
}
}
创建自定义路由实现
import java.util.Random;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class ReadWriteDataSource extends AbstractRoutingDataSource{
//xml中配置的mysql数据源
private static final String[] s = {"slaveDataSource1","slaveDataSource2"};
@Override
protected Object determineCurrentLookupKey() {
String status = MyThreadLocal.getStatus();
//System.out.println(status);
if(status==null){//默认使用主库
return "masterDataSource";
}else{
if("read".equals(status)){
//这里以随机来作为自己的路由策略 后续可以自定义路由策略
return s[new Random().nextInt(s.length)];
}else{
return "masterDataSource";
}
}
}
}
配置自己的路由策略到spring工厂中
到此,就完成了mysql读写分离的路由实现