這麼多TiDB負載均衡方案總有一款適合你

balahoho發表於2021-10-18

【是否原創】是
【首發渠道】TiDB 社群

前言

分散式關係型資料庫TiDB是一種計算和儲存分離的架構,每一層都可以獨立地進行水平擴充套件,這樣就可以做到有的放矢,對症下藥。

從TiDB整體架構圖可以看到,計算層(圖中的TiDB Cluster)負責與外部應用使用MySQL協議通訊,每一個TiDB Server是一個無狀態節點彼此獨立,應用端連線任何一個TiDB節點都可以正常訪問資料庫。這樣做的好處有以下兩點:

  • 簡單增加TiDB節點就可以提升叢集處理能力。

  • 把TP請求和AP請求在計算層分開,各玩各的互不影響。

但這樣帶來的一個問題是,如果叢集有多個TiDB節點,應用端應該訪問哪一個呢?我們不可能在應用端寫多個資料庫連線,A業務訪問TiDB-1,B業務訪問訪問TiDB-2。又或者是習慣了分庫分表讀寫分離的同學會考慮用MyCat這類的資料庫中介軟體來實現請求轉發。

TiDB的誕生初衷就是徹底告別各種複雜的資料庫拆分模式,做到不和資料庫中介軟體強耦合,我們需要的是一個單純的代理來自動實現TiDB節點路由。
這時候,執行在TCP層的負載均衡元件顯然更適合。

TiDB可以用哪些負載均衡元件

經過多方調研和使用,這裡推薦以下三種TiDB負載均衡方案。

首先是HAProxy,它作為一款工作在TCP(四層)和HTTP(七層)的開源代理元件,支援豐富的負載均衡策略以及出色的效能從而使用非常廣泛,它是TiDB官方推薦的負載均衡方式。

其次是LVS,這也是一款鼎鼎大名的負載均衡開源軟體,它工作在網路第四層,流量直接通過Linux核心來處理效能非常高,同時也支援多種負載均衡演算法,但是配置和使用相對比較複雜。

最後要介紹一下TiDB孵化器下的Weir,這個專案最初由伴魚發起並開源,而後PingCAP也加入其中,也是官方推薦的負載均衡之一。除此之外,它還支援SQL審計、監控、多租戶、自適應熔斷限流等高階特性,對這方面有需求的可以優先考慮。和前面兩種不一樣的是,它是工作在第七層應用層的。

另外,像DNS解析或F5這類非軟體層面的負載均衡也可以用在TiDB上,但不在本文的討論範圍。

HAProxy

HAProxy在TiDB中的最佳實踐官網有一篇文章詳細介紹過,手把手教你如何安裝和配置,地址是https://docs.pingcap.com/zh/tidb/dev/haproxy-best-practices

這裡借用裡面的一張圖:

從這張圖裡面可以發現,引入HAProxy後它自身成為了一個單點,一旦出現故障那整個資料庫都無法訪問,那麼搭建高可用負載均衡是不可避免的,這裡我推薦的方式是使用Keepalived+VIP的方式,架構如下圖所示:

Keepalived的工作原理是基於虛擬路由冗餘協議(VRRP)讓兩臺主機繫結同一個虛擬IP,當其中的master節點故障時自動路由到backup節點,無需人工介入。

值得一提的是,bakcup節點在成為master前都是閒置狀態,有一定的資源浪費。

接下來看一下如何操作。

直接通過yum就可以安裝keepalived:

[root@localhost ~]# yum install -y keepalived

[root@localhost ~]# which keepalived
/usr/sbin/keepalived

它的配置檔案在/etc/keepalived/keepalived.conf

分別在兩臺負載均衡節點上安裝keepalived。

在第一臺節點(設定為master)上修改keepalived的配置如下:

! Configuration File for keepalived

global_defs {
   router_id kad_01 #節點標識,要全域性唯一
}

vrrp_instance tidb_ha {
    state MASTER #節點角色
    interface ens192 #這裡設定成自己的網路卡名字,標識繫結到哪個網路卡
    virtual_router_id 51 #虛擬路由id,同一組主備節點要相同
    priority 100 #優先順序,要確保master的優先順序比backup的高
    advert_int 1 #主備之間檢查頻率為1秒
    authentication {
        auth_type PASS
        auth_pass tidb666 #主備之間的認證密碼
    }

    virtual_ipaddress {
        10.3.65.200 #設定繫結的虛擬IP
    }
}

這裡只演示了最基礎的幾個配置,它還可以用更多的配置實現複雜功能,可以參考其他資料實現。

