1. 程式人生 > >周記 8

周記 8

打開 特性 compile script 內置 發現 ans 內存管理 增量

  hexo太不好用了 各種 出錯 就在這裏寫自己的周記吧!

# PHP的一些問題

## PHP-FPM 運行模式

php-fpm的進程數可以根據設置分為動態和靜態。

* 靜態:直接開啟指定數量的php-fpm進程,不再增加或者減少;


* 動態:開始的時候開啟一定數量的php-fpm進程,當請求量變大的時候,動態的增加php-fpm進程數到上限,當空閑的時候自動釋放空閑的進程數到一個下限。

這裏先說一下涉及到這個的幾個參數吧,他們分別是pm、pm.max_children、pm.start_servers、pm.min_spare_servers和pm.max_spare_servers。

pm表示使用那種方式,有兩個值可以選擇,就是static(靜態)或者dynamic(動態)。在更老一些的版本中,dynamic被稱作apache-like。這個要註意看配置文件給出的說明了。

下面4個參數的意思分別為:

* pm.max_children:靜態方式下開啟的php-fpm進程數量。
* pm.start_servers:動態方式下的起始php-fpm進程數量。
* pm.min_spare_servers:動態方式下的最小php-fpm進程數量。
* pm.max_spare_servers:動態方式下的最大php-fpm進程數量。

那麽,對於我們的服務器,選擇哪種執行方式比較好呢?事實上,跟Apache一樣,我們運行的PHP程序在執行完成後,或多或少會有內存泄露的問題。這也是為什麽開始的時候一個php-fpm進程只占用3M左右內存,運行一段時間後就會上升到20-30M的原因了。所以,動態方式因為會結束掉多余 的進程,可以回收釋放一些內存,所以推薦在內存較少的服務器或者VPS上使用。具體最大數量根據 內存/20M 得到。比如說512M的VPS,建議pm.max_spare_servers設置為20。至於pm.min_spare_servers,則建議根據服務器的負載情況來設置,比較合適的值在5~10之間。

然後對於比較大內存的服務器來說,設置為靜態的話會提高效率。因為頻繁開關php-fpm進程也會有時滯,所以內存夠大的情況下開靜態效果會更好。數量也可以根據 內存/30M 得到。比如說2GB內存的服務器,可以設置為50;4GB內存可以設置為100等。

Nginx不支持對外部程序的直接調用或者解析,所有的外部程序(包括PHP)必須通過FastCGI接口來調用。FastCGI接口在Linux下是socket(這個socket可以是文件socket,也可以是ip socket)。為了調用CGI程序,還需要一個FastCGI的wrapper(wrapper可以理解為用於啟動另一個程序的程序),這個wrapper綁定在某個固定socket上,如端口或者文件socket。當Nginx將CGI請求發送給這個socket的時候,通過FastCGI接口,wrapper接收到請求,然後派生出一個新的線程,這個線程調用解釋器或者外部程序處理腳本並讀取返回數據;接著,wrapper再將返回的數據通過FastCGI接口,沿著固定的socket傳遞給Nginx;最後,Nginx將返回的數據發送給客戶端。


總結一下 fpm運行是多線程模型 分為靜態和動態兩種模式

靜態更加適合內存良好的機器

動態更適合內存小的機器

靜態的分配原理是 機器內存 / 30

動態的分配原則是 機器內存 / 20

動態能動態的開閉worker 可以避免一些 內存泄漏問題~

## OPcache

PHP(本文所述案例PHP版本均為7.1.3)作為一門動態腳本語言,其在zend虛擬機執行過程為:讀入腳本程序字符串,經由詞法分析器將其轉換為單詞符號,接著語法分析器從中發現語法結構後生成抽象語法樹,再經靜態編譯器生成opcode,最後經解釋器模擬機器指令來執行每一條opcode。

在上述整個環節中,生成的opcode可以應用編譯優化技術如死代碼刪除、條件常量傳播、函數內聯等各種優化來精簡opcode,達到提高代碼的執行性能的目的。

PHP擴展opcache,針對生成的opcode基於共享內存支持了緩存優化。在此基礎上又加入了opcode的靜態編譯優化。這裏所述優化通常采用優化器(Optimizer)來管理,編譯原理中,一般用優化遍(Opt pass)來描述每一個優化。

