一. 回憶主從複製的一些缺點 上節說到主從複製的一些問題 我們再來回憶一下 主從複製,增加了一個資料庫副本,從資料庫和主資料庫的數據最終會是一致的 之所以說是最終一致,因為mysql複製是非同步的,正常情況下主從複製數據之間會有一個微小的延遲 通過這個資料庫副本看似解決了資料庫單點問題,但並不完美 因 ...
一. 回憶主從複製的一些缺點
上節說到主從複製的一些問題
我們再來回憶一下
主從複製,增加了一個資料庫副本,從資料庫和主資料庫的數據最終會是一致的
之所以說是最終一致,因為mysql複製是非同步的,正常情況下主從複製數據之間會有一個微小的延遲
通過這個資料庫副本看似解決了資料庫單點問題,但並不完美
因為這種架構下,如果主伺服器宕機,需要手動切換從伺服器,業務中斷不能忍受,不能滿足應用高可用的要求
如果才能解決當master伺服器宕機後,前端應用自動切換鏈接呢?
二. 引入vip後的資料庫架構
最簡單的方式就是給資料庫複製集群上增加一個虛擬ip
虛擬IP(vip):
就是一個未分配給真實主機的ip,也就是說對外提供伺服器的主機除了有一個真實IP外還有一個虛擬IP
而前端應用程式使用虛擬ip來鏈接資料庫伺服器,當master宕機後,虛擬ip由masterDB遷移到slaveDB上,這樣就不用人為的修改前端應用的配置了
設置虛擬IP有很多種方法
除了腳本來實現這些功能,還可以通過MHA,MMM這樣的集群管理工具來實現
此處採用keepalived來配置虛擬ip
首先,keepalived可以提供vip,並對主從資料庫的健康狀態進行監控
當主DB宕機時,遷移VIP到主備資料庫伺服器上,這樣就解決了當masterDB宕機時,要手動修改應用伺服器的配置,從新連接到主備伺服器上的問題
但是目前還存在一個問題,當master恢復後,由於當前的寫操作遷移到了從上,所以如果要使用到老的master對新的master(也就是老的slave)進行同步,就不得不重新配置主從複製了,特別是在基於日誌點的複製情況下,這時候如果我們資料庫的數據量非常大,在這種情況下,重新初始化數據也是比較耗時的
所以我們要對主從複製的架構進行一些更改,改變原來的主從複製為主主複製,但一定要保證同一個時間只有一個主提供服務,而另一個主(也就是主備)是處於只讀狀態的,只對外提供讀服務,而不提供寫服務
以前說過InnoDB表的主鍵最好採用自增ID的列,而在主主複製中,為了避免兩個主中同時寫帶來的主鍵衝突,我們需要修改自增主鍵的一些配置,使兩個主上的自增主鍵按照不同的步長進行增長 ,這隻是為了以防萬一採取的一個配置,當我們使用主主複製時,還是要保證在任意時間,均只有一個主可以對外提供服務 ,而另一個主只提供只讀的服務
三. 主主複製配置調整
master 資料庫配置修改
auto_increment_increment = 2 # 控制自增ID增長的步長,預設為1
auto_increment_offset = 1 # 控制自增ID從哪個值開始
這樣修改後,id將會變成1,3,7,9...的形式
主備資料庫配置修改
auto_increment_increment = 2 # 控制自增ID增長的步長,預設為1
auto_increment_offset = 2 # 控制自增ID從哪個值開始
這樣修改後,id將會變成2,4,6,8...的形式
Keepalived簡介
Keepalived基於ARRP網路協議,ARRP可以將2台設備虛擬成一個設備,可對外提供一個虛擬IP,也就是我們架構中的vip,在伺服器內部實際上是擁有虛擬IP的設備如果正常工作的話就是MASTER設備,同一組內其他伺服器不能用這個虛擬IP,狀態是屬於BACKUP狀態,處於BACKUP狀態的設備,除了接收MASTER的ARRP狀態通告之外,不執行任何對外的服務,當主機失效時,backup將接管原先的master的虛擬IP以及對外提供的各項服務
安裝
yum install keepalived -y
keepalived配置
在主和主備伺服器上都要安裝和配置keepalived
配置文件所在位置
/etc/keepalived/keepalived.conf
配置文件中是按模塊劃分的
! Configuration File for keepalived
global_defs {
router_id mysql_ha
}
vrrp_script check_run {
script "/etc/keepalived/check_mysql.sh"
interval 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 200
priority 99
advert_int 1
nopreempt
authentication {
auth_type PASS
auth_pass 1200
}
track_script {
check_run
}
virtual_ipaddress {
192.168.3.99/24
}
}
vrrp_script模塊中:
定義用於檢查mysql是否運行正常的腳本,以及檢查的時間間隔
vrrp_instance 模塊中:
定義了keepalived服務所處的狀態,這個狀態只有BACKUP和MASTER兩個值
interface :定義了綁定 虛擬ip的網路介面,一定要是伺服器上可用的網路介面
virtual_router_id :定義了虛擬服務的id,要取0到255之間的數字
priority :定義了優先順序,值越高,優先順序越大,備份的備設備的值要比主設備低
nopreempt:此值代表不搶占資源,意思是原來的主宕機後,即使再恢復後也不會把虛擬IP搶回來,對於資料庫來說,這樣可以避免主從切換的二次傷害的可能
authentication :配置驗證信息,這個在兩個節點必須是一致的
virtual_ipaddress :定義了使用的虛擬ip信息
四. 演示使用
1. 修改主、主備伺服器配置
[client]
port = 3306 # 客戶端埠號為3306
socket = /home/mysql/data/mysql.sock
[mysqld]
# skip #
skip_name_resolve = 1
skip-external-locking =1
# GENERAL #
user = mysql # MySQL啟動用戶
default_storage_engine = InnoDB # 新數據表的預設數據表類型
character-set-server = utf8 # #服務端預設編碼(資料庫級別)
socket = /home/mysql/data/mysql.sock
pid_file = /home/mysql/data/mysqld.pid
basedir = /home/mysql #使用該目錄作為根目錄(Mysql安裝目錄);
port = 3306
bind-address = 0.0.0.0
log_error_verbosity = 3
explicit_defaults_for_timestamp = off
#sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
#sql_mode = NO_ENGINE_SUBSTITUTION
# undo log
# innodb_undo_directory = /home.mysql/undo
# innodb_undo_tablespaces = 32
# MyISAM #
key_buffer_size =32M
# SAFETY #
max_allowed_packet = 100M
max_connect_errors = 1000000
sysdate_is_now =1
#innodb = FORCE
#innodb_strict_mode = 1
# Replice #
server-id = 100
relay_log = /home/mysql/sql_log/mysqld-relay-bin
#plugin-load = semisync_master.so
log_slave_updates = on
master_info_repository = TABLE
relay_log_info_repository =TABLE
#######主主複製新增的配置###########
auto_increment_increment = 2 # 控制自增ID增長的步長,預設為1
auto_increment_offset = 1 # 控制自增ID從哪個值開始
#####################
# gtid_mode = on
# enforce_gtid_consistency =on
# skip-slave-start =1
#rpl_semi_sync_master_enabled = 1
#rpl_semi_sync_master_timeout=200 # 0.2 second
master_info_respository = TABLE
# gtid_mode= on
# enforce_gtid_consistency = on
# skip-slave-start = 1
# DATA STORAGE #
datadir = /home/mysql/data #mysql 數據文件存放的目錄
tmpdir = /tmp # MySQL存放臨時文件的目錄
# BINARY LOGGING #
log_bin = /home/mysql/sql_log/mysql-bin
max_binlog_size = 1000M
binlog_format = row
expire_log_days = 7
sync_binlog = 1
# CACHES AND LIMITS #
tmp_table_size = 32M
max_heap_table_size = 32M
query_cache_type = 0
auto_increment_increment ,auto_increment_offset 這兩個參數是動態參數,我們可以直接通過命令進行修改,不需要重啟
查看並修改
命令如下
mysql> show variables like 'auto%';
mysql> set global auto_increment_increment=2;
mysql> set global auto_increment_offset =1;
從伺服器配置進行類似修改即可
從伺服器配置
[client]
port = 3306 # 客戶端埠號為3306
socket = /home/mysql/data/mysql.sock
[mysqld]
# skip #
skip_name_resolve = 1
skip-external-locking =1
# GENERAL #
user = mysql # MySQL啟動用戶
default_storage_engine = InnoDB # 新數據表的預設數據表類型
character-set-server = utf8 # #服務端預設編碼(資料庫級別)
socket = /home/mysql/data/mysql.sock
pid_file = /home/mysql/data/mysqld.pid
basedir = /home/mysql #使用該目錄作為根目錄(Mysql安裝目錄);
port = 3306
bind-address = 0.0.0.0
log_error_verbosity = 3
explicit_defaults_for_timestamp = off
#sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
#sql_mode = NO_ENGINE_SUBSTITUTION
read_only = on
# undo log
# innodb_undo_directory = /home.mysql/undo
# innodb_undo_tablespaces = 32
# MyISAM #
key_buffer_size =32M
# SAFETY #
max_allowed_packet = 100M
max_connect_errors = 1000000
sysdate_is_now =1
#innodb = FORCE
#innodb_strict_mode = 1
# Replice #
server-id = 101
relay_log = /home/mysql/sql_log/mysqld-relay-bin
#plugin-load = semisync_master.so
log_slave_updates = on
master_info_repository = TABLE
relay_log_info_repository =TABLE
#######主主複製新增的配置###########
auto_increment_increment = 2 # 控制自增ID增長的步長,預設為1
auto_increment_offset = 2 # 控制自增ID從哪個值開始
#####################
# gtid_mode = on
# enforce_gtid_consistency =on
# skip-slave-start =1
#rpl_semi_sync_master_enabled = 1
#rpl_semi_sync_master_timeout=200 # 0.2 second
master_info_respository = TABLE
# gtid_mode= on
# enforce_gtid_consistency = on
# skip-slave-start = 1
# DATA STORAGE #
datadir = /home/mysql/data #mysql 數據文件存放的目錄
tmpdir = /tmp # MySQL存放臨時文件的目錄
# BINARY LOGGING #
log_bin = /home/mysql/sql_log/mysql-bin
max_binlog_size = 1000M
binlog_format = row
expire_log_days = 7
sync_binlog = 1
# CACHES AND LIMITS #
tmp_table_size = 32M
max_heap_table_size = 32M
query_cache_type = 0
mysql> show variables like 'auto%';
mysql> set global auto_increment_increment=2;
mysql> set global auto_increment_offset =2;
從伺服器查看二進位日誌信息
命令
mysql> show master status \G
可以查看當前使用的日誌文件
File:mysql-bin:000003
和日誌點
Position:25423894
然後去主伺服器上使用change master命令
由於從到主的複製鏈路之前已開啟過,現在只需要配置主到從的
2. 配置啟動主到從的複製鏈路
由於是主主複製 是把當前的主當作從,把當前的從當作主,在主上配置的master_host的值是從的ip,
命令如下:
mysql> change master to master_host='192.168.2.100',
-> master_user='dba_repl',
-> master_password='123456',
->MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=25423894;
mysql> start slave;
3. 在主DB、主備DB的伺服器上分別安裝keepalived
yum install keepalived -y
[root@localhost ~]# cd /etc/keepalived/
[root@localhost keepalived]# ls
check_mysql.sh keepalived.bak keepalived.conf
check_mysql.sh 一定要有執行許可權
chmod a+x check_mysql.sh
4. 主從伺服器分別啟動keepalived
[root@localhost keepalived]# /etc/init.d/keepalived start
啟動成功後,可以通過ip addr命令查看虛擬ip
可以通過手動關閉mysql模式宕機,查看主的ip addr中的虛擬Ip已不存在,此時已虛擬Ip在從伺服器上
五. 目前架構存在的問題
目前架構:
一臺主伺服器,一臺從伺服器,加入了keepalived服務來監控主從伺服器的運行健康狀態,並通過keepalived伺服器生成了一個虛擬IP,前端應用是通過虛擬IP來進行資料庫的訪問,並且為了使主庫宕機後能儘快恢復,把原來的主從複製改為了主主複製
存在的問題:
但是目前的讀寫操作還只是全部通過虛擬IP使用同一臺資料庫伺服器(主伺服器或主備伺服器)來進行訪問的,所以這個架構也沒有解決單台資料庫伺服器讀寫壓力大的問題
對於資料庫伺服器來說,讀負載和寫負載是兩個不同的問題
- 寫操作只能在Master資料庫上執行
- 讀操作既可以在Master庫上執行,也可以在Slave庫上執行
相對於寫負載,解決讀負載要更容易,因為我們可以很容易的得到多個slave伺服器,並且在正常的業務環境中資料庫所執行的讀操作次數要遠遠高於寫操作的次數
同時那些有性能問題的慢查詢,也都是讀操作產生的
六. 如何解決讀壓力大的問題?
- 進行讀寫分離,主伺服器主要執行寫操作
- 讀操作的壓力平均分攤到不同的SLAVE伺服器上
- 增加前端緩存伺服器如Redis,memcache等
推薦使用redis緩存伺服器,redis優點:可持久化,可主從複製,可集群
解決方式見:如何解決MySQL讀壓力大的問題
七. 如何解決寫壓力大的問題
MySQL複製無法緩解寫壓力
利用緩存,合併多次寫為一次寫
緩解寫壓力需要對MasterDB進行拆分
前面資料庫操作規範中說過,程式所使用的資料庫賬號只能在同一個資料庫下進行操作,不允許跨庫查詢資料庫
對MasterDB進行拆分的步驟
MasterDB中有mc_userdb、mc_productdb、mc_orderdb三個資料庫
步驟1:按需求建立新的DB集群
建立好新的資料庫集群後,就可以在新老集群之間建立主從同步關係,把要拆分的DB同步到新的集群中
即 把mc_productdb同步到商品集群中 ,mc_orderdb同步到訂單集群中
步驟2:同步要拆分的DB到新的集群中
當資料庫同步成功後,找一個業務的低峰時間段,比如凌晨3、4點時,把老集群上資料庫的賬號,遷移到新的資料庫集群中
步驟3:遷移資料庫賬號到對應的新集群
遷移成功後,修改老集群的應用在老集群資料庫上的鏈接,把
對mc_productdb和mc_orderdb上的鏈接修改到新的集群上