淺析MySQL中主從延遲問題的原因與解決方法
從一個主從延遲問題開始回顧主從復(fù)制原理,并思考主從延遲造成的原因和解決方案。當然,作為底層開發(fā),最后還是只能快準狠的通過一個簡單粗暴的等待方案進行應(yīng)對。
事情的起因
事情要從我寫下這樣的代碼開始
// 獲取當前數(shù)據(jù)庫中未使用的數(shù)據(jù)轉(zhuǎn)為正在使用的狀態(tài) int updateUsing = fateDataDao.update(FateDataStatusEnum.UNUSED.getCode(),FateDataStatusEnum.USING.getCode()); log.info("update UNUSED to USING:{}",updateUsing); // 獲取正在使用狀態(tài)的數(shù)據(jù) List<FateData> fateDataList = fateService.queryStatus(FateDataStatusEnum.USING.getCode()); log.info("queryStatus USING:{}",fateDataList.size());
這部分邏輯清晰簡單明了,把UNUSED狀態(tài)的數(shù)據(jù)更新為USING狀態(tài),然后查詢?nèi)〕?strong>USING狀態(tài)的數(shù)據(jù)。
按照一個正常的邏輯來說updateUsing
的數(shù)量和fateDataList.size()
的數(shù)量應(yīng)該一樣,但是,他不正常。
我在測試環(huán)境小數(shù)據(jù)量測試時,這段代碼邏輯完全無誤。但是上了灰度環(huán)境進行大量數(shù)據(jù)的測試就出現(xiàn)了這樣的問題。
此時,我?guī)е苫蠛筒唤?,將目光投向百度?/p>
首先我認為問題可能好似,MYSQL更新返回的是查詢到的行數(shù),而不是受影響的行數(shù)。
但是負責(zé)MYSQL的同事和我說MYSQL已經(jīng)配置了返回受影響行數(shù),并告訴我應(yīng)該是主從延遲問題,沒辦法解決,看看業(yè)務(wù)能不能改下吧。
這時,我才反應(yīng)過來當時粗略了解的主從延遲問題,我已經(jīng)忘的差不多了。
什么是主從復(fù)制
要了解主從延遲,首先就要知道什么是主從復(fù)制。
MySQL的主從復(fù)制(Master-Slave Replication)是一種數(shù)據(jù)庫復(fù)制技術(shù),用于解決數(shù)據(jù)備份、讀寫分離、負載均衡以及故障恢復(fù)等問題。
主從復(fù)制的基本原理是將一個數(shù)據(jù)庫實例(主服務(wù)器)的數(shù)據(jù)復(fù)制到另一個或多個數(shù)據(jù)庫實例(從服務(wù)器),使得從服務(wù)器的數(shù)據(jù)與主服務(wù)器保持同步。
主從復(fù)制的基本工作流程
- 從服務(wù)器連接到主服務(wù)器,生成兩個線程,一個I/O線程,一個SQL線程
- 主服務(wù)器記錄所有的數(shù)據(jù)更改(INSERT、UPDATE、DELETE),同時會生成一個 log dump 線程,用來給從庫 i/o線程傳binlog。
- 主服務(wù)器會生成一個 log dump 線程將binlog寫到relay log(中繼日志) 文件中
- 從服務(wù)器的SQL 線程會讀取relay log文件中的日志,并解析成具體操作,來實現(xiàn)主從的操作一致,而最終數(shù)據(jù)一致;
流程圖如下:
主從復(fù)制解決的問題
要用到主從復(fù)制的原因主要是為了高可用、高并發(fā):
- 數(shù)據(jù)備份和恢復(fù): 從服務(wù)器可以用作主服務(wù)器的備份,當主服務(wù)器發(fā)生故障時,可以快速切換到從服務(wù)器進行恢復(fù)。
- 讀寫分離: 主服務(wù)器負責(zé)寫操作,而從服務(wù)器可以用于處理讀操作,從而分擔(dān)主服務(wù)器的負載,提高系統(tǒng)性能。
- 負載均衡: 多個從服務(wù)器可以平均分擔(dān)讀請求,實現(xiàn)負載均衡,提高系統(tǒng)的可伸縮性。
- 高可用性: 當主服務(wù)器故障時,可以快速切換到一個從服務(wù)器,保證系統(tǒng)的高可用性。
主從復(fù)制帶來的問題
主從復(fù)制也會造成一些衍生出的問題:
- 數(shù)據(jù)一致性: 主從復(fù)制是異步的,存在一定的延遲,因此在進行讀寫分離時,需要注意可能出現(xiàn)的數(shù)據(jù)一致性問題。
- 寫操作集中: 所有寫操作都集中在主服務(wù)器上,可能導(dǎo)致主服務(wù)器的負載較高。
- 配置和維護: 操作復(fù)雜,需要正確配置主從服務(wù)器,以及定期進行監(jiān)控和維護,確保系統(tǒng)正常運行。
本次遇到的bug,主要就是數(shù)據(jù)一致性方面的問題了。
主從延遲的原因
主庫使用單線程順序?qū)懭隻inlog,效率很高。然而從庫的SQL Thread線程需要對主庫的日志進行隨機IO來重新執(zhí)行DML和DDL,效率較低,難以跟上主庫日志寫入速度,因此產(chǎn)生了主從延遲。
另外,從庫SQL Thread也是單線程,當主庫并發(fā)較高時,產(chǎn)生大量DML,超過了從庫單線程能處理的速度,或者從庫中有大查詢語句產(chǎn)生鎖等待,也會導(dǎo)致從庫執(zhí)行延遲,無法跟上主庫的進度。
主從延遲的解決方案
從主從延遲的原因,我們定位出主要是主庫的高并發(fā)和從庫的SQL Thread效率低造成了這樣的問題。
所以,在不增加機器的情況下的解決方案就是控制主庫的并發(fā)或者提升從庫的SQL Thread處理效率,例如MySQL 5.6 版本后,提供的一種多線程的方式。
簡單粗暴的解決方案
當然,對于我們公司的底層開發(fā)來說,這種層次的設(shè)計需要更高層面的人來推動,而且也需要更長的時間才能處理。
所以這里貼出我自己的解決方案。Thread.sleep
時間請自行控制。
// 獲取當前數(shù)據(jù)庫中未使用的數(shù)據(jù)轉(zhuǎn)為正在使用的狀態(tài) int updateUsing = fateDataDao.update(FateDataStatusEnum.UNUSED.getCode(),FateDataStatusEnum.USING.getCode()); LOGGER.info("update UNUSED to USING:{}",updateUsing); // 獲取正在使用狀態(tài)的數(shù)據(jù) List<FateData> fateDataList = fateService.queryStatus(FateDataStatusEnum.USING.getCode()); LOGGER.info("queryStatus USING:{}",fateDataList.size()); // 如果數(shù)據(jù)相等,直接略過。 if(updateUsing != fateDataList.size()){ // updateUsing為0,但fateDataList不為空的情況。任務(wù)失敗,未更新 if (updateUsing == 0){ Cat.logEvent("updateTmpData","jobFailed:"+"updateUsing:"+updateUsing+"--fateDataList:"+fateDataList.size()); transaction.setStatus(Transaction.SUCCESS); return response; } // 數(shù)據(jù)數(shù)目不相等,等待三秒相等再繼續(xù) boolean equalFlag = false; while(!equalFlag){ // 等待主從延遲 Thread.sleep(3000); fateDataList = fateService.queryStatus(FateDataStatusEnum.USING.getCode()); Cat.logEvent("updateTmpData","equalFailed:"+"updateUsing:"+updateUsing+"--fateDataList:"+fateDataList.size()); LOGGER.info("queryStatus USING:{}",fateDataList.size()); equalFlag = true; } // 數(shù)據(jù)數(shù)目不相等,則需要數(shù)據(jù)不為空再繼續(xù) while(fateDataList.isEmpty()){ // 等待主從延遲 Thread.sleep(1000); fateDataList = fateService.queryStatus(FateDataStatusEnum.USING.getCode()); Cat.logEvent("updateTmpData","queryFailed:"+"updateUsing:"+updateUsing+"--fateDataList:"+fateDataList.size()); LOGGER.info("queryStatus USING:{}",fateDataList.size()); } }
知識補充
Mybatis使用<update>標簽怎么返回影響行數(shù)
Mybatis使用<update>標簽怎么返回影響行數(shù)
在MyBatis Plus中,使用<update>標簽執(zhí)行更新操作時,默認情況下是不返回影響行數(shù)的。但是可以通過配置來實現(xiàn)返回影響行數(shù)的功能。
在MyBatis Plus的配置文件(通常是mybatis-plus-config.xml)中,可以添加如下配置項:
<configuration> <settings> <setting name="returnAffectedCount" value="true"/> </settings> </configuration>
通過設(shè)置returnAffectedCount為true,可以讓MyBatis Plus在執(zhí)行更新操作后返回影響行數(shù)。
另外,如果只需要獲取更新操作的影響行數(shù)而不需要返回具體的更新結(jié)果,可以使用MyBatis Plus提供的UpdateWrapper或者LambdaUpdateWrapper來進行更新操作,并通過調(diào)用相應(yīng)的方法獲取影響行數(shù)。例如:
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("age", 25); int affectedCount = userMapper.update(null, updateWrapper);
在上述示例中,使用了UpdateWrapper.eq()方法指定了更新條件,并通過調(diào)用userMapper.update()方法執(zhí)行更新操作,并返回影響行數(shù)。
總結(jié)起來,在MyBatis Plus中使用<update>標簽執(zhí)行更新操作時,默認情況下是不返回影響行數(shù)的。但可以通過配置來實現(xiàn)返回影響行數(shù)的功能,或者使用UpdateWrapper或者LambdaUpdateWrapper來獲取影響行數(shù)。
到此這篇關(guān)于淺析MySQL中主從延遲問題的原因與解決方法的文章就介紹到這了,更多相關(guān)MySQL主從延遲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
CentOS下將MySQL 5.1升級到MySQL 5.5的步驟
這篇文章主要介紹了CentOS下將MySQL 5.1升級到MySQL 5.5的步驟,需要的朋友可以參考下2015-08-08mysql8.0無備份通過idb文件恢復(fù)數(shù)據(jù)的方法、idb文件修復(fù)和tablespace?id不一致處理
文章描述了公司服務(wù)器斷電后數(shù)據(jù)庫故障的過程,作者通過查看錯誤日志、重新初始化數(shù)據(jù)目錄、恢復(fù)備份文件、修改配置文件等步驟,成功修復(fù)了MySQL數(shù)據(jù)庫2025-03-03MySQL常用命令與內(nèi)部組件及SQL優(yōu)化詳情
這篇文章主要介紹了MySQL常用命令與內(nèi)部組件及SQL優(yōu)化詳情,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-07-07MySQL數(shù)據(jù)表字段操作指南之添加、修改與刪除方法
這篇文章主要介紹了MySQL中使用ALTER TABLE語句修改數(shù)據(jù)表結(jié)構(gòu)的方法,包括添加、修改和刪除字段,通過實例演示了如何高效地管理數(shù)據(jù)表結(jié)構(gòu),需要的朋友可以參考下2024-12-12MySQL中觸發(fā)器的基礎(chǔ)學(xué)習(xí)教程
這篇文章主要介紹了MySQL中觸發(fā)器的基礎(chǔ)學(xué)習(xí)教程,包括對觸發(fā)器的創(chuàng)建和管理等基本知識,著力推薦!需要的朋友可以參考下2015-12-12