這篇文章主要講解了“怎么做數(shù)據(jù)庫(kù)讀寫(xiě)分離”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“怎么做數(shù)據(jù)庫(kù)讀寫(xiě)分離”吧!
創(chuàng)新互聯(lián)公司主營(yíng)寧遠(yuǎn)網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,APP應(yīng)用開(kāi)發(fā),寧遠(yuǎn)h5小程序開(kāi)發(fā)搭建,寧遠(yuǎn)網(wǎng)站營(yíng)銷(xiāo)推廣歡迎寧遠(yuǎn)等地區(qū)企業(yè)咨詢(xún)
實(shí)現(xiàn)方式
對(duì)于讀寫(xiě)分離的使用,主要分為兩種方式,客戶(hù)端方式和代理方式。
客戶(hù)端方式可以自己用 Spring 自帶的 AbstractRoutingDataSource 來(lái)實(shí)現(xiàn),也可以用開(kāi)源的框架來(lái)實(shí)現(xiàn),比如 Sharding-JDBC。
代理方式需要編寫(xiě)代理服務(wù)來(lái)對(duì)所有節(jié)點(diǎn)進(jìn)行管理,應(yīng)用不需要關(guān)注多個(gè)數(shù)據(jù)庫(kù)節(jié)點(diǎn)信息??梢宰约簩?shí)現(xiàn),也可以用開(kāi)源的框架,也可以用商業(yè)的云服務(wù)。
數(shù)據(jù)延遲
談到數(shù)據(jù)延遲,你先得理解主從架構(gòu)的原理。對(duì)數(shù)據(jù)的增刪改操作在主庫(kù)上執(zhí)行,查詢(xún)?cè)趶膸?kù)上執(zhí)行,當(dāng)數(shù)據(jù)剛插入到主庫(kù),然后馬上去查詢(xún)的時(shí)候,很有可能數(shù)據(jù)還沒(méi)同步到從庫(kù)上,就會(huì)出現(xiàn)查詢(xún)不到的情況。
像我之前在某些網(wǎng)站發(fā)表文章,發(fā)表之后跳轉(zhuǎn)到列表頁(yè)面,發(fā)現(xiàn)沒(méi)有新發(fā)表的文章,重新刷新下頁(yè)面又有了,這一看這就是讀寫(xiě)分離后的數(shù)據(jù)延遲導(dǎo)致的現(xiàn)象。
強(qiáng)制路由數(shù)據(jù)延遲要不要解決,一般取決于業(yè)務(wù)場(chǎng)景。對(duì)于實(shí)時(shí)性要求沒(méi)有那么高的業(yè)務(wù)場(chǎng)景,允許一定的延遲,對(duì)于實(shí)時(shí)性要求高的場(chǎng)景,唯一的方式就是直接從主庫(kù)進(jìn)行查詢(xún),這樣才能及時(shí)讀到剛插入或者修改后最新的數(shù)據(jù)。
強(qiáng)制路由
就是一種解決方案,也就是將讀請(qǐng)求強(qiáng)制分發(fā)到主庫(kù)進(jìn)行查詢(xún)。大部分中間件都支持 Hint 語(yǔ)法/FORCE_MASTER/和/FORCE_SLAVE/。
以 Sharding-JDBC 舉例,框架提供了 HintManager 來(lái)強(qiáng)制路由,使用方式如下:
HintManager hintManager = HintManager.getInstance(); hintManager.setMasterRouteOnly();
為了方便使用,建議封裝一個(gè)注解,在需要實(shí)時(shí)查詢(xún)的業(yè)務(wù)方法上加上注解,通過(guò)切面進(jìn)行強(qiáng)制路由的設(shè)置。
注解使用:
@MasterRoute @Override public UserBO getUser(Long id) { log.info("查詢(xún)用戶(hù) [{}]", id); if (id == null) { throw new BizException(ResponseCode.PARAM_ERROR_CODE, "id不能為空"); } UserDO userDO = userDao.getById(id); if (userDO == null) { throw new BizException(ResponseCode.NOT_FOUND_CODE); } return userBoConvert.convert(userDO); }
切面設(shè)置:
@Aspect public class MasterRouteAspect { @Around("@annotation(masterRoute)") public Object aroundGetConnection(final ProceedingJoinPoint pjp, MasterRoute masterRoute) throws Throwable { HintManager hintManager = HintManager.getInstance(); hintManager.setMasterRouteOnly(); try { return pjp.proceed(); } finally { hintManager.close(); } } }
事務(wù)操作
在事務(wù)中的讀請(qǐng)求,走主庫(kù)還是從庫(kù)呢?對(duì)于這個(gè)問(wèn)題,最簡(jiǎn)單的方式就是所有事務(wù)中的操作都走主庫(kù),在事務(wù)中經(jīng)常會(huì)存在插入,然后再重新查詢(xún)的場(chǎng)景,此時(shí)事務(wù)沒(méi)提交,就算同步很快,從庫(kù)也是沒(méi)有數(shù)據(jù)的,所以只能走主庫(kù)。
但還有一些請(qǐng)求,只需要查詢(xún)從庫(kù)就行了,如果針對(duì)所有事務(wù)中的操作都強(qiáng)制路由,也不是很好。在 Sharding-JDBC 中的做法挺好的,對(duì)于同一線(xiàn)程且同一數(shù)據(jù)庫(kù)連接內(nèi),如有寫(xiě)入操作,以后的讀操作均從主庫(kù)讀取,用于保證數(shù)據(jù)一致性。如果我們?cè)跀?shù)據(jù)寫(xiě)入之前有查詢(xún)請(qǐng)求,還是走的從庫(kù),減輕主庫(kù)壓力。
動(dòng)態(tài)強(qiáng)制路由
在功能開(kāi)發(fā)的時(shí)候就決定了哪些接口要強(qiáng)制走主庫(kù),這個(gè)時(shí)候我們會(huì)在代碼上進(jìn)行路由的控制,也就是前面講的自定義注解。如果有些是沒(méi)有加的,但是在線(xiàn)上運(yùn)行的時(shí)候發(fā)現(xiàn)還是要走主庫(kù)才可以,這個(gè)時(shí)候就需要改代碼重新發(fā)布了。
動(dòng)態(tài)強(qiáng)制路由可以結(jié)合配置中心來(lái)實(shí)現(xiàn),通過(guò)配置的方式來(lái)決定哪些接口要強(qiáng)制路由,然后在 Filter 中通過(guò) HintManager 來(lái)設(shè)置,避免改代碼重啟。
也可以通過(guò)切面精確到業(yè)務(wù)方法級(jí)別的動(dòng)態(tài)路由配置。
流量分發(fā)
場(chǎng)景一:
假設(shè)你有一個(gè)主節(jié)點(diǎn),兩個(gè)從節(jié)點(diǎn),讀請(qǐng)求較多,兩個(gè)從節(jié)點(diǎn)壓力有點(diǎn)大。這個(gè)時(shí)候只能增加第三個(gè)從節(jié)點(diǎn)來(lái)分擔(dān)壓力?,F(xiàn)象是主庫(kù)的壓力并不大,寫(xiě)入較少,從成本來(lái)考慮,是否可以不增加第三個(gè)從節(jié)點(diǎn)呢?
場(chǎng)景二:
假設(shè)你有一個(gè) 8 核 64G 的主庫(kù),8 核 64G 的從庫(kù),4 核 32G 的從庫(kù),從配置上來(lái)看,4 核 32G 的從庫(kù)處理能力肯定是要低于其他兩個(gè)的,這個(gè)時(shí)候如果我們沒(méi)有定制流量分發(fā)的比例,就會(huì)出現(xiàn)低配數(shù)據(jù)庫(kù)壓力過(guò)高而導(dǎo)致的問(wèn)題。當(dāng)然這個(gè)也能避免使用不同規(guī)則的從庫(kù)。
上面的場(chǎng)景需要能夠?qū)φ?qǐng)求進(jìn)行管理,在 Sharding-JDBC 中提供了讀寫(xiě)分離的路由算法,我們可以自定義算法來(lái)進(jìn)行流量的分發(fā)管理。
實(shí)現(xiàn)算法類(lèi):
public class KittyMasterSlaveLoadBalanceAlgorithm implements MasterSlaveLoadBalanceAlgorithm { private RoundRobinMasterSlaveLoadBalanceAlgorithm roundRobin = new RoundRobinMasterSlaveLoadBalanceAlgorithm(); @Override public String getDataSource(String name, String masterDataSourceName, ListslaveDataSourceNames) { String dataSource = roundRobin.getDataSource(name, masterDataSourceName, slaveDataSourceNames); // 控制邏輯,比如不同的從節(jié)點(diǎn)(配置不同)可以有不同的比例 return dataSource; } @Override public String getType() { return "KITTY_ROUND_ROBIN"; } @Override public Properties getProperties() { return roundRobin.getProperties(); } @Override public void setProperties(Properties properties) { roundRobin.setProperties(properties); } }
基于 SPI 機(jī)制的配置:
org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithm org.apache.shardingsphere.core.strategy.masterslave.RandomMasterSlaveLoadBalanceAlgorithm com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm
讀寫(xiě)分離的配置:
spring.shardingsphere.masterslave.load-balance-algorithm-class-name=com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithm spring.shardingsphere.masterslave.load-balance-algorithm-type=KITTY_ROUND_ROBIN
感謝各位的閱讀,以上就是“怎么做數(shù)據(jù)庫(kù)讀寫(xiě)分離”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)怎么做數(shù)據(jù)庫(kù)讀寫(xiě)分離這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!