訂單量超過(guò)5000萬(wàn)怎么辦?

隨著電商平臺(tái)的快速崛起,很多互聯(lián)網(wǎng)公司開(kāi)始面臨著單實(shí)例存儲(chǔ)瓶頸的問(wèn)題.目前業(yè)內(nèi)主流的關(guān)系型數(shù)據(jù)庫(kù)主要就是mysql、oracle、postgre等,拿mysql來(lái)說(shuō),mysql數(shù)據(jù)庫(kù)是樹(shù)形結(jié)構(gòu),復(fù)雜度是logn,他的瓶頸主要來(lái)自頻繁的io的讀寫(xiě),對(duì)磁盤(pán)文件壓力比較大,并且隨著單表里面記錄的瘋狂增長(zhǎng),對(duì)于查詢來(lái)說(shuō),訂單表的深度也在增加.但有些人會(huì)說(shuō)那加索引呀,不好意思這個(gè)只能說(shuō)是一種優(yōu)化方式,沒(méi)有從本質(zhì)上解決問(wèn)題.

所以分庫(kù)分表應(yīng)運(yùn)而生,目前分庫(kù)分表有多種解決方案,比較流行的就是mycat和sharding jdbc,其中mycat基于阿里開(kāi)源的的cobar進(jìn)行開(kāi)發(fā)的,但是目前社區(qū)不是很活躍,而且反饋不是特別理想.這里推薦一下sharding jdbc,同樣也是優(yōu)秀的開(kāi)源作品,來(lái)自當(dāng)當(dāng)網(wǎng),只需要依賴相應(yīng)的jar包就行.兩者各有優(yōu)缺,具體的差異本文不在贅述,請(qǐng)自行谷歌.當(dāng)然如果你們公司的基礎(chǔ)團(tuán)隊(duì)研發(fā)實(shí)力很強(qiáng),也可以自研,原理還是圍繞著AOP思想绝页、sql解析、分片策略等來(lái)做,這塊下文會(huì)有demo進(jìn)行補(bǔ)充.

目前分片的算法主要有兩種:第一種就是Hash這塊算法上可以實(shí)現(xiàn)打散的很均勻,缺點(diǎn)是對(duì)于范圍查詢比較麻煩,第二種就是range.樓主所在的公司是按照訂單創(chuàng)建時(shí)間就行分表,對(duì)于近三個(gè)月的訂單我們是存在關(guān)系型數(shù)據(jù)庫(kù),超過(guò)三個(gè)月的訂單目前我們走的是Hbase,對(duì)于超過(guò)半年前的訂單,建議走離線,這塊主要大數(shù)據(jù)分析了,hive等.sharding jdbc提供了四個(gè)分片算法,可以根據(jù)自己的業(yè)務(wù)場(chǎng)景自己實(shí)現(xiàn)就行.

單分片鍵數(shù)據(jù)源分片算法SingleKeyDatabaseShardingAlgorithm

單分片表分片算法SingleKeyTableShardingAlgorithm

多分片鍵數(shù)據(jù)源分片算法MultipleKeyDatabaseShardingAlgorithm

多分片表分片算法MultipleKeyTableShardingAlgorithm

這四個(gè)算法都支持in妇萄、between溃列、equal.


但是對(duì)于訂單到底如何拆分呢?

首先看觀察最近一年每個(gè)月的訂單量,一般企業(yè)在數(shù)據(jù)庫(kù)建設(shè)的時(shí)候會(huì)考略未來(lái)三到五年的一個(gè)持續(xù)的增長(zhǎng),所以你的技術(shù)架構(gòu)應(yīng)該具有前瞻性.一般一個(gè)月只有幾十萬(wàn)或者幾百萬(wàn)的訂單量暫時(shí)就別分庫(kù)了,一般分表就可以了,這樣也避免了,分庫(kù)以后,帶來(lái)的分布式事務(wù)的問(wèn)題.如果每天的訂單量超過(guò)百萬(wàn)級(jí)別甚至千萬(wàn)級(jí)別,恭喜你,已經(jīng)在業(yè)內(nèi)小有名氣了.這塊具體的拆分,涉及到水平拆分和垂直拆分.水平拆分,想象一下橫著切西瓜的動(dòng)作,一般就是構(gòu)建子表,垂直拆分的話,這塊有些講究,首先要保證分表后這些表的并集是原來(lái)表的全集.一般來(lái)說(shuō),根據(jù)冷熱數(shù)據(jù),或者字典訪問(wèn)的頻率,來(lái)做分離.


