千萬級(jí)數(shù)據(jù)量系統(tǒng)優(yōu)化實(shí)踐魁巩,一點(diǎn)一滴記錄分享

注:文中的“Doctor”是精分對(duì)話者,另一個(gè)我族檬,道德約束者歪赢,話嘮,請(qǐng)無視他

前言:博主最近在做公司一個(gè)很重要的項(xiàng)目单料,有關(guān)于汽車物聯(lián)網(wǎng)金融的埋凯,作為剛工作一年的新人來說点楼,這是一個(gè)極好的學(xué)習(xí)機(jī)會(huì),而且被信任的感覺很好啊有木有白对。簡單說博主在為某實(shí)力國產(chǎn)汽車公司賣命掠廓,這個(gè)系統(tǒng)涉及到的用戶有幾百萬,每個(gè)用戶都會(huì)有很多的金融數(shù)據(jù)甩恼,金融總是要結(jié)算的蟀瞧,所以每個(gè)月到結(jié)算日會(huì)有千萬條數(shù)據(jù)需要操作,累計(jì)有幾十億次的計(jì)算条摸,那么面對(duì)這龐大的數(shù)據(jù)集悦污,我是瑟瑟發(fā)抖的,之前對(duì)于優(yōu)化只是建立在理論上的钉蒲,這次雖然是好的實(shí)踐機(jī)會(huì)切端,但是作為核心項(xiàng)目,一點(diǎn)也馬虎不得顷啼。

Doctor:你的廢話太多了踏枣。。钙蒙。

咳咳茵瀑,那么廢話不說了,現(xiàn)在開始進(jìn)入正題躬厌,本文章將會(huì)跟大家分享一下個(gè)人在優(yōu)化中的一些實(shí)踐和心得马昨,不足的地方希望大家指正。

關(guān)于針對(duì)該系統(tǒng)的優(yōu)化烤咧,我主要做了以下幾點(diǎn):

數(shù)據(jù)庫SQL語句調(diào)優(yōu)偏陪,mybatis批量插入,建立索引

適當(dāng)使用Redis存儲(chǔ)不常修改但是常用的數(shù)據(jù)

使用異步處理煮嫌,校驗(yàn)完核心數(shù)據(jù)即可返回前臺(tái)操作完成笛谦,并異步跑后續(xù)操作

多線程處理海量運(yùn)算

該系統(tǒng)的架構(gòu)是基于SpringCloud的微服務(wù)架構(gòu),Springboot+Mybatis+Maven+MySql整合昌阿,同時(shí)使用到Redis饥脑、kafka。

1懦冰、數(shù)據(jù)庫SQL語句調(diào)優(yōu)灶轰,mybatis批量插入,建立索引

關(guān)于SQL語句調(diào)優(yōu)我想最基本的就是在項(xiàng)目開始階段刷钢,根據(jù)業(yè)務(wù)需求抽象成數(shù)據(jù)庫表的時(shí)候要請(qǐng)專門的DBA來分析建表笋颤,本系統(tǒng)是由我跟另一位高級(jí)開發(fā)建的表,其實(shí)后期發(fā)現(xiàn)并不夠完美,也許跟業(yè)務(wù)變更有些大導(dǎo)致的吧伴澄,我認(rèn)為數(shù)據(jù)庫表的設(shè)計(jì)和代碼一樣也需要健壯性赋除,我們的系統(tǒng)使用的是基于SpringCloud的微服務(wù)架構(gòu),其實(shí)更像是把單體服務(wù)拆成多個(gè)springboot小項(xiàng)目非凌,既然使用敏捷迭代開發(fā)模式(我們現(xiàn)在的模式也可以勉強(qiáng)說是devops举农,只不過devops里面運(yùn)維要做的很多工作都由我們開發(fā)做了),就要針對(duì)變動(dòng)頻繁的需求設(shè)計(jì)出合理健壯的數(shù)據(jù)庫表結(jié)構(gòu)敞嗡。

首先颁糟,要按照三范式來建表,但是既然是范式就未必一定遵守喉悴,其實(shí)有些時(shí)候棱貌,比如某張表里面有很多需要頻繁加起來的字段,假設(shè)我們現(xiàn)在有一張結(jié)算表account粥惧,里面有三個(gè)字段:account_a键畴、account_b、account_c突雪,每次系統(tǒng)結(jié)算的時(shí)候都會(huì)從這個(gè)表里面取出這三個(gè)字段累加起來使用,那么我們可以違背范式規(guī)則涡贱,增加一個(gè)冗余字段:account_d咏删,存放前三個(gè)字段的和,這樣使用的時(shí)候就不需要取出前三個(gè)再計(jì)算问词,直接取最后一個(gè)字段即可督函。