整體上說,優化遍分兩種:

一種是分析pass,是提供數據流、控制流分析信息為轉換pass提供輔助信息;

一種是轉換pass,它會改變生成代碼,包括增刪指令、改變替換指令、調整指令順序等,通常每一個pass前後可dump出生成代碼的變化。

本文基於編譯原理,結合opcache擴展提供的優化器,以PHP編譯基本單位op_array、PHP執行最小單位opcode為出發點。介紹編譯優化技術在Zend虛擬機中的應用,梳理各個優化遍是如何一步步優化opcode來提高代碼執行性能的。最後結合PHP語言虛擬機執行給出幾點展望。

OPcache 緩存的最小粒度 我認為是 單個PHP 文件

使用下列推薦設置來獲得較好的性能:


opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
opcache.enable_cli=1
opcache.save_comments=0

你也可以禁用 opcache.save_comments 並且啟用 opcache.enable_file_override。 需要提醒的是,在生產環境中使用上述配置之前,必須經過嚴格測試。 因為上述配置存在一個已知問題,它會引發一些框架和應用的異常, 尤其是在存在文檔使用了備註註解的時候。


### 靜態編譯 解釋執行 即時編譯

靜態編譯(static compilation),也稱事前編譯(ahead-of-time compilation),簡稱AOT。即把源代碼編譯成目標代碼,執行時在支持目標代碼的平臺上運行。

動態編譯(dynamic compilation),相對於靜態編譯而言,指”在運行時進行編譯”。通常情況下采用解釋器(interpreter)編譯執行,它是指一條一條的解釋執行源語言。

JIT編譯(just-in-time compilation),即即時編譯,狹義指某段代碼即將第一次被執行時進行編譯,而後則不用編譯直接執行,它為動態編譯的一種特例。 PHP8 HHVM


### OP_ARRAY

類似於C語言的棧幀(stack frame)概念,即一個運行程序的基本單位(一幀),一般為一次函數調用的基本單位。此處,一個函數或方法、整個PHP腳本文件、傳給eval表示PHP代碼的字符串都會被編譯成一個op_array。

實現上op_array為一個包含程序運行基本單位的所有信息的結構體,當然opcode數組為該結構最為重要的字段,不過除此之外還包含變量類型、註釋信息、異常捕獲信息、跳轉信息等。

## OPCODE

解釋器執行(ZendVM)過程即是執行一個基本單位op_array內的最小優化opcode,按順序遍歷執行,執行當前opcode,會預取下一條opcode,直到最後一個RETRUN這個特殊的opcode返回退出。

這裏的opcode某種程度也類似於靜態編譯器裏的中間表示(類似於LLVM IR),通常也采用三地址碼的形式,即包含一個操作符,兩個操作數及一個運算結果。其中兩個操作數均包含類型信息。此處類型信息有五種,分別為:

編譯變量(Compiled Variable,簡稱CV),編譯時變量即為php腳本中定義的變量。

內部可重用變量(VAR),供ZendVM使用的臨時變量,可與其它opcode共用。

內部不可重用變量(TMP_VAR),供ZendVM使用的臨時變量,不可與其它opcode共用。

常量(CONST),只讀常量,值不可被更改。

無用變量(UNUSED)。由於opcode采用三地址碼,不是每一個opcode均有操作數字段,缺省時用該變量補齊字段。

類型信息與操作符一起,供執行器匹配選擇特定已編譯好的C函數庫模板,模擬生成機器指令來執行。

參考資料:https://tech.youzan.com/understanding-opcode-optimization-in-php/

## PHP 腳本執行過程

PHP -> lex -> tokens -> parse -> simple 表達式 -> compile -> opcode -> zendvm -> exec

1)Scanning(Lexing) ,將PHP代碼轉換為語言片段(Tokens)。

2)Parsing, 將Tokens轉換成簡單而有意義的表達式。

3)Compilation, 將表達式編譯成Opocdes。

4)Execution, 順次執行Opcodes,每次一條,從而實現PHP腳本的功能。

## PHP內存模型與執行模型

內存管理一般會包括以下內容:

是否有足夠的內存供我們的程序使用;

如何從足夠可用的內存中獲取部分內存;

對於使用後的內存,是否可以將其銷毀並將其重新分配給其它程序使用。