由于前面有鋪墊,所以這塊講一下如何用AOP手動(dòng)實(shí)現(xiàn)分庫(kù)呢?

cglib+aspectj:

1).自定義annotation:

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface DataSource {

? ? EnumDataSourceType value();

}

2)假如這里有兩個(gè)數(shù)據(jù)庫(kù):

public enum EnumDataSourceType{

? ? ORDER0("order0", "默認(rèn)數(shù)據(jù)源"),

? ? ORDER1("order1", "order1數(shù)據(jù)源");

}

3)Aspect

@Component

@Aspect

public class DataSourceAspect {

@Pointcut("@annotation(com.example..switchdatasource.DataSource)")

private void dataSourcePointCut() {

}

@Around("dataSourcePointCut()")

public Object around(ProceedingJoinPoint joinPoint) {

try {

String dataSource = getDataSourceFromMethod(joinPoint);

//設(shè)置數(shù)據(jù)源

DataSourceSwitcher.setDatasourceKey(dataSource);

return joinPoint.proceed();

}catch (Throwable throwable) {

LogUtils.ERROR.error(joinPoint.getSignature().getDeclaringTypeName() +"." + joinPoint.getSignature().getName() +" error", throwable);

}finally {

DataSourceSwitcher.clearDataSourceType();

}

return? "ERROR";

}

private String getDataSourceFromMethod(ProceedingJoinPoint joinPoint) {

MethodSignature signature = (MethodSignature) joinPoint.getSignature();

Method method = signature.getMethod();

//判斷方法體上是否使用注解

? ? ? ? if (method.isAnnotationPresent(DataSource.class)) {

DataSource annotation = method.getAnnotation(DataSource.class);

//獲取該方法上的注解名

? ? ? ? ? ? String source = annotation.value().getSource();

//新order庫(kù)數(shù)據(jù)源

? ? ? ? ? ? if (source.equals(EnumDataSourceType.ORDER1.getSource()) {

return source;

}

}

LogUtils.COMMON.info("annotation by dataSource error, please check it.");

return null;

}

}

4)實(shí)現(xiàn)AbstractRoutingDataSource,這個(gè)是關(guān)鍵

public class DataSourceSwitcher extends AbstractRoutingDataSource {

private static final ThreadLocalDATASOURCE_KEY =new ThreadLocal<>();

public static void clearDataSourceType() {

DATASOURCE_KEY.remove();//清理ThreadLocal變量防止內(nèi)存泄露

}

@Override

? ? protected Object determineCurrentLookupKey() {

String s =DATASOURCE_KEY.get();

return s;

}

public static void setDatasourceKey(String dataSource) {

DATASOURCE_KEY.set(dataSource);

}

}


5)剩余的就是配置datasource文件

這塊注意事項(xiàng),需要指定一個(gè)默認(rèn)的數(shù)據(jù)源.


接下來(lái)重點(diǎn)說(shuō)一下sharding jdbc+spring boot如何實(shí)現(xiàn):

