| MySQL —— 主从复制 读写分离 |
|---|
MySQL — — — Replication**
主从复制的好处:
* 做数据的热备* 如果主数据库宕机,可以快速将业务系统切换到从数据库上,可避免数据丢失。* 业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的频率,提高单个机器的I/O性能。如果对数据库的读和写都在同一个数据库服务器中操作,业务系统性能会降低。
主从复制(也称 AB 复制)允许将来自一个MySQL数据库服务器(主服务器)的数据复制到一个或多个MySQL数据库服务器(从服务器)。
复制是异步的 从站不需要永久连接以接收来自主站的更新。

* 主服务器上面的任何修改都会通过自己的 I/O tread(I/O 线程)保存在二进制日志 Binary log 里面。* 从服务器上面也启动一个 I/O thread,通过配置好的用户名和密码, 连接到主服务器上面请求读取二进制日志,然后把读取到的二进制日志写到本地的一个Realy log(中继日志)里面。* 从服务器上面同时开启一个 SQL thread 定时检查 Realy log(这个文件也是二进制的),如果发现有更新立即把更新的内容在本机的数据库上面执行一遍。
前提是作为主服务器角色的数据库服务器必须开启二进制日志。
每个从服务器都会收到主服务器二进制日志的全部内容的副本。
从服务器设备负责决定应该执行二进制日志中的哪些语句。
除非另行指定,否则主从二进制日志中的所有事件都在从站上执行。
MySQL — — — 读写分离
**
一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案,其中一个是主库,负责写入数据,我们称之为:写库;其它都是从库,负责读取数据,我们称之为:读库。
那么,对我们的要求是:
1、读库和写库的数据一致;(这个是很重要的一个问题,处理业务逻辑要放在service层去处理,不要在dao或者mapper层面去处理)2、写数据必须写到写库;读数据必须到读库。解决读写分离的方案有两种:应用层解决和中间件解决。
应用层解决读写分离
在进入Service之前,使用AOP来做出判断,是使用写库还是读库,判断依据可以根据方法名判断,比如说以query、find、get等开头的就走读库,其他的走写库。
/*** @author* 定义动态数据源,实现通过集成Spring提供的AbstractRoutingDataSource,只需要实现determineCurrentLookupKey方法即可* 由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。*/public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {// 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源keyreturn DynamicDataSourceHolder.getDataSourceKey();}}
记录线程的数据源
/*** @author* 使用ThreadLocal技术来记录当前线程中的数据源的key*/public class DynamicDataSourceHolder {//写库对应的数据源keyprivate static final String MASTER = "master";//读库对应的数据源keyprivate static final String SLAVE = "slave";//使用ThreadLocal记录当前线程的数据源keyprivate static final ThreadLocal<String> holder = new ThreadLocal<String>();/*** 设置数据源key* @param key*/public static void putDataSourceKey(String key) {holder.set(key);}/*** 获取数据源key* @return*/public static String getDataSourceKey() {return holder.get();}/*** 标记写库*/public static void markMaster(){putDataSourceKey(MASTER);}/*** 标记读库*/public static void markSlave(){putDataSourceKey(SLAVE);}}
切面
/*** @author* 定义数据源的AOP切面,通过该Service的方法名判断是应该走主库还是从库*/@AspectJpublic class DataSourceAspect {/*** 在进入Service方法之前执行** @param point 切面对象*/public void before(JoinPoint point) {// 获取到当前执行的方法名String methodName = point.getSignature().getName();if (isSlave(methodName)) {// 标记为主库DynamicDataSourceHolder.markSlave();} else {// 标记为次库DynamicDataSourceHolder.markMaster();}}/*** 判断是否为次库** @param methodName* @return*/private Boolean isSlave(String methodName) {// 方法名以query、find、get开头的方法名走从库return StringUtils.startsWithAny(methodName, "query","find","get");}}