PHP底層對內存的管理, 圍繞著小塊內存列表(free_buckets)、 大塊內存列表(large_free_buckets)和剩余內存列表(rest_buckets)三個列表來分層進行的。 ZendMM向系統進行的內存申請,並不是有需要時向系統即時申請,而是由ZendMM的最底層(heap層)先向系統申請一大塊的內存,通過對上面三種列表的填充,建立一個類似於內存池的管理機制。 在程序運行需要使用內存的時候,ZendMM會在內存池中分配相應的內存供使用。這樣做的好處是避免了PHP向系統頻繁的內存申請操作

PHP對內存的分配,是結合PHP的用途來設計的,PHP一般用於web應用程序的數據支持,單個腳本的運行周期一般比較短(最多達到秒級),內存大塊整塊的申請,自主進行小塊的分配, 沒有進行比較復雜的不相臨地址的空閑內存合並,而是集中再次向系統請求。 這樣做的好處就是運行速度會更快,缺點是隨著程序的運行時間的變長,內存的使用情況會“越來越多”(PHP5.2及更早版本)。 所以PHP5.3之前的版本並不適合做為守護進程長期運行。

https://www.cnblogs.com/phpworld/p/5916560.html

PHP從下到上是一個4層體系:

Zend引擎:Zend整體用純C實現,是PHP的內核部分,它將PHP代碼翻譯(詞法、語法解析等一系列編譯過程)為可執行opcode的處理並實現相應的處理方法、實現了基本的數據結構(如hashtable、oo)、內存分配及管理、提供了相應的api方法供外部調用,是一切的核心,所有的外圍功能均圍繞Zend實現。

Extensions:圍繞著Zend引擎,extensions通過組件式的方式提供各種基礎服務,我們常見的各種內置函數(如array系列)、標準庫等都是通過extension來實現,用戶也可以根據需要實現自己的extension以達到功能擴展、性能優化等目的(如貼吧正在使用的PHP中間層、富文本解析就是extension的典型應用)。

Sapi:Sapi全稱是Server Application Programming Interface,也就是服務端應用編程接口,Sapi通過一系列鉤子函數,使得PHP可以和外圍交互數據,這是PHP非常優雅和成功的一個設計,通過sapi成功的將PHP本身和上層應用解耦隔離,PHP可以不再考慮如何針對不同應用進行兼容,而應用本身也可以針對自己的特點實現不同的處理方式。

上層應用:這就是我們平時編寫的PHP程序,通過不同的sapi方式得到各種各樣的應用模式,如通過webserver實現web應用、在命令行下以腳本方式運行等等。

如果PHP是一輛車,那麽車的框架就是PHP本身,Zend是車的引擎(發動機),Ext下面的各種組件就是車的輪子,Sapi可以看做是公路,車可以跑在不同類型的公路上,而一次PHP程序的執行就是汽車跑在公路上。因此,我們需要:性能優異的引擎+合適的車輪+正確的跑道。

PHP53的 gc 改進

http://www.cnblogs.com/leoo2sk/archive/2011/02/27/php-gc.html

是因為一個zval在一個時刻只能表示一種類型的變量。可以看到_zvalue_value中只有5個字段,但是PHP中算上NULL有8種數據類型,那麽PHP內部是如何用5個字段表示8種類型呢?這算是PHP設計比較巧妙的一個地方,它通過復用字段達到了減少字段的目的。例如,在PHP內部布爾型、整型及資源(只要存儲資源的標識符即可)都是通過lval字段存儲的;dval用於存儲浮點型;str存儲字符串;ht存儲數組(註意PHP中的數組其實是哈希表);而obj存儲對象類型;如果所有字段全部置為0或NULL則表示PHP中的NULL,這樣就達到了用5個字段存儲8種類型的值。

在PHP只用於做動態頁面腳本時,這種泄露也許不是很要緊,因為動態頁面腳本的生命周期很短,PHP會保證當腳本執行完畢後,釋放其所有資源。但是PHP發展到目前已經不僅僅用作動態頁面腳本這麽簡單,如果將PHP用在生命周期較長的場景中,例如自動化測試腳本或deamon進程,那麽經過多次循環後積累下來的內存泄露可能就會很嚴重。這並不是我在聳人聽聞,我曾經實習過的一個公司就通過PHP寫的deamon進程來與數據存儲服務器交互。