在往數(shù)據(jù)庫批量插入數(shù)據(jù)的時(shí)候,有兩種方式激挪,一種是通過在接口實(shí)現(xiàn)類里對(duì)集合遍歷調(diào)用mapper辰狡,這樣每有一條數(shù)據(jù)就會(huì)調(diào)一次mapper插一次數(shù)據(jù)庫;另一種是使用mybatis的動(dòng)態(tài)語句foreach垄分,直接把一個(gè)集合插入宛篇,這樣只需要一次mybatis動(dòng)態(tài)代理,速度會(huì)比前一種快很多薄湿,但是需要注意的是mysql默認(rèn)接受sql的大小是1M叫倍,如果集合過大就會(huì)報(bào)異常,可以調(diào)整MySQL安裝目錄下的my.ini文件[mysqld段的"max_allowed_packet = 1M"來改變默認(rèn)接受的大小豺瘤。

關(guān)于索引的建立吆倦,這里的實(shí)踐真的是驚艷到我了,我測試將10萬條數(shù)據(jù)去和一張170萬條數(shù)據(jù)的表校驗(yàn)坐求,對(duì)比出這10萬條數(shù)據(jù)中哪些不在這表中蚕泽,粗略估計(jì)最差的情況就要計(jì)算10萬*170萬次,也就是1700億次桥嗤,當(dāng)然實(shí)際不可能這么多次须妻,我們只是假設(shè)每次校驗(yàn)都校驗(yàn)到最后一條數(shù)據(jù)才能找到派任。我第一次跑的時(shí)候走了70多秒:

然后我給where語句后面的字段加了索引,使用的是B+樹算法璧南,建立索引的過程也等了一小段時(shí)間掌逛,畢竟170萬條數(shù)據(jù),當(dāng)我建立完索引之后一運(yùn)行司倚,直接把我嚇到了:

不得不感嘆豆混,這索引的強(qiáng)大,不實(shí)踐真的沒有體會(huì)动知,以前只是停留在理論上皿伺,這里博主還發(fā)現(xiàn)了一件奇怪的事情,就是我測試完一遍得到上一張圖片的結(jié)果后又跑了一遍:

這里比上一次運(yùn)行又快了十倍盒粮,不知道這次的優(yōu)化是怎么出來的鸵鸥,是一個(gè)小謎題,如果有大神知道請(qǐng)?jiān)u論里指教丹皱。

2妒穴、適當(dāng)使用Redis存儲(chǔ)不常修改但是常用的數(shù)據(jù)

我們可以把常用且不常修改的信息暫存到Redis里面,比如系統(tǒng)維護(hù)的數(shù)據(jù)字典表里面的信息摊崭,這樣校驗(yàn)的時(shí)候直接從內(nèi)存中取讼油,避免了數(shù)據(jù)庫層的訪問,可以大大提高響應(yīng)速度呢簸,如果是大量信息校驗(yàn)的系統(tǒng)里面矮台,這種寫法將會(huì)很大限度的提升接口響應(yīng)速度。

下面是redis的部分配置內(nèi)容:

redis:

connect-timeout: 20s

database: 7

host: 127.0.0.1? ? #本地地址根时,實(shí)際項(xiàng)目看項(xiàng)目的地址

jedis:

pool:

max-active: 200

read-timeout: 20s

下面是具體使用的代碼:

/**

* 常用緩存鍵類

*

* @author Coder_gasenwell

* @version 1.0

* @createDate 2020/5/4

*/

public interface CacheKey {

/**

* 字典緩存前綴

*/

String PREFIX_TEST_SYS_DICT_TYPECODE = "test:sys_dict:typeCode:";

/**

* 字典緩存前綴

*/

String PREFIX_TEST_SYS_DEPT_USERS_TREE = "test:sys_dept_users:tree:";

}

@Autowired

private RedisUtils redisUtils;

//摘選一個(gè)方法作為參考瘦赫,只關(guān)注實(shí)現(xiàn)語句邏輯,每次先檢索redis里面是不是有緩存蛤迎,如果沒有就去數(shù)據(jù)庫取數(shù)據(jù)然后存緩存确虱,如果有的話就直接使用緩存。

@Override

