1、MySQL主從複製原理:java
MySQL的主從複製和MySQL的讀寫分離二者不分家,基於主從複製的架構纔可實現數據的讀寫分離。mysql
一、MySQL支持的複製類型:linux
(1)基於語句的複製。顧名思義就是在主服務器上執行的SQL語句,在從服務器上執行一樣的語句。MySQL默認採用這種方式的複製,效率比較高。
(2)基於行的複製。把改變的內容複製過去,而不是把命令再從主服務器上執行一遍。
(3)混合類型的複製。默認採用基於語句的複製,一旦發現基於語句沒法精確複製時,就會採用基於行的複製。sql
以上三種複製類型,不須要人爲干預,MySQL數據庫會自動控制。數據庫
二、複製的工做過程,以下圖所示:
一、在每一個事物更新數據完成以前,master在二進制日誌記錄這些改變。寫入二進制日誌完成後,master通知存儲引擎提交事務。vim
二、slave將master的Binary log複製到其中繼日誌。首先slave開始一個工做線程——I/O線程,I/O線程在master上打開一個普通的鏈接,而後開始Binlog dump process(Binlog轉儲過程),Binlog dump process從master的二進制日誌中讀取事件,若是已經跟上master,它會睡眠並等待master產生新的事件。I/O線程將這些事件寫入中繼日誌。後端
三、SQL slave thread(SQL從線程)處理該過程的最後一步。SQL線程從中繼日誌讀取事件,並重放其中的事件而更新slave的數據,使其與master中的數據一致。只要該線程與I/O現場曾保持一致,中繼日誌一般存在系統的緩存中,因此中繼日誌的開銷很小。centos
複製過程有一個很重要的限制,就是在slave上的複製是串行化的,master上時並行化的。說白了就是,有可能一些數據更新是多條SQL語句同時在master上進行的,但slave進行復制時,只能一條一條的執行SQL語句進行數據同步。緩存
2、MySQL讀寫分離原理:服務器
簡單來講,就以下圖同樣,就是隻在主服務器上寫,只在從服務器上讀。基本的原理是讓主數據庫處理數據寫入、更新操做,而從數據庫處理select查詢操做。
較爲常見的MySQL讀寫分離分爲兩種:
一、基於程序代碼內部實現:在代碼中根據select、insert語句進行路由分類,這類方法目前在生產環境中應用最普遍。優勢是性能較好,由於在程序代碼中實現,不須要增長額外的設備做爲硬件開支;缺點是須要開發人員來實現,咱們運維無從下手。
二、基於中間代理實現:代理位於客戶端和MySQL服務器之間,代理服務器接到客戶端的請求後經過判斷後轉發到後端數據庫。中間代理有兩個表明性程序:MySQL-Proxy和amoeba(變形蟲)。
兩者區別以下:
MySQL-Proxy是MySQL開源項目,經過自帶的lua腳本進行SQL判斷,雖然是MySQL官方產品,可是MySQL官方並不建議將其應用到生產環境。
amoeba使用Java語言進行開發,阿里巴巴將其用於生產環境,它不支持事務和存儲過程。
雖然經過程序代碼實現MySQL讀寫分離是一個很好的選擇,但並非全部的應用都適合在程序代碼中實現讀寫分離,一些大型複雜的Java應用,若是在程序代碼中實現讀寫分離對代碼的改動就比較大。因此,大型複雜的應用通常都會考慮使用代理層來實現。
3、搭建MySQL主從複製及讀寫分離:
環境以下:
.
準備工做:
一、五臺 centos 7服務器,主從複製的三臺須要安裝MySQL。
二、相關軟件包:
MySQL 軟件包:https://pan.baidu.com/s/1u-gF81Un0ZE5QIIjGwH1Sg 提取碼: 4i6x
amoeba軟件包:https://pan.baidu.com/s/1sHcMTKAPX3A_gulhhaonyw 提取碼: yzbx
三、保證網絡流暢、互通、防火牆放行流量。
MySQL安裝可參考:http://www.javashuo.com/article/p-ktgnavmj-be.html
搭建主從複製(創建時間同步環境):
配置主服務器:
[root@mysql /]# yum -y install ntp # 安裝 ntp [root@mysql /]# vim /etc/ntp.conf ........................ // 省略部份內容 添加以下兩行 server 127.127.1.0 fudge 127.127.1.0 stratum 8 [root@mysql /]# systemctl restart ntpd [root@mysql /]# systemctl enable ntpd [root@mysql /]# vim /etc/my.cnf ............................... server_id = 11 # 修改,ID 要和從服務器區分開來 log_bin = master-bin # 修改 log-slave-updates = true # 添加 [root@mysql /]# systemctl restart mysqld # 重啓服務使配置生效 [root@mysql /]# mysql -u root -p Enter password: # 輸入密碼 mysql> grant replication slave on *.* to 'myslave'@'192.168.1.%' identified by '123123'; mysql> flush privileges; mysql> show master status; +-------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------+----------+--------------+------------------+-------------------+ | master-bin.000001 | 552 | | | | +-------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) # file列顯示日誌名,position 列顯示偏移量,在後面配置從服務器時須要使用
配置從服務器:
[root@mysql /]# yum -y instal ntpdate # 安裝 ntpdate 工具 [root@mysql /]# ntpdate 192.168.1.10 # 進行時間同步 23 Sep 18:06:22 ntpdate[6959]: no server suitable for synchronization found [root@mysql /]# vim /etc/my.cnf ................ server_id = 22 # ID 記得要和主服務器區分開來 relay-log = relay-log-bin # 添加 relay-log-index = slave-relay-bin.index # 添加 [root@mysql /]# systemctl restart mysqld # 重啓使配置生效 [root@mysql /]# mysql -u root -p Enter password: # 登陸 mysql 配置同步 mysql> change master to master_host='192.168.1.10',master_user='myslave',master_password='123123',master_Log_file='master-bin.000001',master_log_pos=552; # 按主服務器的結果更改上面命令中的mater_log_file和mater_log_pos參數 mysql> start slave; # 啓動同步 mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.1.10 Master_User: myslave Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000001 Read_Master_Log_Pos: 552 Relay_Log_File: relay-log-bin.000002 Relay_Log_Pos: 284 Relay_Master_Log_File: master-bin.000001 Slave_IO_Running: Yes # 確保這兩行爲yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB:
在另外一臺從服務器上進行相同的配置,同步至主服務器,這裏我就很少作解釋了
搭建讀寫分離:
在主機 Amoeba 上安裝Java環境:
[root@localhost media]# ls amoeba-mysql-binary-2.2.0.tar.gz jdk-6u14-linux-x64.bin [root@localhost media]# cp * /usr/src/ [root@localhost media]# cd /usr/src/ [root@localhost src]# chmod +x jdk-6u14-linux-x64.bin [root@localhost src]# ./jdk-6u14-linux-x64.bin # 執行以後一直空格鍵,出現yes後輸入yes,回車 .................. Do you agree to the above license terms? [yes or no] yes Press Enter to continue..... # 出現此行回車 Done. # 說明成功了 [root@localhost src]# ls # 此目錄下會多出一個目錄 amoeba-mysql-binary-2.2.0.tar.gz debug jdk1.6.0_14 jdk-6u14-linux-x64.bin kernels [root@localhost src]# mv jdk1.6.0_14/ /usr/local/jdk1.6 [root@localhost src]# vim /etc/profile ....................... # 切到最後一行,輸入如下五行 export JAVA_HOME=/usr/local/jdk1.6 export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib export PATH=$JAVA_HOME/lib:$JAVA_HOME/jre/bin:$PATH:$HOME/bin export AMOEBA_HOME=/usr/local/amoeba export PATH=$PATH:$AMOEBA_HOME/bin [root@localhost src]# source /etc/profile # 執行s使配置生效 [root@localhost /]# java -version # 查看 Java 版本 java version "1.6.0_14" Java(TM) SE Runtime Environment (build 1.6.0_14-b08) Java HotSpot(TM) 64-Bit Server VM (build 14.0-b16, mixed mode)
master、slave一、slave2 中開放權限給 amoeba 訪問:
mysql> grant all on *.* to test@'192.168.1.%' identified by '123.com';
安裝並配置 Amoeba 軟件:
[root@localhost /]# mkdir /usr/local/amoeba [root@localhost /]# cd /usr/src/ [root@localhost src]# tar zxf amoeba-mysql-binary-2.2.0.tar.gz -C /usr/local/amoeba/ [root@localhost /]# chmod -R 755 /usr/local/amoeba/ # 給amoeba目錄權限 [root@localhost /]# /usr/local/amoeba/bin/amoeba amoeba start|stop # 顯示此內容說明安裝成功了 [root@localhost /]# vim /usr/local/amoeba/conf/amoeba.xml # 編輯配置文件 ................................ <property name="authenticator"> <bean class="com.meidusa.amoeba.mysql.server.MysqlClientAuthenticator"> <property name="user">amoeba</property> # 修改客戶端登陸的用戶和密碼(搜索user可到) <property name="password">123456</property> <property name="filter"> <bean class="com.meidusa.amoeba.server.IPAccessController"> ......................................... <property name="sqlFunctionFile">${amoeba.home}/conf/functionMap.xml</property> <property name="LRUMapSize">1500</property> <property name="defaultPool">master</property> # 修改成master <property name="writePool">master</property> # 修改成master <property name="readPool">slaves</property> # 修改成slaves # 以上兩行被註釋過,須要將註釋去掉 <property name="needParse">true</property> </queryRouter>
編輯dbServers/xml文件
[root@localhost /]# vim /usr/local/amoeba/conf/dbServers.xml ............................ <!-- mysql user --> <property name="user">test</property> # 修改成test <property name="password">123.com</property> # 設置密碼,將註釋去掉 </factoryConfig> .............................. <dbServer name="master" parent="abstractServer"> # 修更名稱 <factoryConfig> <!-- mysql ip --> <property name="ipAddress">192.168.1.10</property> # 設置主機IP </factoryConfig> </dbServer> <dbServer name="slave1" parent="abstractServer"> <factoryConfig> <!-- mysql ip --> <property name="ipAddress">192.168.1.20</property> </factoryConfig> </dbServer> <dbServer name="slave2" parent="abstractServer"> <factoryConfig> <!-- mysql ip --> <property name="ipAddress">192.168.1.30</property> </factoryConfig> </dbServer> <dbServer name="slaves" virtual="true"> <poolConfig class="com.meidusa.amoeba.server.MultipleServerPool"> <!-- Load balancing strategy: 1=ROUNDROBIN , 2=WEIGHTBASED , 3=HA--> <property name="loadbalance">1</property> <!-- Separated by commas,such as: server1,server2,server1 --> <property name="poolNames">slave1,slave2</property> </poolConfig> </dbServer> [root@localhost /]# /usr/local/amoeba/bin/amoeba start& # 啓動服務
接下來開始測試:
在client主機上安裝mysql:
[root@localhost /]# yum -y install mysql [root@localhost /]# mysql -u amoeba -p123456 -h 192.168.1.40 -P8066 # 登陸訪問
# 在master主機test庫中建立一個表,同步到各從服務器上 mysql> create table zang (id int(10),name varchar(10)); # 而後關掉各從服務器上的slave功能,並插入區別語句 mysql> insert into zang values('3','wangwu'); # 主服務器上 mysql> stop slave; # 關閉slave功能 mysql> insert into zang values('1','zhangsan'); # 從服務器一 mysql> insert into zang values('2','lisi'); # 從服務器二
測試讀操做:
MySQL [test]> select * from zang; # 第一次查詢 +------+----------+ | id | name | +------+----------+ | 1 | zhangsan | +------+----------+ 1 row in set (0.05 sec) MySQL [test]> select * from zang; # 第二次查詢 +------+------+ | id | name | +------+------+ | 2 | lisi | +------+------+ 1 row in set (0.00 sec) MySQL [test]> select * from zang; # 第三次查詢 +------+----------+ | id | name | +------+----------+ | 1 | zhangsan | +------+----------+ 1 row in set (0.00 sec)
測試寫操做:
client 主機上插入一條數據:
MySQL [test]> insert into zang values('4','zholiu'); Query OK, 1 row affected (0.01 sec)
MySQL [test]> select * from zang; # 在client上查不到 +------+------+ | id | name | +------+------+ | 2 | lisi | +------+------+ mysql> select * from zang; # 只在master主服務上查到了,從服務器由於關閉了salve功能因此同步不到 +------+----------+ | id | name | +------+----------+ | 3 | wangwu | | 4 | zholiu | +------+----------+ 3 rows in set (0.01 sec)
這樣,就實現了 MySQL 的讀寫分離,全部寫操做所有在 master主服務器上,用來避免數據的不一樣步,全部讀操做都分攤給了 從服務器,用來分擔服務器的壓力。