PHP5.3中的垃圾回收算法——Concurrent Cycle Collection in Reference Counted Systems

首先PHP會分配一個固定大小的“根緩沖區”,這個緩沖區用於存放固定數量的zval,這個數量默認是10,000,如果需要修改則需要修改源代碼Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES然後重新編譯。

由上文我們可以知道,一個zval如果有引用,要麽被全局符號表中的符號引用,要麽被其它表示復雜類型的zval中的符號引用。因此在zval中存在一些可能根(root)。這裏我們暫且不討論PHP是如何發現這些可能根的,這是個很復雜的問題,總之PHP有辦法發現這些可能根zval並將它們投入根緩沖區。

當根緩沖區滿額時,PHP就會執行垃圾回收,此回收算法如下:

1、對每個根緩沖區中的根zval按照深度優先遍歷算法遍歷所有能遍歷到的zval,並將每個zval的refcount減1,同時為了避免對同一zval多次減1(因為可能不同的根能遍歷到同一個zval),每次對某個zval減1後就對其標記為“已減”。

2、再次對每個緩沖區中的根zval深度優先遍歷,如果某個zval的refcount不為0,則對其加1,否則保持其為0。

3、清空根緩沖區中的所有根(註意是把這些zval從緩沖區中清除而不是銷毀它們),然後銷毀所有refcount為0的zval,並收回其內存。

如果不能完全理解也沒有關系,只需記住PHP5.3的垃圾回收算法有以下幾點特性:

1、並不是每次refcount減少時都進入回收周期,只有根緩沖區滿額後在開始垃圾回收。

2、可以解決循環引用問題。

3、可以總將內存泄露保持在一個閾值以下。

## PHP cli 的交互模式

php -a

## PHP-FPM restart reload


https://www.cnblogs.com/GaZeon/p/5421906.html

啟動php-fpm:
/usr/local/php/sbin/php-fpm

php 5.3.3 以後的php-fpm 不再支持 php-fpm 以前具有的 /usr/local/php/sbin/php-fpm (start|stop|reload)等命令,所以不要再看這種老掉牙的命令了,需要使用信號控制:
master進程可以理解以下信號
INT, TERM 立刻終止?QUIT 平滑終止?USR1 重新打開日誌文件?USR2 平滑重載所有worker進程並重新載入配置和二進制模塊


reload --重新加載,reload會重新加載配置文件,Nginx服務不會中斷。而且reload時會測試conf語法等,如果出錯會rollback用上一次正確配置文件保持正常運行。
restart --重啟(先stop後start),會重啟Nginx服務。這個重啟會造成服務一瞬間的中斷,如果配置文件出錯會導致服務啟動失敗,那就是更長時間的服務中斷了。

因此 reload 更加平滑 對線上業務影響更小

php-fpm master 進程可以理解一下以下的信號:?
INT, TERM 立刻終止
QUIT 平滑終止
USR1 重新打開日誌文件
USR2 平滑重載所有worker進程並重新載入配置和二進制模塊
?
關閉php-fpm:

kill -INT `cat /usr/local/php/var/run/php-fpm.pid`

平滑重啟php-fpm:

kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`


kill usr2 會 保證使用原來的配置文件


## 常用PHP configure

```
/configure --prefix=/usr/local/Cellar/php54 --with-mysql=/usr/local/Cellar/mysql/5.7.22/ --with-mysqli=/usr/local/Cellar/mysql/5.7.22/bin/mysql_config --enable-bcmath --enable-mbstring --enable-sockets --enable-magic-quotes --enable-fpm --enable-fastcgi --enable-pcntl --with-mcrypt --with-mhash --with-gmp --with-openssl --enable-zip --enable-ftp --with-bz2 --with-pdo-mysql=/usr/local/Cellar/mysql/5.7.22/ --with-curl
```

# Others

## base58

比特幣用的加密方式

相比Base64,Base58不使用數字"0",字母大寫"O",字母大寫"I",和字母小寫"l",以及"+"和"/"符號。

設計Base58主要的目的是:

* 避免混淆。在某些字體下,數字0和字母大寫O,以及字母大寫I和字母小寫l會非常相似。

* 不使用"+"和"/"的原因是非字母或數字的字符串作為帳號較難被接受。

* 沒有標點符號,通常不會被從中間分行。

* 大部分的軟件支持雙擊選擇整個字符串。

但是這個base58的計算量比base64的計算量多了很多。因為58不是2的整數倍,需要不斷用除法去計算。

感覺 base58 計算方式是有bug的

大多數都很低了

## Linux 進程狀態

Linux 進程狀態

```
D 不可中斷 Uninterruptible sleep (usually IO)