然後啟動服務:

[root@localhost haproxy]# systemctl start keepalived
[root@localhost haproxy]# systemctl status keepalived

可以看到當前keepalived已經是active (running)狀態,虛擬IP已經繫結到了網路卡上:

我們用虛擬IP為入口驗證一下是否能夠登入到TiDB中:

發現登入成功。

用相同的方式,用如下配置檔案在第二個節點啟動keepalived服務:

! Configuration File for keepalived

global_defs {
   router_id kad_02 #節點標識,要全域性唯一
}

vrrp_instance tidb_ha {
    state BACKUP #節點角色
    interface eth0 #這裡設定成自己的網路卡名字,標識繫結到哪個網路卡
    virtual_router_id 51 #虛擬路由id,同一組主備節點要相同
    priority 50 #優先順序,要確保master的優先順序比backup的高
    advert_int 1 #主備之間檢查頻率為1秒
    authentication {
        auth_type PASS
        auth_pass tidb666 #主備之間的認證密碼
    }

    virtual_ipaddress {
        10.3.65.200 #設定繫結的虛擬IP
    }
}

這裡要注意router_idstatepriority要與第一個節點不同。

接著我模擬master節點故障,直接把它關機:

[root@localhost haproxy]# poweroff
Connection closing...Socket close.

Connection closed by foreign host.

Disconnected from remote host(centos-9) at 20:38:16.

