『網際網路架構』軟體架構-mysql主從(二)
通俗來講,如果對資料庫的讀和寫都在同一個資料庫伺服器中操作,業務系統性能會降低。 為了提升業務系統性能,優化使用者體驗,可以通過做主從複製(讀寫分離)來減輕主資料庫的負載。 而且如果主資料庫宕機,可快速將業務系統切換到從資料庫上,可避免資料丟失。原始碼:https://github.com/limingios/netFuture
為什麼要主從同步
- 實時災備,用於故障切換
- 讀寫負載均衡
- 定時任務專用
- 開發人員檢視
瞭解原理
下面這個圖,就是主從同步的原理。
- webapp(就是java的應用程式)往主庫mysqld內進行寫。
1.1 丟到mysql的data節點
1.2 資料的語句丟到binlogs裡(開啟主從同步一定要開啟binlogs)除了select查詢語句都不在記錄啦。
1.3 主節點mysqld有個過濾器 ,可以指定那些資料庫生成對應的binlogs。那些需要實時同步,那些不需要。
1.4 binlogs 如果一直生成最後硬碟會不會爆掉,不會的可以設定binlogs的有效期,指定什麼之前自動失效,它就會自動刪除。 - 從庫生成兩個執行緒,一個I/O執行緒,一個SQL執行緒。
2.1 i/o執行緒去請求主庫 的binlog,並將得到的binlog日誌寫到relay log(中繼日誌) 檔案中。
2.2 relay log 被sql thread讀又生成了binlog 和 data
2.3 主庫會生成一個 log dump 執行緒,用來給從庫 i/o執行緒傳binlog。
2.4 slave裡面的生成的binlog (可以開啟,或者關閉)可以被另外的slave 進行讀取。形成一個鏈式的。 - 同步的原理,binlog有個節點(position)當主從不一致的時候,slave 就要去拿了資料了。
問題及解決方法
mysql主從複製存在的問題:
1. 主庫宕機後,資料可能丟失
2. 從庫只有一個sql Thread,主庫寫壓力大,複製很可能延時
解決
* 半同步複製—解決資料丟失的問題(主節點插入前一條語句的時候,binlogs已經達到了slave的relay binlog)
1. 5.5整合到mysql,以外掛的形式存在,需要單獨安裝
2. 確保事務提交後binlog至少傳輸到一個從庫
3. 不保證從庫應用完這個事務的binlog
4. 效能有一定的降低,響應時間會更長
5. 網路異常或從庫宕機,卡主主庫,直到超時或從庫恢復
-
問題:當slave掛了時候,master一直通訊,可能導致整個系統都蹦了
解決方案:
- master等待通知的超時時間是10秒,如果10秒slave同步的標識還沒到,我該繼續插入到本地的master的data就插入。
- 但是很多公司都是設定1秒,因為很多公司99%的沒問題的,但是1秒這1%,就有問題了。這個設定根據業務來。架構師的水平和經驗了。
一致性和同步時間本身就是雙刃劍,沒有完全的通用解決方案,只能通過業務和效能綜合考量選擇最優解。
master和slave節點享受一致性的時候,比喻下:master是皇帝,下面很多的slave皇子,但是slave中肯定有一名是太子,當master駕崩了,其中一個太子就程式設計皇帝了。
網際網路主流的主從的方案
- 一主一從,小公司使用比較多,並沒有進行讀寫分離,slave節點主要是熱備的。出現問題很快的恢復。記得當初有個同事無意中刪除了一個很重要的表的資料,可能刪除了20多條,導致半月都沒好過,資料一致性的問題一直在解決。手工一條一條的找到。
- 一主多從,小公司使用比較多,一般情況下都是一個master節點三到4個slave的節點, 3個slave節點中其中一個是太子,可以保證資料的一致性,
- 雙主,比較變態,一般用不到,寫效能非常高的時候可能會用雙主,mysql是允許,北京的寫在一個庫,上海的寫在一個庫,說實話不推薦。
- 級聯同步,一個master一個slave(太子)節點多個slave節點,一個master如果下面不是級聯的既要做讀又要做寫,還要做同步,master的節點壓力很大。級聯就是讓其中一個slave做同步,給master節點的壓力減少了,也達到了主從同步的問題。這個也有問題,如果太子掛掉了,後面的皇子沒有領頭羊了都變成單機了。
- 主主從,也就是2個主互訪的,用後面的主來同步slave,但是如果後面的master掛掉的話,後面的slave也和級聯同步的情況一致也尷尬了。
- 環形多主,非常變態,一定不推薦。寫的併發是超級超級高的,沒辦法才這樣玩,全國31省,31個master,每個省一個master,如果其中一個master掛掉,整個環形歇菜了。master下面也可以加入slave,估計超大型的才會用到。但是目前也不會這麼用了。
介紹主流的一主多從的方案
如何實現,master就負責寫,slave就負責讀,需要一個負載均衡的解決方案。這裡說下atlas。場景如果在商城生成一個訂單,立馬就要讀這個訂單,按照傳統的方案我應該讀slave庫,但是這時候slave還沒完成同步。這是不是很尷尬,解決方案就是atlas,如果你生成了訂單在master,立馬需要讀的話,只需要在sql語句中加入一個註解,就可以直接去master裡面讀了。
Atlas是由 Qihoo 360公司Web平臺部基礎架構團隊開發維護的一個基於MySQL協議的資料中間層專案。它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基礎上,修改了大量bug,添加了很多功能特性。目前該專案在360公司內部得到了廣泛應用,很多MySQL業務已經接入了Atlas平臺,每天承載的讀寫請求數達幾十億條。同時,有超過50家公司在生產環境中部署了Atlas,超過800人已加入了我們的開發者交流群,並且這些數字還在不斷增加。
Atlas官方連結: https://github.com/Qihoo360/Atlas/blob/master/README_ZH.md
Atlas下載連結: https://github.com/Qihoo360/Atlas/releases
vagrant生成虛擬機器
通過原始碼生成3個虛擬機器,準備工作。vagrant已經安裝了 對應的docker。用docker安裝nexus就是為了避免環境變數,使用者賦權等複雜的操作。對於vagrant的如何安裝不用的系統不一樣可以參看
mac 安裝vgarant :https://idig8.com/2018/07/29/docker-zhongji-07/
window安裝vgaranthttps://idig8.com/2018/07/29/docker-zhongji-08/
系統型別 | IP地址 | 節點角色 | CPU | Memory | Hostname |
---|---|---|---|---|---|
Centos7 | 192.168.66.101 | atlas-proxy | 2 | 2G | atlas-proxy |
Centos7 | 192.168.66.102 | master | 2 | 2G | master |
Centos7 | 192.168.66.103 | slave | 2 | 2G | slave |
- 三臺機器window/mac開通遠端登入root使用者下
su - # 密碼 vagrant #設定 PasswordAuthentication yes vi /etc/ssh/sshd_config sudo systemctl restart sshd
-
關閉、禁用防火牆(讓所有機器之間都可以通過任意埠建立連線)
>實際生產的環境,根據實際開通指定埠
systemctl stop firewalld systemctl disable firewalld #檢視狀態 systemctl status firewalld
102(master),103(slave)配置
拉取映象
docker pull mysql:5.7
102(master)配置
- 配置目錄
mkdir -p /usr/local/mysqlData/master/cnf mkdir -p /usr/local/mysqlData/master/data cd /usr/local/mysqlData/master/cnf vi mysql.cnf
- 配置mysql.cnf
[mysqld] pid-file= /var/run/mysqld/mysqld.pid socket= /var/run/mysqld/mysqld.sock datadir= /var/lib/mysql symbolic-links=0 character-set-server = utf8 #skip-networking innodb_print_all_deadlocks = 1 max_connections = 2000 max_connect_errors = 6000 open_files_limit = 65535 table_open_cache = 128 max_allowed_packet = 4M binlog_cache_size = 1M max_heap_table_size = 8M tmp_table_size = 16M read_buffer_size = 2M read_rnd_buffer_size = 8M sort_buffer_size = 8M join_buffer_size = 28M key_buffer_size = 4M thread_cache_size = 8 query_cache_type = 1 query_cache_size = 8M query_cache_limit = 2M ft_min_word_len = 4 log-bin = mysql-bin server-id = 1 binlog_format = mixed performance_schema = 0 explicit_defaults_for_timestamp #lower_case_table_names = 1 interactive_timeout = 28800 wait_timeout = 28800 # Recommended in standard MySQL setup sql_mode=NO_ENGINE_SUBSTITUTION,NO_AUTO_CREATE_USER,STRICT_TRANS_TABLES [mysqldump] quick max_allowed_packet = 16M [myisamchk] key_buffer_size = 8M sort_buffer_size = 8M read_buffer = 4M write_buffer = 4M
- 啟動容器
docker images docker run -itd -p 3306:3306 --name master \ -v/usr/local/mysqlData/master/cnf:/etc/mysql/conf.d \ -v /usr/local/mysqlData/master/data:/var/lib/mysql\ -e MYSQL_ROOT_PASSWORD=masterpwd ae6b78bedf88 docker container ls
- 進入容器開啟遠端訪問
docker container ls docker exec -it e2ca4d3cd633 /bin/bash mysql -uroot -pmasterpwd
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'masterpwd' WITH GRANT OPTION; GRANT REPLICATION SLAVE ON *.* to 'reader'@'%' identified by 'readerpwd'; FLUSH PRIVILEGES; show master status;
103(slave)配置
- 配置目錄
mkdir -p /usr/local/mysqlData/slave/cnf mkdir -p /usr/local/mysqlData/slave/data cd /usr/local/mysqlData/slave/cnf vi mysql.cnf
- 配置mysql.cnf
[mysqld] pid-file= /var/run/mysqld/mysqld.pid socket= /var/run/mysqld/mysqld.sock datadir= /var/lib/mysql symbolic-links=0 character-set-server = utf8 #skip-networking innodb_print_all_deadlocks = 1 max_connections = 2000 max_connect_errors = 6000 open_files_limit = 65535 table_open_cache = 128 max_allowed_packet = 4M binlog_cache_size = 1M max_heap_table_size = 8M tmp_table_size = 16M read_buffer_size = 2M read_rnd_buffer_size = 8M sort_buffer_size = 8M join_buffer_size = 28M key_buffer_size = 4M thread_cache_size = 8 query_cache_type = 1 query_cache_size = 8M query_cache_limit = 2M ft_min_word_len = 4 log-bin = mysql-bin server-id = 2 binlog_format = mixed performance_schema = 0 explicit_defaults_for_timestamp #lower_case_table_names = 1 interactive_timeout = 28800 wait_timeout = 28800 # Recommended in standard MySQL setup sql_mode=NO_ENGINE_SUBSTITUTION,NO_AUTO_CREATE_USER,STRICT_TRANS_TABLES [mysqldump] quick max_allowed_packet = 16M [myisamchk] key_buffer_size = 8M sort_buffer_size = 8M read_buffer = 4M write_buffer = 4M
- 啟動容器
docker images docker run -itd -p 3307:3306 --name slave1 \ -v /usr/local/mysqlData/slave/cnf:/etc/mysql/conf.d \ -v /usr/local/mysqlData/slave/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=slavepwd ae6b78bedf88 docker container ls
-
進入容器開啟遠端訪問
>建立遠端連線使用者,並賦予查詢資料庫,以及查詢的許可權,可以用於讀寫分離。
docker container ls docker exec -it c380d2b5f6cf /bin/bash mysql -uroot -pslavepwd
grant SHOW DATABASES,SELECT on *.* to 'slave'@'%' identified by 'slavepwd'; FLUSH PRIVILEGES;
進入102master的mysql容器
檢視主庫 show master status
show master status
進入103slave的mysql容器
配置主從,剛才102的status的postion=889 這裡也要配置成一致的。
master_log_file 也需要跟主庫保持一致
change master to master_host='192.168.66.102',master_user='reader',master_password='readerpwd',master_log_file='mysql-bin.000003',master_log_pos=889;
#啟動主從 start slave; show slave status\G
建立一個數據庫自動同步完成
Atlas 代理配置(需要才用,不需要上邊就足夠了)
如果你想使用Atlas代理,就需要102的mysql密碼 和103的mysql密碼保持一致,原因是360好久不維護了,提問裡面有人遇見過用多個pwds的時候直接出現亂碼。103(slave)配置(配置目錄,配置mysql.cnf 還是保持不變)
- 重新刪除103的slave的mysql建立新的
cd /usr/local/mysqlData/slave/data rm -rf * docker ps docker rm -f c380d2b5f6cf #這裡的slave103的密碼跟master102的密碼一致 docker run -itd -p 3307:3306 --name slave1 \ -v /usr/local/mysqlData/slave/cnf:/etc/mysql/conf.d \ -v /usr/local/mysqlData/slave/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=masterpwd ae6b78bedf88 docker ps
- 修改資料庫許可權
docker ps docker exec -it e698f50d7ece /bin/bash mysql -uroot -pmasterpwd
- 102 進入mysql檢視position
#一定要記牢了,slave同步的時候要用,如果不一致就無法同步啦 show master status;
- 103 進入容器同步
docker ps docker exec -it6djasdad /bin/bash GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'masterpwd' WITH GRANT OPTION; FLUSH PRIVILEGES; change master to master_host='192.168.66.102',master_user='root',master_password='masterpwd',master_log_file='mysql-bin.000003',master_log_pos=1047; #啟動主從 start slave; show slave status\G
在slave主機上這2個都為yes才算配置成功。