public List getDictSetCache(String typeCode) {

List dictList = null;

// 確認(rèn)緩存中是否有字典忘苛,如果沒有有可能是第一次創(chuàng)建緩存

Set redisKeys = redisUtils.keys(CacheKey.PREFIX_TEST_SYS_DICT_TYPECODE + "*");

if (null != redisKeys && redisKeys.size() > 0) {

dictList = sysDictMapper.listByTypeCode(typeCode);

if (null != dictList && dictList.size() > 0) {

dictList.sort(new Comparator() {

@Override

//這里是對(duì)數(shù)據(jù)進(jìn)行一個(gè)排序蝉娜,可以忽略

public int compare(SysDict o1, SysDict o2) {

return o1.getSort() - o2.getSort();

}

});

redisUtils.set(CacheKey.PREFIX_TEST_SYS_DICT_TYPECODE + dictList.get(0).getTypeCode(), dictList);

}

3、使用異步處理扎唾,校驗(yàn)完核心數(shù)據(jù)即可返回前臺(tái)操作完成召川,并異步跑后續(xù)操作

雖然校驗(yàn)數(shù)據(jù)這里優(yōu)化速度很快了,但是校驗(yàn)完要把海量數(shù)據(jù)取出來進(jìn)行計(jì)算處理胸遇,所以為了避免從前端調(diào)一個(gè)接口讓用戶等太久荧呐,就把校驗(yàn)和后續(xù)計(jì)算分開,校驗(yàn)完成后把不存在的數(shù)據(jù)生產(chǎn)Excel表格導(dǎo)出,存在的數(shù)據(jù)開始走異步倍阐,此時(shí)返回前臺(tái)接口調(diào)用完成概疆。這時(shí)候前臺(tái)會(huì)告訴用戶校驗(yàn)完成,并下載到用戶本地一個(gè)Excel文件峰搪。

Spring里面內(nèi)置了@Async岔冀,所以這里開啟異步處理只需要在方法體上打上該注解即可,然后由主要方法調(diào)用該方法概耻。

4使套、多線程處理海量運(yùn)算

Spring是通過TaskExecutor來實(shí)現(xiàn)多線程的,使用Spring為我們提供的ThreadPoolTaskExecutor來實(shí)例化線程池鞠柄,因?yàn)槲覀兒芏鄷r(shí)候都是調(diào)用異步任務(wù)才會(huì)用到多線程侦高,所以往往都會(huì)配合在啟動(dòng)類上打上@EnableAsync注解,在實(shí)際方法上打@Async注解厌杜,就可以開啟多線程異步任務(wù)奉呛。

@SpringBootApplication(scanBasePackages = "cn.com.test")

@EnableFeignClients(basePackages = "cn.com.test")

@EnableAsync

@EnableDiscoveryClient

@EnableScheduling

@MapperScan("cn.com.test.mapper")

public class ThreadConfig implements AsyncConfigurer{

@Override

public Executor getAsyncExecutor() {

ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();

//核心線程數(shù)

threadPool.setCorePoolSize(8);

//最大線程數(shù)

threadPool.setMaxPoolSize(50);

//緩沖隊(duì)列

threadPool.setQueueCapacity(10);

threadPool.setAwaitTerminationSeconds(60);

threadPool.setThreadNamePrefix("TestThread:");

threadPool.initialize();

return threadPool;

}

@Override

public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

return null;

}

}

Doctor:我今天是不是很安靜,我的臺(tái)詞都沒了夯尽?

emmm瞧壮,寫的太專注,忘了給Doctor加戲了呐萌,下次一定馁痴。

好了,以上就是我的優(yōu)化實(shí)踐肺孤,這次實(shí)踐確實(shí)體會(huì)到性能優(yōu)化的強(qiáng)大之處,對(duì)于企業(yè)級(jí)開發(fā)的理解也更深了一步济欢,也頗有成就感赠堵,想起來我們畢業(yè)前院長說的一句話:希望你們隨著時(shí)間沉淀技術(shù)沉淀人格。技術(shù)和人格對(duì)于一個(gè)工程師來說法褥,都很重要茫叭。

歡迎批評(píng)指正。

原文作者:Coder_gasenwell

原文鏈接:https://blog.csdn.net/Coder_gasenwell/article/details/105905797

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末半等,一起剝皮案震驚了整個(gè)濱河市揍愁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杀饵,老刑警劉巖莽囤,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異切距,居然都是意外死亡辽剧,警方通過查閱死者的電腦和手機(jī)竭翠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門赃泡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兄淫,“玉大人,你說我怎么就攤上這事痹籍。” “怎么了?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵贺氓,是天一觀的道長。 經(jīng)常有香客問我床蜘,道長辙培,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任悄泥,我火速辦了婚禮虏冻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘弹囚。我一直安慰自己厨相,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布鸥鹉。 她就那樣靜靜地躺著蛮穿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毁渗。 梳的紋絲不亂的頭發(fā)上践磅,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音灸异,去河邊找鬼府适。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肺樟,可吹牛的內(nèi)容都是我干的檐春。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼么伯,長吁一口氣:“原來是場噩夢啊……” “哼疟暖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起田柔,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤俐巴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后硬爆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體欣舵,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年摆屯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邻遏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糠亩。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖准验,靈堂內(nèi)的尸體忽然破棺而出赎线,到底是詐尸還是另有隱情,我是刑警寧澤糊饱,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布垂寥,位于F島的核電站,受9級(jí)特大地震影響另锋,放射性物質(zhì)發(fā)生泄漏滞项。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一夭坪、第九天 我趴在偏房一處隱蔽的房頂上張望文判。 院中可真熱鬧,春花似錦室梅、人聲如沸戏仓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赏殃。三九已至,卻和暖如春间涵,著一層夾襖步出監(jiān)牢的瞬間仁热,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國打工勾哩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抗蠢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓思劳,卻偏偏與公主長得像物蝙,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敢艰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349