mysql主從,spring 讀寫分離
1,mysql的複製依賴bin-log日誌檔案,bin-log記錄了所有在master端執行的 ddl/dml/事務操作序列,並同步到slave端,slave根據日誌復現序列,即完成同步; 複製流程: 1,slave上面的io執行緒連線上master,並請求指定的日誌檔案的指定位置之後的內容; 2,master接收到來自slave的io執行緒的請求後,通過負責複製的io執行緒根據請求資訊讀取指定日誌 指定位置之後的日誌資訊,返回給slave端的io執行緒 3,slave的io執行緒接收到資訊後,將接收到的日誌內容一次寫入大slave端的 relay-log 檔案(mysql-relay-bin.xxxxxx)的最末端 4,slave的sql執行緒檢測到relay-Log中新增加了內容後,會立馬解析該log檔案中的新增內容,並執行語句;
主從配置: 1,備份主資料庫,並同步到從資料庫 2,開啟主資料庫bin-log日誌(my.cn) 3,配置從資料庫主資料庫相關資訊
具體步驟: 前提:拷貝主資料庫資料到從資料庫。
1,主3306 my.cnf server-id=1 //資料庫唯一標識 log-bin=/data/3306/log-bin/mysql-bin //二進位制日誌檔案 log-bin-index=/data/3306/log-bin/mysql-bin-index //二進位制索引檔案
2,啟動主資料庫 3,在mysql命令列檢視主資料庫狀態: mysql>show master status; +------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-------------------+ | mysql-bin.000020 | 120 | | | | +------------------+----------+--------------+------------------+-------------------+
4,配置從資料庫 從my.cnf relay-log=/data/3307/relay-log/relay-bin relay-log-index=/data/3307/relay-log/relay-bin-index relay-log-info-file=/data/3307/relay-log/relay-log.info server-id=2
5,啟動從資料庫 6,在從資料庫執行查詢語句: change master to master_host='119.23.19.127', //主資料ip master_port=3306, master_user='root', master_password='root', master_log_file='mysql-bin.000001',//master產生的log-bin 日誌 master_log_pos=0;
change master to master_host='119.23.19.127', master_port=3306, master_user='root', master_password='root', master_log_file='mysql-bin.000001', master_log_pos=0;
6,開啟主從複製 start slave;
show slave status;
============java讀寫分離
xml:
<!-- 配置資料來源 --> <bean name="dataSourceMaster" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> //...... </bean> <!-- 配置資料來源 --> <bean name="dataSourceRead" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url2}" /> <property name="username" value="${jdbc.username2}" /> <property name="password" value="${jdbc.password2}" /> //..... </bean> <!--資料來源選擇器 --> <bean name="routingDataSource" class="com.edu.utils.EduRoutingDataSource"> <!--資料來源集合,spring 會根據傳入key 去找對應資料來源 --> <property name="targetDataSources"> <map> <entry key="dbMaster" value-ref="dataSourceMaster"></entry> <entry key="dbRead" value-ref="dataSourceRead"></entry> </map> </property> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="routingDataSource" /> <!-- 設定欄位為空時,也返回該欄位 --> <property name="configLocation" value="classpath:configuration.xml"></property> <!-- 自動掃描entity目錄, 省掉Configuration.xml裡的手工配置 --> <property name="mapperLocations"> <list> <value>classpath:com/edu/dao/mapperxml/*.xml</value> <value>classpath:com/edu/dao/mapperxml/*/*.xml</value> </list> </property> </bean>
<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="routingDataSource" /> </bean>
DbContext.java /** * 要選擇的資料放進執行緒變數裡 * @author Administrator * */ public class DbContext { private static ThreadLocal<String> dbPools=new ThreadLocal<String>(); public static void set(String db){ dbPools.set(db); } public static String get(){ return dbPools.get(); } }
DbHandler.java import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint; import org.springframework.stereotype.Component;
/** * 資料來源選擇 前置通知 * @author * */ @Component @Aspect public class DbHandler{ /** Description: 配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點;攔截暴露出來的服務類介面public方法<p> void */ @Pointcut("execution(public *..*Service.*(..))") public void face(){ } @Before("face()") public void dbBefore(JoinPoint joinPoint) throws Throwable { MethodInvocationProceedingJoinPoint methodJoinPoint = (MethodInvocationProceedingJoinPoint) joinPoint; String className = methodJoinPoint.getTarget().getClass().getName(); String methodName = methodJoinPoint.getSignature().getName(); Method method; try { Class<?>[] parameterTypes = ((MethodSignature)methodJoinPoint.getSignature()).getMethod().getParameterTypes(); method = methodJoinPoint.getTarget().getClass().getMethod(methodName, parameterTypes); DbReadAnnotation dbAnnotation = method.getAnnotation(DbReadAnnotation.class); if(dbAnnotation==null){ DbContext.set(EduRoutingDataSource.dbMasterDataSource); }else{ DbContext.set(EduRoutingDataSource.dbReadDataSource); } }catch(Exception e){ } } }
EduRoutingDataSource.java import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 資料來源選擇器 * @author Administrator * */ public class EduRoutingDataSource extends AbstractRoutingDataSource { //讀和寫資料來源標識 public static final String dbMasterDataSource ="dbMaster"; //只讀資料來源標識 public static final String dbReadDataSource ="dbRead"; /** * spring 要使用到資料來源時會回撥這個方法,獲取要使用的資料來源 */ @Override protected Object determineCurrentLookupKey() { return DbContext.get()==null?EduRoutingDataSource.dbMasterDataSource:DbContext.get(); }
}
DbReadAnnotation.java
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 資料來源選擇 註解 * 方法打上這標籤都用讀的 資料來源 * @author Administrator * */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface DbReadAnnotation { }