一. 回憶主從復制的一些缺點
上節說到主從復制的一些問題
我們再來回憶一下
主從復制,增加了一個數據庫副本,從數據庫和主數據庫的數據最終會是一致的
之所以說是最終一致,因為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上的鏈接修改到新的集群上