首先看一下sharding實(shí)現(xiàn)分片的流程圖:(這個(gè)圖是網(wǎng)上拷貝過(guò)來(lái)的,地址:https://www.cnblogs.com/mr-yang-localhost/p/8313360.html#_label1)

這塊抽重點(diǎn)講一下幾個(gè)模塊:

1.pom.xml首先maven引入依賴:

<dependency>

<groupId>io.shardingjdbc</groupId>

<artifactId>sharding-jdbc-core-spring-boot-starter</artifactId>

<version>這塊省了</version>

</dependency>

2.分表算法

public final class MultipleKeysModuloTableShardingAlgorithm implements MultipleKeysTableShardingAlgorithm {?

@Override?

public Collection<String> doSharding(final Collection<String> availableTargetNames, final Collection<ShardingValue<?>> shardingValues) {?

Set orderIdValueSet = getShardingValue(shardingValues,"order_id");?

Set userIdValueSet = getShardingValue(shardingValues,"user_id");?

List result =new ArrayList<>();? ? Set<List<Integer>> valueResult = Sets.cartesianProduct(userIdValueSet, orderIdValueSet);?

for (List<Integer> value : valueResult) {?

String suffix = Joiner.on("").join(value.get(0) % 2, value.get(1) % 2);?

for (String tableName : availableTargetNames) {?

if (tableName.endsWith(suffix)) {?

? ? ? ? ? ? ? ? ? ? result.add(tableName);?

? ? ? ? ? ? ? ? }?

? ? ? ? ? ? }?

? ? ? ? }?

return result;?

? ? }?


private Set<Integer> getShardingValue(final Collection<ShardingValue<?>> shardingValues, final String shardingKey) {?

Set valueSet =new HashSet<>();?

ShardingValue shardingValue =null;?

for (ShardingValue<?> each : shardingValues) {?

if (each.getColumnName().equals(shardingKey)) {?

? ? ? ? ? ? ? ? shardingValue = (ShardingValue<Integer>) each;?

break;?

? ? ? ? ? ? }?

? ? ? ? }?

if (null == shardingValue) {?

return valueSet;?

? ? ? ? }?

switch (shardingValue.getType()) {?

case SINGLE:?

? ? ? ? ? ? ? ? valueSet.add(shardingValue.getValue());?

break;?

case LIST:?

? ? ? ? ? ? ? ? valueSet.addAll(shardingValue.getValues());?

break;?

case RANGE:?

for (Integer i = shardingValue.getValueRange().lowerEndpoint(); i <= shardingValue.getValueRange().upperEndpoint(); i++) {?

? ? ? ? ? ? ? ? ? ? valueSet.add(i);?

? ? ? ? ? ? ? ? }?

break;?

default:?

throw new UnsupportedOperationException();?

? ? ? ? }?

return valueSet;?

? ? }?

}?


數(shù)據(jù)庫(kù)測(cè)試結(jié)果:



總結(jié):由于sharding jdbc還不支持動(dòng)態(tài)建表,所以子表目前還需要提前構(gòu)建,但是也可以通過(guò)其他方式去彌補(bǔ).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子刽射,更是在濱河造成了極大的恐慌军拟,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件誓禁,死亡現(xiàn)場(chǎng)離奇詭異懈息,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)摹恰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)辫继,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人俗慈,你說(shuō)我怎么就攤上這事姑宽。” “怎么了闺阱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵炮车,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我酣溃,道長(zhǎng)瘦穆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任救拉,我火速辦了婚禮难审,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亿絮。我一直安慰自己告喊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布派昧。 她就那樣靜靜地躺著黔姜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蒂萎。 梳的紋絲不亂的頭發(fā)上秆吵,一...
    開(kāi)封第一講書(shū)人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音五慈,去河邊找鬼纳寂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泻拦,可吹牛的內(nèi)容都是我干的毙芜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼争拐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腋粥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤隘冲,失蹤者是張志新(化名)和其女友劉穎闹瞧,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體展辞,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奥邮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纵竖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漠烧。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杏愤,死狀恐怖靡砌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情珊楼,我是刑警寧澤通殃,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站厕宗,受9級(jí)特大地震影響画舌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜已慢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一曲聂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧佑惠,春花似錦朋腋、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至赌厅,卻和暖如春穷绵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背特愿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工仲墨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人揍障。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓目养,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亚兄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子混稽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 無(wú)憂牽夢(mèng)望晨光 臥榻催人赴遠(yuǎn)方 兩季風(fēng)情食迥異 佳肴美艷齒留香
    藍(lán)色汪星人閱讀 262評(píng)論 0 1
  • 《誰(shuí)都不許占有大地》 他們寫(xiě)下:麥子,鹽,稻田匈勋,雪山寫(xiě)下村莊礼旅,縣城,工廠或者月亮把詞匯打上烙印使后來(lái)者成為拙劣的模...
    阿劍啊閱讀 559評(píng)論 11 18
  • 過(guò)去的五年是輝煌的五年洽洁,中國(guó)經(jīng)濟(jì)騰飛痘系,綜合國(guó)力增強(qiáng),國(guó)際地位提升饿自,人民幸福指數(shù)攀升汰翠,取得了讓世界矚目的成績(jī)。...
    Suou26閱讀 474評(píng)論 0 6
  • 馬克思的異化理論認(rèn)為昭雌,我們之所以不喜歡工作复唤,是因?yàn)樵诠ぷ髦校覀兊娜诵员荒ㄈチ酥蛭裕覀儽划惢闪藱C(jī)器上的零件佛纫。這給人...
    Sunny飛鏡閱讀 112評(píng)論 0 0