關鍵詞:mysql高可用概述,mysql高可用架構html
20190918 如今業內經常使用的MySQL高可用方案有哪些?
目前來講,用的比較多的開源方案份內置高可用與外部實現,內置高可用有以下:
一、官方版本分支:MGR(首推)
二、percona分支:PXC
三、MariaDB:Galera Clustermysql
外部實現方案:
一、orchestrator(GTID)
二、replication-manager(GTID)
三、MHA(傳統複製)
四、MOHA(支持多AZ部署)
五、其餘...web
【1.1.1】mysql支持的複製類型sql
基於binlog的3種模式(詳情參考:binlog的3種日誌記錄模式),oracle在mysql5.5版本收購shell
【1.1】statement:基於語句的複製(5.5.6以前默認),主服務器執行的SQL語句,在從服務器執行一樣的語句數據庫
【1.2】row:基於行的複製(5.5.7以後默認),把改變的內容複製到從庫,而不是把SQL命令在從庫從新執行一遍。mysql5.0就開始支持windows
【1.3】mixed:混合類型的複製,默認是使用 statement 語句方式複製,一旦發現基於語句沒法精確複製時(好比now() 由於主從有延遲致使數據不一致)就會採用基於 row 行的方式複製。安全
【1.1.2】mysql的4種同步方式介紹服務器
如下圖片均引用自《深刻淺出mysql開發、優化與管理維護》網絡
【1.2.1】異步複製(和MSSQL的高性能模式鏡像同樣):(3.23 native)
主庫只管binlog dump數據到從庫,不保證主從徹底一致,斷電、崩潰等狀況會丟失數據。
【1.2.2】全同步複製:
【2.2.1】核心概念:主從複製,主庫要等到從庫重作事務而且提交成功,接受到ACK成功確認標識後,主庫才能提交事務。
【2.2.2】與半同步的區別:半同步是隻須要持久化到relay log階段便可返回ACK成功標識給主庫,而全同步須要等待從庫SQL進程完整的運行完該事務內容才能返回ACK成功標識。
【2.2.3】原理:
主庫事務寫入redo log和 binlog 文件後,binlog dump線程提取binlog數據給IO線程,IO線程把數據加載回從庫的relay log文件。
從庫SQL線程開啓事務重作應用relay log數據操做,等從庫提交完後,返回ACK確認標識,主庫纔會提交。
【1.2.3】傳統半同步複製:(自5.5開始,插件)
【1.2.3.1】原理:
master事務commit 指令已經寫入binlog(注意,這裏已經提交了,再去同步,只是在等一個ACK)和 binlog 文件後,binlog dump線程提取binlog數據給IO線程,IO線程把數據加載回從庫的relay log文件。
只要IO線程-》slave的relay log已經flush disk 磁盤落地,slave就返回ACK確認標識給master。
【1.2.3.2】特性:
這裏的commit主庫上是已經在 binlog、redo log 中提交了的,其餘session均可以看到。
但須要等待從庫返回ACK確認標識才會把事務提交到存儲引擎持久化(即 ibdata、ibd等磁盤數據文件)且返回到client一個commit成功的指令。
【1.2.3.3】宕機狀況:
master上這個事務實際上是已經提交了而且落盤master的binlog了,master的其餘Session是能夠看到這個提交後的事務的,而slave尚未commit。
這個時候master掛了,master上的數據和slave不一致。master crash後,master根據redo重作提交了這個事務.
在切換到從後,slave由於沒有commit而回滾了這個事務致使數據丟失。致使主從不一致。
【1.2.4】加強半同步複製(mysql 5.7.4及以上才能夠用,與MSSQL鏡像高安全模式相同):
與【1.2.3】傳統半同步複製大體同樣。惟一的區別就是,在事務提交過來後:
核心區別:主庫會等從庫在relay log 階段持久化該事務到該文件後,接受ACK成功確認標識後,再進行提交(commit指令寫入binlog/redolog)
【1.2.4.1】傳統的半同步複製:
master 會把事務在主庫先提交了,其餘訪問能夠查看到,只是等從庫返回ACK標識才反饋給client 事務提交成功信息。
【1.2.4.2】加強半同步複製:
master 只會寫binlog,而後等slave的IO線程把事務持久化(flush disk)到 relay log 上後,返回ACK確認標識。
master收到確認標識後纔會commit 寫入到binlog 和 redo log,並返回給客戶端commit成功的指令。
【1.2.4.3】宕機狀況:
主庫上這個事務由於沒有寫入redo log,因此這個事務被認爲是未提交。master的其餘session是不能夠看到這個事務的。
這個時候主庫掛了,master上的數據和slave一致,master crash後,slave不丟數據。
【1.2.5】GTID 複製(mysql 在 5.6.2 及以後開始支持GTID):
【1.2.5.1】GTID(Global Transaction Identifiers)概念:
對於一個已提交事務的編號,事務的惟一編號,而且是一個全局惟一的編號。GTID和事務會記錄到binlog中,用來標識事務。
GTID是用來替代之前,傳統複製方法(binlog+position),mysql 5.6.2開始支持GTID。
mysql支持GTID後,一個事務在集羣中就再也不孤單,在每個節點中,若是存在具相同標識符的狀況,能夠避免同一個事務,在同一個節點出現屢次的狀況。
(能夠初步理解成row模式的,和statement的區別,前者記得是具體作了什麼事,後者記錄的是位置)
GTID的出現最直接的效果就是,每個事物在集羣中具備了惟一性的意義,相對於行復制來說數據安全性更高,故障切換更簡單。
【1.2.5.2】簡單案例解釋概念:
好比,當咱們一主2從,主出故障後切換到從DB1上去了,那麼另外2臺機器,須要從新手動構建主從;
具體爲:
-- 使用傳統方式構建的主庫宕機從新搭建 -- 麻煩點:每臺機器的Binlog名字和Postion位置點都不同,須要從新定位文件在哪裏,位置在哪裏 change master to master_host='xxx', master_user='xxx', master_password='xxx', master_port='xxx', master_log_file='xxx', master_log_pos='xxx'; -- 使用GTID方式的主庫宕機從新搭建 -- 優點點:每臺機器上的GTID都是同樣的,不須要管文件是哪一個,位置在哪裏,能夠自動根據GTID定位 change master to master_host='xxx', master_user='xxx', master_password='xxx', master_port='xxx', master_auto_postion=1;
【1.2.5.3】GTID的組成
GTID 是由 server_uuid:Sequence_Number 組成;
(1)server_uuid:是一個mysql實例的全局惟一表示;存放在 ${datadir}/auto.cnf
(2)Sequence_Number:是mysql內部的一個事務的標號,一個mysql實例不會重複的序列號(保證服務器內惟一),也表示在該實例上已經提交事務的數量,而且隨着事務提交而遞增。
(3)根據GTID能夠知道事務最初是在哪一個實例上提交的,方便故障排查和切換
【1.3】複製之間的區別
Mysql羣組複製(mysql 5.7.19)
相關介紹參考:https://www.cnblogs.com/luoahong/articles/8043035.html
【2.1】基本概念介紹
由MySQL基於Paxos所開發的,基本複製理念以下圖
全部機器都是主,爲了不數據衝突在執行提交一個事務後,會先進行數據衝突驗證,而後才進行binlog寫入與relay log複製。
除了多master和數據驗證之外,能夠暫時理解成後面的全部步驟和半同步複製同樣。
【2.2】MGR架構
(1)總體架構
(2)replication plugin 插件
(3)GCS
【2.3】MGR的模式
【2.3.1】單主模式(single primary mode)
(1)單主模式基本架構與形式概念
(2)單主模式的運行機制
【2.3.2】多主同步模式
(1)多主同步模式的衝突檢測(核心是基於主鍵),這個檢測就是certify
這個衝突檢測就是最開始運行原理的certify
若是真有主鍵相同的操做執行了怎麼辦?先執行的提交,後執行的衝突回滾,如圖
(2)多主同步模式下的限制
限制和規則
•僅InnoDB Engine(Transactional and row level lock)
•表必須有主鍵
•gtid-mode=ON
•binlog格式爲Row-based
•對於同一個對象執行DDL和DML應在同一成員上執行,不支持在不一樣服務器上執行DDL
•不支持具備多級外鍵依賴關係的表,特別是已定義CASCADING外鍵約束的表
•不支持「serializable」隔離級別
【2.4】MGR的監視
(1)經常使用系統表監視
兩個performance_schema表
•replication_group_members
•replication_group_member_stats
本地成員狀態
•擴展Replication performance_schema 表
•group_replication_recovery channel information
•group_replication_applier channel information
•新的全局變量
•group_replication_primary_member
(2)MGR羣組複製的高可用性
更好的容錯度
羣組複製的高可用性
–故障(F)所需的服務器數量(N)N = 2F + 1.
–最多支持9個成員
•容許4個成員故障。
–沒有腦裂的問題
•僅當大多數成員(N/2+1)在線時,羣組纔可用,以下圖
【2.5】 腦裂問題
(1)什麼是腦裂?(2)MGR爲何會出現腦裂?
(1)什麼是腦裂:我理解的就是,一個大腦控制變成了2個大腦控制,各作各的。產生了不一樣步,不一致的操做與選擇。
(2)MGR爲何會出現腦裂:大多數狀況是由於網絡鏈接問題。以下圖
檢測網絡分區:Performance_Schema.replication_group_members
有2個可能-》《1》真就另外3臺掛掉了 《2》另外3臺沒掛,是由於和這2臺連不上了沒法通訊
咱們這裏說《2》這種狀況
即,原本MGR羣組的5臺機器都能互相連通,忽然,S3-S5這3臺機器與S1-S2機器失去了鏈接。
只有這S1-S2這2臺機器被監控到在線,但其餘3臺機器是多數節點,它們也可能有鏈接在使用。對於S3-S5而言,S1-S2是宕機概念的。
而後對於S3-S5而言的MGR是能夠正常運行的,就把其S1-S2丟失了。
這個時候S1-S2 這2臺 查詢能夠正常運行,但不能夠進行增刪改(若是發生增刪改,就沒法再次回到MGR羣主裏了),以下圖
如何處理呢?強制指定節點生成一個新的MGR。
【2.6】MGR讀取一致性參數解析
group_replication_consistency8.0.14以後
•EVENTUAL,BEFORE,AFTER和BEFORE_AND_AFTER
(1)EVENTUAL(默認值)事務在執行以前不等待先前的事務應用,也不等待其餘成員應用其更改。這是8.0.14以前的
(2)BEFORE
事務將在開始執行以前等待全部先前的事務完成。這可確保此事務將在最新的數據快照上執行,而不用管是哪一個成員。
(3)AFTER
事務將等待其更改已應用於其餘成員。這可確保一旦此事務完成,全部後續事務都會讀取包含其更改的數據庫狀態,而無論它們在哪一個成員上執行。
(4)BEFORE_AND_AFTER
•此事務將等到:
1)全部先前的事務在開始執行以前完成;
2)其變動已適用於其餘成員。這可確保:
1)此事務將在最新的數據快照上執行;
2)一旦此事務完成,全部後續事務都會讀取包含其更改的數據庫狀態,而無論它們在哪一個成員上執行
【2.7】瞭解MGR節點狀態
參考:https://blog.csdn.net/d6619309/article/details/70432640
主庫高可用,實現故障轉移與主從分離,在MySQL故障切換過程當中。
MHA能作到在0~30 秒以內自動完成數據庫的故障切換操做,而且在故障切換過程當中,MHA能在最大程度上保證數據的一致性。
【3.1】MHA的組成概念與部署概念
MHA由兩部分組成:MHA Manager(管理節點)和MHA Node(數據節點)。
MHA Manager可單獨部署在一臺機器上管理多個master-slave集羣,也能夠部署在一臺slave節點上。
MHA Node運行在每臺MySQL服務器上,MHA Manager會定時探測集羣中的master節點。
當master出現故障時,它能夠自動將最新數據的slave提高爲新的master,而後將全部其餘的slave從新指向新的master,整個故障轉移過程對應用程序徹底透明。
MHA Manager:
(1)master自動切換及故障轉移命令運行
(2)其餘的幫助腳本運行:手動切換master;master/slave 狀態監測
MHA Node:
(1)複製主節點的binlog數據
(2)對比節點的中繼日誌文件
(3)無需中止從節點的SQL線程,定時刪除中繼日誌
【3.2】MHA的基本工做原理
(1)從宕機崩潰的master保存二進制日誌事件(binlog events)
(2)識別含有最新更新的slave;
(3)應用差別的中繼日誌到其餘的slave;
(4)應用從master保存的二進制日誌事件(binlog events);
(5)提高一個slave爲新的master;
(6)是其餘的slave鏈接新的master進行復制;
【3.3】MHA經常使用架構
服務器資源:
(1)至少5臺PC,其中2臺mysql主庫,2臺mysql從庫,1臺MHA Monitor
(2)其中的MHA Monitor 能夠選擇低配(即爲 MHA MANAGER)
(3)若是不採用F5等做爲從庫的負載均衡器,可用2臺PC SERVER部署LVS 或 HAProxy + Keepalived組合來代替;
優勢:雙主熱備,讀寫分離,SLAVE集羣能夠線性擴展
缺點:讀寫分離須要再程序段解決,但Master大批量寫操做時會產生主從延時。
實際架構方案《1》《2》
《1》1主3從
數據庫層面:一主三從,選定某一臺從爲故障切換後的主。
客戶端-》VIP-》負載均衡-》中間件讀寫分離-》主PC1寫
-》從PC2~PC4讀,PC2指定爲故障切換的master
《2》2主2從
數據庫層面:2主構成VIP,如今正在寫的主庫把全部數據同步到另外3臺機器。
通常狀況下,只有一臺主工做,另一臺主空閒待機。
【3.4】MHA的優缺點
優勢:
由Perl語言開發的開源工具
由master自動監控和故障轉移
master crash 不會致使主從數據不一致
能夠支持基於 GTID的複製模式(MYSQL 5.7)
MHA在進行故障轉移時更不易產生數據丟失
同一個監控節點能夠監控多個集羣
MHA增強了數據的安全性
缺點:
須要編寫腳本或利用第三方工具來實現VIP的配置
MHA啓動後,只會對主數據庫進行監控
須要基於SSH免認證配置,存在必定的安全隱患
沒有提供從服務器的讀負載均衡功能
【3.5】MHA的工具組件中的經常使用腳本
(1)Manager工具組件:
masterha_check_ssl:檢查MHA的SSH配置
masterha_check_repl:檢查mysql複製
masterha_manager:啓動MHA
masterha_check_status:檢測當前MHA運行狀態
masterha_master_monitor:監測Master是否宕機
masterha_master_switch:控制故障轉移(自動或者手動)
masterha_conf_host:添加或刪除配置的server信息
(2)Node工具組件:(這些一般由MHA Manager的腳本出發,無需人工操做)
save_binary_logs:保存和複製master的二進制日誌
apply_diff_relay_logs:識別差別的中繼日誌事件兵並用於其餘slave
filter_mysqlbinlog:清除沒必要要的rollback事件(MHA已經再也不使用它)
purge_relay_logs:按期清除中繼日誌(不會阻塞SQL線程)
(3)自定義擴展
secondary_check_script:經過多條網絡路由檢測master的可用性;
master_ip_failover_script:更新application使用的masterip(須要修改)
shutdown_script:強制關閉master節點
report_script:發送報告;
init_conf_load_script:加載初始配置參數;
master_ip_online_change:更新Master節點IP地址
利用KeepAlived實現故障轉移(功能上相似於MSSQL的鏡像,形式上相似於windows的故障轉移羣集)
【4.1】keepalived 簡介
(4.1.1)起源
keepalived軟件起初是專門爲了LVS負載均衡軟件設計的,用來管理並監控LVS集羣系統中各個服務節點的狀態,後臺又加入了能夠實現高可用的VRRP功能。(VRRP:全稱 virtual router redundancy protocol,虛擬路由冗餘協議)
所以Keepalived除了可以管理LVS軟件外,還能夠做爲其餘服務(如:Nginx/Haproxy/Mysql)的高可用解決方案軟件;
(4.1.2)實現與組成
keepalived 軟件主要是經過VRRP協議實現高可用功能的。
VRRP是 virtual route redundancyProtocol(虛擬路由器榮譽協議)的縮寫,VRRP出現的目的就是爲了解決靜態路由單點故障問題,它可以保證當個別節點宕機時,整個網絡能夠不間斷的運行。
(4.1.3)常見運做場景
一個web服務器至少有2臺PC運行Keepalived,一臺做爲主服務器(master),一臺做爲備份服務器(Backup),可是對外表現爲一個虛擬IP,在Keepalived服務政策工做時,主Master節點會不斷地向備節點發送(多播的方式,組播地址爲224.0.0.18)心跳消息,用以告訴備節點本身還活着。
當主Master節點發生故障時,就沒法發送心跳消息,備節點也就所以沒法繼續監測到來自主Master節點的心跳,因而調用自身接管程序,接管主Master節點的IP資源及服務。
而當主Master節點恢復時,備Backup節點又會釋放主節點故障時自身接管的IP資源及服務,恢復到原來的備用角色。(搶佔模式)(也能夠設置成非搶佔模式,讓其保持主,而不釋放資源給原主,具體見4.1.7)
因此,keepAlived,一方面具備配置管理LVS的功能,同時還具備對LVS下面節點進行健康檢查的功能,另外一方面也能夠實現系統網絡服務的高可用功能,並且Keepalived是能夠工做在網絡的 Layer3,4&7,即網絡層(IP層),傳輸層(TCP層),及應用層,再後面就直接在不少場景下代替了原始的LVS軟件方案。
(4.1.4)keepalived服務的三個重要功能
(1)管理LVS負載均衡軟件
(2)實現LVS集羣節點的健康檢查
(3)做爲系統網絡服務的高可用性(failover)
(4.1.5)Keepalived服務的工做原理
Keepalived服務對之間經過VRRP進行通訊的,VRRP是經過競選機制來肯定主備的(有點像故障轉移羣集中的投票仲裁形式),主的優先級高於備,所以工做時主會優先得到全部的資源,備節點處於等待狀態,當主掛了的時候,備節點就會接管主節點的資源,而後頂替主節點對外服務。
在Keepalived服務對之間,只有做爲主的服務器會一直髮送VRRP廣播包,告訴備它還活着,此時備不會搶佔主,當主不可用(也就是備沒有受到VRRP廣播包信息),就會啓動相關服務接管資源,保證業務的連續性,接管速度最快能夠小於1秒。
(4.1.6)keepalived 的三個核心模塊
分別是core/check/vrrp
core:keepalived的核心,負責主進程的啓動、維護以及全局配置文件的加載和解析。
check:負責健康檢查,包括常見的各類檢查方式。
vrrp:是用來實現VRRP協議的;(VRRP:全稱 virtual router redundancy protocol,虛擬路由冗餘協議)
(4.1.7)什麼是VRRP?
VRRP,全稱 virtual router redundancy protocol,虛擬路由冗餘協議。
VRRP的出現就是爲了解決靜態路由的單點故障問題,VRRP是經過一種競選機制來將路由的任務交給某臺VRRP路由器的。
Keepalived經過組播(默認)、單播(自定義),實現keepalived主備推選,工做模式分爲搶佔和非搶佔。
《1》搶佔模式
主服務器正常工做時,虛擬IP會在主上,備不提供服務,當主服務優先級低於備的時候,備會自動搶佔虛擬IP,這時,主提供服務,備提供服務。
也就是說,搶佔模式下,不分主備,只管優先級。
無論 keepalived.conf 裏的 state 配置成 master 仍是 backup ,只看誰的priority 優先級高, priority 優先級高的那一個,在故障恢復後,會自動將VIP資源再次搶回來。
《2》非搶佔模式
這種方式經過參數nopreempt(通常設置在advert_int 的那一行下面)來控制。無論priority優先級,只要master機器發生故障,VIP資源就會被切換到backup上。
而且,當master機器恢復後,也不會將VIP資源搶回來。除非Backup機器發生故障,才能自動把VIP等資源切換會主庫。
nopreempt 這個參數只能用戶state爲backup的狀況,因此在配置的時候要把master和backup的state都設置成backup,這樣纔會實現keepalived的非搶佔模式!
也就是說
a)當state狀態一個爲master,一個爲backup的時候,加不加nopreempt 這個參數都是同樣的效果。
即都是根據priority優先級來決定誰搶佔VIP資源,屬於搶佔模式!
b)當state狀態都設置成backup,若是不配置nopreempt參數。
也是根據priority優先級來決定誰搶佔VIP資源,屬於搶佔模式!
c)當state狀態都設置成backup,若是配置了 nopreempt 參數,那麼久不會去考慮priority優先級了。
是非搶佔模式! 即只有VIP當前所在機器發生故障,另外一臺機器才能接管VIP。 不考慮優先級問題。
【4.2】Keepalived在MySQL上有什麼做用?
mysql雙主複製,即互爲Master-Slave(只有一個Master提供寫操做),能夠實現數據庫服務器的熱備。
但一個Master宕機後不能實現動態切換,使用Keepalived,能夠經過虛擬IP,實現雙主對外的統一接口以及自動檢查、失敗切換機制,從而實現MySQL數據庫的高可用方案。
《1》架構1:主備集羣架構(雙主HA+keepalived)
方案:mysql 雙主 或者 主從+keepalived 主從自動切換
服務器:2臺PC
優勢:架構簡單,節省資源
缺點:沒法線性擴展,主從失敗後須要手動恢復主從架構
Galera高可用集羣
是一套支持雙主故障切換和雙主平常管理的腳本程序
參考自《mysql高可用解決方案_社區》PDF--出自徐軼韜
【8.1】What is MySQL InnoDB Cluster?
•由如下的組件構成MySQL的高可用框架
•MySQL Group Replication:提供DB的擴展、自動故障轉移
•MySQL Router:提供應用程序鏈接目標的故障轉移
•MySQL Shell:設置羣組複製的環境、設置Router
•2017年4月12日GA
•如下產品能夠單獨安裝使用
•MySQL 5.7.19 (2017-07-17)
•MySQL Router 2.1.4 (2017-07-24)
•MySQL Shell 1.0.10 (2017-07-28)
【8.2】mysql innodb cluster 架構概念
【8.3】組成部分介紹
【8.3.1】mysql route
能夠實現負載均衡,讀寫分離,自動故障切換等功能,是原生支持MGR的官網組件產品。
MySQL Router 可以在不影響現有應用的前提下,將單獨的MySQL實例輕易地遷移到具備高可用性的InnoDB分佈式集羣。
在8.0的改進
原生支持InnoDB集羣
•獲取Group Replication 的架構
•使用存在於各組成員的元數據
•Bootstraps 時設定InnoDB客戶端的路由
•智能的將客戶端路由到InnoDB集羣
•支持多主和單主模式
•重要的改進
•日誌
•監控
•性能
•安全性
【8.3.2】mysql shell
注意,mysql shell 是8.x 推出的集成功能,是替代mysql utilities(5.7及以前)的產品,並集成了不少新功能(8.x版本已經沒有mysql utilities 了)
在mysql innodb cluster中,可使用它來設計、管理、監控
多語言: JavaScript, Python, SQL
–支持編寫腳本
•支持Document 和關係型數據庫模式
•完整的開發和管理API
mysql shell 管理API:
mysql-js> dba.help() •經過全局對象‘dba’使用MySQL管理接口 •執行DBA的操做 •管理MySQL InnoDBclusters •Create clusters •Deploy MySQLinstances •Get cluster info •Start/Stop MySQLInstances •Validate MySQLinstances …
【8.4】核心組成部分MGR
【8.4.1】MySQL Group Replication: 它提供些什麼功能?
•高可用分佈式MySQL數據庫服務
•不需人工操做服務器的固障移轉
•提供分佈式容錯能力
•支持多主更新的架構
•自動重配置(加入/移除節點,崩潰,失敗)
•自動偵測和處理衝突
【8.4.2】MGR的發展示狀與將來趨勢
Step1(已實現):實現組複製高可用,如今已經實現
Step2(正在進行中當前版奔8.0.17):在實現組複製高可用的基礎上,實現異步複製作只讀,而且異步複製只讀從庫會隨着組複製中的故障轉移而自動把異步複製的主庫設置成被故障轉移的主庫。
總結起來就一句話:MGR裏的自動故障轉移後,異步複製從庫自動連上新主庫。詳細見圖
Step3(還沒開始實現):基於Step1和Step2下的分片MGR,也就是說把每一個分片作一個MGR,再根據步驟1(分片內的MGR實現HA)和步驟2(MGR下的異步複製+自動故障轉移後從庫自動連上新主庫)
最終目標:
【8.4.3】MGR/InnnoDB Cluster的當前用戶(2019.08.16 記錄時間)
參考文件:
《mysql高可用解決方案_社區》PDF--徐軼韜
《深刻淺出MySQL數據庫開發、優化與管理維護》第2版(唐漢明、翟振興)
博客園~關於MGR的博文:https://www.cnblogs.com/luoahong/articles/8043035.html