R 正在運行,或在隊列中的進程

S 處於休眠狀態

T 停止或被追蹤

Z 僵屍進程

W 進入內存交換(從內核2.6開始無效)

X 死掉的進程


< 高優先級

N 低優先級

L 有些頁被鎖進內存

s 包含子進程

+ 位於後臺的進程組;

l 多線程,克隆線程 multi-threaded (using CLONE_THREAD, like NPTL pthreads do)


```

## 常見限流算法

令牌桶算法

1)存放固定令牌的桶,生產令牌的速率固定
?2)當令牌達到上限時候,產生的令牌被丟棄或拒絕
?3)n個請求過來,拿n個令牌,若令牌不足,則請求被決絕或等待

傳統

如果counter的值大於100並且該請求與第一個 請求的間隔時間還在1分鐘之內

如果該請求與第一個請求的間隔時間大於1分鐘,且counter的值還在限流範圍內,那麽就重置 counter

漏桶算法

1)桶容量固定,固定速錄流出
?2)桶是空的,不流出
?3)以任意速率流入桶,若超過桶容量,被丟棄


滑動窗口

滑動窗口,又稱rolling window。為了解決這個問題,我們引入了滑動窗口算法。如果學過TCP網絡協議的話,那麽一定對滑動窗口這個名詞不會陌生。下面這張圖,很好地解釋了滑動窗口算法:

## Redis Lua

腳本的安全性
當將 Lua 腳本復制到附屬節點, 或者將 Lua 腳本寫入 AOF 文件時, Redis 需要解決這樣一個問題: 如果一段 Lua 腳本帶有隨機性質或副作用, 那麽當這段腳本在附屬節點運行時, 或者從 AOF 文件載入重新運行時, 它得到的結果可能和之前運行的結果完全不同。

三、使用Lua腳本的好處

1、減少網絡開銷:可以將多個請求通過腳本的形式一次發送,減少網絡時延和請求次數。

2、原子性的操作:Redis會將整個腳本作為一個整體執行,中間不會被其他命令插入。因此在編寫腳本的過程中無需擔心會出現競態條件,無需使用事務。

3、代碼復用:客戶端發送的腳步會永久存在redis中,這樣,其他客戶端可以復用這一腳本來完成相同的邏輯。

4、速度快:見 與其它語言的性能比較, 還有一個 JIT編譯器可以顯著地提高多數任務的性能; 對於那些仍然對性能不滿意的人, 可以把關鍵部分使用C實現, 然後與其集成, 這樣還可以享受其它方面的好處。

5、可以移植:只要是有ANSI C 編譯器的平臺都可以編譯,你可以看到它可以在幾乎所有的平臺上運行:從 Windows 到Linux,同樣Mac平臺也沒問題, 再到移動平臺、遊戲主機,甚至瀏覽器也可以完美使用 (翻譯成JavaScript).

6、源碼小巧:20000行C代碼,可以編譯進182K的可執行文件,加載快,運行快。

Redis Lua解釋器加載以下Lua庫:

```

1、base lib.
2、table lib.
3、string lib.
4、math lib.
5、struct lib.
6、cjson lib.
7、cmsgpack lib.
8、bitop lib.
9、redis.sha1hex function.
10、redis.breakpoint和redis.debug 函數在Redis Lua調試器的上下文中。