Type `help' to learn how to use Xshell prompt.
[C:\~]$ 

退出連線後再次登入,發現依然正常:

另外一點使用HAProxy的彩蛋是,Prometheus官方已經出了HAProxy的exporter,而且從HAProxy 2.0開始已經自己提供了prometheus-exporter,這意味著我們可以很方便的把對HAProxy的監控整合到TiDB的監控體系中。

限於篇幅,以後再介紹詳細。

LVS

LVS支援三種工作模式,每種模式的工作原理這裡不做介紹,大家去參考其他資料,每一種模式的配置方式都不同,我只列出它們之間的對比結果:

我以使用最廣泛的DR模式來介紹配置過程,相比HAProxy它在網路上的配置要複雜一些。

從DR的工作原理可以得知,LVS所在的主機和它後面的真實服務主機都需要繫結同一個虛擬IP。
先配置LVS的主機,複製一份預設網路卡的配置進行修改:

[root@localhost ~]# cd /etc/sysconfig/network-scripts
[root@localhost network-scripts]# cp ifcfg-eth0 ifcfg-eth0:1
[root@localhost network-scripts]# vi ifcfg-eth0:1

TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="static"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
IPADDR=10.3.65.201 # 這裡配置虛擬IP
NETMASK=255.255.255.0
NAME="eth0:1"
DEVICE="eth0:1"
ONBOOT="yes"
~           
[root@localhost network-scripts]# systemctl restart network

接著對TiDB節點進行網路卡配置:

[root@localhost ~]# cd /etc/sysconfig/network-scripts
[root@localhost network-scripts]# cp ifcfg-lo ifcfg-lo:1
[root@localhost network-scripts]# vi ifcfg-lo:1

DEVICE=lo:1
IPADDR=10.3.65.201
NETMASK=255.255.255.255
NETWORK=127.0.0.0
# If you're having problems with gated making 127.0.0.0/8 a martian,
# you can change this to something else (255.255.255.255, for example)
BROADCAST=127.255.255.255
ONBOOT=yes
NAME=loopback
~           
[root@localhost network-scripts]# systemctl restart network
[root@localhost network-scripts]# vi /etc/sysctl.conf # 這裡新增下面顯示的幾條配置
[root@localhost network-scripts]# sysctl -p
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.lo.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
[root@localhost network-scripts]# route add -host 10.3.65.201 dev lo:1

Linux核心從2.4版本開始就已經整合了LVS,所以我們不用單獨安裝了,這裡只需要安裝它的管理工ipvsadmin。

[root@localhost ~]# yum install ipvsadm
[root@localhost ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn

通過ipvsadm新增LVS節點資訊:

[root@localhost ~]# ipvsadm  -A -t 10.3.65.201:4000 -s wrr -p 5 #這裡新增虛擬IP,使用加權輪詢策略

然後把TiDB節點也新增進去:

[root@localhost network-scripts]# ipvsadm -a -t 10.3.65.201:4000 -r 10.3.65.126:4000 -g

這裡的-t引數指定LVS節點虛擬IP,-r引數指定後面的真實服務節點也就是TiDB的訪問埠,如果有多個TiDB節點就每個都加入。

準備就緒以後,我們把當前的資訊儲存到LVS配置檔案中,然後啟動服務即可:

[root@localhost network-scripts]# ipvsadm --save > /etc/sysconfig/ipvsadm
[root@localhost network-scripts]# cat /etc/sysconfig/ipvsadm
-A -t 10.3.65.201:4000 -s wrr
-a -t 10.3.65.201:4000 -r 10.3.65.126:4000 -g -w 1
[root@localhost network-scripts]# systemctl start ipvsadm

啟動以後通過虛擬IP來登入到TiDB中,發現登入成功:

LVS的高可用方案與前面提到的HAProxy類似,都是使用keepalived來實現,為了避免大量重複內容,這裡只給出Master的關鍵配置:

! Configuration File for keepalived

global_defs {
   router_id kad_01 #節點標識,要全域性唯一
}

vrrp_instance tidb_ha {
    state MASTER #節點角色
    interface eth0 #這裡設定成自己的網路卡名字,標識繫結到哪個網路卡
    virtual_router_id 51 #虛擬路由id,同一組主備節點要相同
    priority 100 #優先順序,要確保master的優先順序比backup的高
    advert_int 1 #主備之間檢查頻率為1秒
    authentication {
        auth_type PASS
        auth_pass tidb666 #主備之間的認證密碼
    }

    virtual_ipaddress {
        10.3.65.201 #設定繫結的虛擬IP
    }

    virtual_server 10.3.65.201 4000 {
	    delay_loop 6  #服務輪詢的時間間隔
	    lb_algo wrr  #加權輪詢排程,排程演算法有 rr|wrr|lc|wlc|lblc|sh|sh
	    lb_kind DR   #LVS工作模式 NAT|DR|TUN
	    persistence_timeout 50  #會話保持時間
	    protocol TCP  #健康檢查協議

	    # 真實的後臺服務,這裡配TiDB訪問資訊,每個TiDB節點配置一個real_server
	    real_server 10.3.65.126 4000 {
	        weight 1
	        TCP_CHECK {
	            connect_timeout 3
	            nb_get_retry 3
	            delay_before_retry 3
	            connect_port 4000
	        }
	    }
	}
}

Backup節點配置參考前面的修改即可,更多詳細配置可以去網上搜尋。

Weir

網上關於Weir的資訊並不多,有用的資料都來自Github主頁的文件,Weir的架構和原理可以參考這篇文章:https://asktug.com/t/topic/93717,這裡只總結一下如何去快速使用。

首先從Github拉取原始碼編譯安裝:

[root@localhost ~]# git clone https://github.com/tidb-incubator/weir
[root@localhost ~]# cd weir
[root@localhost ~]# make weirproxy

它涉及到兩個配置檔案,一個和proxy相關(weir預設埠是6000),一個和namespace相關,都在conf目錄下。簡單起見,proxy的配置我不做任何修改都用預設值,只改一下namespace配置裡和TiDB有關的引數:

[root@localhost weir]# vi conf/namespace/test_namespace.yaml

version: "v1"
namespace: "test_namespace"
frontend:
  allowed_dbs:
    - "test"
  slow_sql_time: 50
  sql_blacklist:
  denied_ips:
  idle_timeout: 3600
  users:
    - username: "weir"
      password: "111111"

backend:
  instances:
    - "10.3.65.126:4000"
  username: "root"
  password: ""
  selector_type: "random"
  pool_size: 10
  idle_timeout: 60
~  

詳細的配置引數可以參考官方文件。
修改好配置後執行啟動檔案:

[root@localhost weir]# ./bin/weirproxy &

通過Weir暴露的入口登入到TiDB,發現登入成功:

這裡要注意的是,MySQL客戶端登入時使用的賬號密碼是namespace中設定的資訊,不再是TiDB原本的賬號密碼。

Weir的高可用方案官方介紹的不多,沒有提供原生的高可用支援。從某個角度來看,Weir更像是TiDB SQL層的一個HTTP擴充套件,所以也是無狀態服務,如果部署多個Weir來做叢集的話,那麼它的上層就需要引入像Nginx或HAProxy這樣的負載均衡元件,問題好像一下子回到最開始了。

與前面兩種方案相比,Weir實施起來稍微有點重,不過它勝在支援前面兩種沒有的功能,這就需要大家根據實際情況取捨,還是希望Weir會越做越好。

總結

總體來說,本文介紹的內容都偏TiDB外側的東西,看似關係不大但是又很重要,一旦用了TiDB就不得不考慮負載均衡的問題,希望本文能給大家帶來一些幫助。

相關文章