- 進入101 主機開始配置代理
wget https://github.com/Qihoo360/Atlas/releases/download/2.2.1/Atlas-2.2.1.el6.x86_64.rpm rpm -ivh Atlas-2.2.1.el6.x86_64.rpm
- 密碼加密,配置檔案需要
cd /usr/local/mysql-proxy/ cd bin ./encrypt masterpwd #下面就需要這個密碼 test.cnf裡面需要 XYx4FmVBYrXmTh762ogNww==
- 配置atlas檔案
vi /usr/local/mysql-proxy/conf/test.cnf
這裡pwds 對應的密碼 就是上一步生成的,log-level設定成了debug目的好測試。sql-log=REALTIME可以檢視sql的走向。
[mysql-proxy] #帶#號的為非必需的配置專案 #管理介面的使用者名稱 admin-username = user #管理介面的密碼 admin-password = pwd #Atlas後端連線的MySQL主庫的IP和埠,可設定多項,用逗號分隔 proxy-backend-addresses = 192.168.66.102:3306 #Atlas後端連線的MySQL從庫的IP和埠,@後面的數字代表權重,用來作負載均衡,若省略則預設為1,可設定多項,用逗號分隔 proxy-read-only-backend-addresses = 192.168.66.103:3307@1 #使用者名稱與其對應的加密過的MySQL密碼,密碼使用PREFIX/bin目錄下的加密程式encrypt加密,下行的user1和user2為示例,將其替換為你的MySQL的使用者名稱和加密密碼! pwds = root:XYx4FmVBYrXmTh762ogNww== #設定Atlas的執行方式,設為true時為守護程序方式,設為false時為前臺方式,一般開發除錯時設為false,線上執行時設為true,true後面不能有空格。 daemon = true #設定Atlas的執行方式,設為true時Atlas會啟動兩個程序,一個為monitor,一個為worker,monitor在worker意外退出後會自動將其重啟,設為false時只有worker,沒有monitor,一般開發除錯時設為false,線上執行時設為true,true後面不能有空格。 keepalive = true #工作執行緒數,對Atlas的效能有很大影響,可根據情況適當設定 event-threads = 8 #日誌級別,分為message、warning、critical、error、debug五個級別 log-level = debug #日誌存放的路徑 log-path = /usr/local/mysql-proxy/log #SQL日誌的開關,可設定為OFF、ON、REALTIME,OFF代表不記錄SQL日誌,ON代表記錄SQL日誌,REALTIME代表記錄SQL日誌且實時寫入磁碟,預設為OFF sql-log = REALTIME #慢日誌輸出設定。當設定了該引數時,則日誌只輸出執行時間超過sql-log-slow(單位:ms)的日誌記錄。不設定該引數則輸出全部日誌。 #sql-log-slow = 10 #例項名稱,用於同一臺機器上多個Atlas例項間的區分 #instance = test #Atlas監聽的工作介面IP和埠 proxy-address = 0.0.0.0:1234 #Atlas監聽的管理介面IP和埠 admin-address = 0.0.0.0:2345 #分表設定,此例中person為庫名,mt為表名,id為分表字段,3為子表數量,可設定多項,以逗號分隔,若不分表則不需要設定該項 #tables = person.mt.id.3 #預設字符集,設定該項後客戶端不再需要執行SET NAMES語句 #charset = utf8 #允許連線Atlas的客戶端的IP,可以是精確IP,也可以是IP段,以逗號分隔,若不設定該項則允許所有IP連線,否則只允許列表中的IP連線 #client-ips = 127.0.0.1, 192.168.1 #Atlas前面掛接的LVS的物理網絡卡的IP(注意不是虛IP),若有LVS且設定了client-ips則此項必須設定,否則可以不設定 #lvs-ips = 192.168.1.1
- 啟動atlas
cd /usr/local/mysql-proxy/bin/ # test 配置檔案的名稱 start 啟動,stop 停止 ./mysql-proxyd test start
監聽埠預設是1234,我沒修改,如果需要修改直接改test.cnf
- 檢視日誌準備測試
cd /usr/local/mysql-proxy/log tail -f sql_test.log
終於搞定了這個圖
另外說一點,之前遇見的一個坑,當在專案中使用框架mybatis連資料庫時,卻都直接去主庫讀寫資料了。偏偏添加了事務@Transactional
解決辦法在方法上加@Transactional(propagation=Propagation.NOT_SUPPORTED)即可。
PS:其實很多公司都是通過代理的方式來管理主從資料庫的。它可以有選擇控制從哪個資料庫走。感覺挺爽的,102走的是insert,103走的是select。
>>原創文章,歡迎轉載。轉載請註明:轉載自IT人故事會,謝謝!
>>原文連結地址:上一篇:已是最新文章