```

## Redis哨兵

redis 哨兵模式

http://redis.majunwei.com/topics/sentinel.html

Redis-Sentinel是官方推薦的高可用解決方案,當redis在做master-slave的高可用方案時,假如master宕機了,redis本身(以及其很多客戶端)都沒有實現自動進行主備切換,而redis-sentinel本身也是獨立運行的進程,可以部署在其他與redis集群可通訊的機器中監控redis集群。

它的主要功能有一下幾點

1、不時地監控redis是否按照預期良好地運行;
?2、如果發現某個redis節點運行出現狀況,能夠通知另外一個進程(例如它的客戶端);
?3、能夠進行自動切換。當一個master節點不可用時,能夠選舉出master的多個slave(如果有超過一個slave的話)中的一個來作為新的master,其它的slave節點會將它所追隨的master的地址改為被提升為master的slave的新地址。
?4、哨兵為客戶端提供服務發現,客戶端鏈接哨兵,哨兵提供當前master的地址然後提供服務,如果出現切換,也就是master掛了,哨兵會提供客戶端一個新地址。

哨兵(sentinel)本身也是支持集群的

很顯然,單個哨兵會存在自己掛掉而無法監控整個集群的問題,所以哨兵也是支持集群的,我們通常用三臺哨兵機器來監控一組redis集群。

具體測試結構 1 m 2slave 3 哨兵

模擬多實例的話 可以使用 多port的形式進行實現


redis考慮的參數:
repl-diskless-sync 無硬盤復制功能 2.8 實驗性 對應硬盤較慢的 slave 防止對redis 帶來性能損失

repl-disable-tcp-nodelay

在slave和master同步後(發送psync/sync),後續的同步是否設置成TCP_NODELAY

假如設置成yes,則redis會合並小的TCP包從而節省帶寬,但會增加同步延遲(40ms),造成master與slave數據不一致

假如設置成no,則redis master會立即發送同步數據,沒有延遲
前者關註性能,後者關註一致性

append_only:
持久化 方式

redis支持兩種持久化方式,一種是 Snapshot(RDB)<二進制文件> 也是默認方式,另一種是Append only file(AOF)的方式。

Client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主線程中保存快照的,由於redis是用一個主線程來處理所有 client的請求,這種方式會阻塞所有client請求。所以不推薦使用。另一點需要註意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步臟數據。如果數據量大的話,而且寫操作比較多,必然會引起大量的磁盤io操作,可能會嚴重影響性能。 Stop the World

應用方鏈接的時候 應該鏈接 哨兵端口?

但是如果哨兵端口失效如何?


部署哨兵之前需要了解的基本事情

1. 一個健壯的部署至少需要三個哨兵實例。

2. 三個哨兵實例應該放置在客戶使用獨立方式確認故障的計算機或虛擬機中。例如不同的物理機或不同可用區域的虛擬機。 (異地多活)

3. sentinel + Redis實例不保證在故障期間保留確認的寫入,因為Redis使用異步復制。然而有方式部署哨兵使丟失數據限制在特定時刻,雖然有更安全的方式部署它。(可能在故障期間 寫入會失效)


4. 你的客戶端要支持哨兵,流行的客戶端都支持哨兵,但不是全部。


5. 沒有HA設置是安全的,如果你不經常的在開發環境測試,在生產環境他們會更好。你可能會有一個明顯的錯誤配置只是當太晚的時候。


6. Sentinel,Docker,或者其他形式的網絡地址交換或端口映射需要加倍小心:Docker執行端口重新映射,破壞Sentinel自動發現其他的哨兵進程和master的slave列表。稍後在這個文檔裏檢查關於Sentinel和Docker的部分,了解更多信息。


測試故障轉移

這時候我們部署的sentinel已經準備好了測試。我們只需要kill掉master檢查配置是否改變。所以只需要做如下的事情:

redis-cli -p 6379 DEBUG sleep 30

這個命令將使master休眠30秒不能訪問。這主要是模仿master由於一些原因掛掉。

Slaves優先級

Redis實例有一個salve-priority的配置參數。這個信息暴露在Redis slave的 INFO 輸出裏,Sentinel使用它以便於在可用的salves中獲取slave進行故障轉移:

* 如果Slave優先級設置為0.slave永遠不會晉升為master。


* 優先級數字越低越被Sentinel優先選擇。


例如如果有一個slave S1在當前master的同一個數據中心,並且另一個salve S2在另一個數據中心,可以設置S1的優先級為10,S2的優先級為100,所以如果master故障並且S1和S2都可用,S1將會被優先選擇。
了解關於slave選擇的更多信息,請參考 本文檔的 slave選擇和優先級部分。

周記 8