本文章大部分內(nèi)容來自官網(wǎng)https://shardingsphere.apache.org/document/current/cn/features/sharding/
垂直分片
按照業(yè)務(wù)拆分的方式稱為垂直分片狸捅,又稱為縱向拆分佃却,它的核心理念是專庫專用赃额。 在拆分之前僚匆,一個(gè)數(shù)據(jù)庫由多個(gè)數(shù)據(jù)表構(gòu)成,每個(gè)表對(duì)應(yīng)著不同的業(yè)務(wù)卵皂。而拆分之后顾犹,則是按照業(yè)務(wù)將表進(jìn)行歸類惕它,分布到不同的數(shù)據(jù)庫中沛贪,從而將壓力分散至不同的數(shù)據(jù)庫陋守。 下圖展示了根據(jù)業(yè)務(wù)需要震贵,將用戶表和訂單表垂直分片到不同的數(shù)據(jù)庫的方案。
垂直分片往往需要對(duì)架構(gòu)和設(shè)計(jì)進(jìn)行調(diào)整水评。通常來講猩系,是來不及應(yīng)對(duì)互聯(lián)網(wǎng)業(yè)務(wù)需求快速變化的;而且之碗,它也并無法真正的解決單點(diǎn)瓶頸蝙眶。 垂直拆分可以緩解數(shù)據(jù)量和訪問量帶來的問題季希,但無法根治褪那。如果垂直拆分之后,表中的數(shù)據(jù)量依然超過單節(jié)點(diǎn)所能承載的閾值式塌,則需要水平分片來進(jìn)一步處理博敬。
水平分片
水平分片又稱為橫向拆分。 相對(duì)于垂直分片峰尝,它不再將數(shù)據(jù)根據(jù)業(yè)務(wù)邏輯分類偏窝,而是通過某個(gè)字段(或某幾個(gè)字段),根據(jù)某種規(guī)則將數(shù)據(jù)分散至多個(gè)庫或表中武学,每個(gè)分片僅包含數(shù)據(jù)的一部分祭往。 例如:根據(jù)主鍵分片,偶數(shù)主鍵的記錄放入0庫(或表)火窒,奇數(shù)主鍵的記錄放入1庫(或表)硼补,如下圖所示。
水平分片從理論上突破了單機(jī)數(shù)據(jù)量處理的瓶頸熏矿,并且擴(kuò)展相對(duì)自由已骇,是分庫分表的標(biāo)準(zhǔn)解決方案。
挑戰(zhàn)
雖然數(shù)據(jù)分片解決了性能票编、可用性以及單點(diǎn)備份恢復(fù)等問題褪储,但分布式的架構(gòu)在獲得了收益的同時(shí),也引入了新的問題慧域。
面對(duì)如此散亂的分庫分表之后的數(shù)據(jù)鲤竹,應(yīng)用開發(fā)工程師和數(shù)據(jù)庫管理員對(duì)數(shù)據(jù)庫的操作變得異常繁重就是其中的重要挑戰(zhàn)之一。他們需要知道數(shù)據(jù)需要從哪個(gè)具體的數(shù)據(jù)庫的分表中獲取昔榴。
另一個(gè)挑戰(zhàn)則是宛裕,能夠正確的運(yùn)行在單節(jié)點(diǎn)數(shù)據(jù)庫中的SQL,在分片之后的數(shù)據(jù)庫中并不一定能夠正確運(yùn)行论泛。例如揩尸,分表導(dǎo)致表名稱的修改,或者分頁屁奏、排序岩榆、聚合分組等操作的不正確處理。
跨庫事務(wù)也是分布式的數(shù)據(jù)庫集群要面對(duì)的棘手事情。 合理采用分表勇边,可以在降低單表數(shù)據(jù)量的情況下犹撒,盡量使用本地事務(wù),善于使用同庫不同表可有效避免分布式事務(wù)帶來的麻煩粒褒。 在不能避免跨庫事務(wù)的場景识颊,有些業(yè)務(wù)仍然需要保持事務(wù)的一致性。 而基于XA的分布式事務(wù)由于在并發(fā)度高的場景中性能無法滿足需要奕坟,并未被互聯(lián)網(wǎng)巨頭大規(guī)模使用祥款,他們大多采用最終一致性的柔性事務(wù)代替強(qiáng)一致事務(wù)。
涉及到的技術(shù)月杉,版本
Sharding-jdbc最好和我的保持一致刃跛,其他的可以不一樣。
技術(shù) | 版本 |
---|---|
Sharding-jdbc | 4.0.0-RC1 |
mybatis-plus | 3.3.1.tmp |
HikariCP | 3.4.2 |
SpringBoot | 2.2.7 |
easy | 0.0.1-SNAPSHOT |
其中easy是自己在日常開發(fā)中總結(jié)的一個(gè)工具包苛萎,一個(gè)注解處理java8LocalDateTime格式化問題桨昙,SpringMVC統(tǒng)一異常處理,參數(shù)校驗(yàn)配置等等工具類項(xiàng)目地址:https://github.com/xiao-ren-wu/easy
約定
邏輯庫&邏輯表
Sharding-jdbc提供了邏輯表和邏輯庫的概念腌歉,供開發(fā)人員使用蛙酪,如圖
該示例中,將數(shù)據(jù)庫進(jìn)行水平拆分翘盖,分為db0和db1桂塞,又分別對(duì)表進(jìn)行了水平拆分,分別是db0里面的table0,table1和db1里面的table0和table1,他們對(duì)應(yīng)的邏輯庫為db,對(duì)應(yīng)的邏輯表為table,邏輯庫和邏輯表是抽象出來的最仑,不是真實(shí)存在的藐俺,邏輯表的結(jié)構(gòu)和物理表的結(jié)構(gòu)一致,這樣泥彤,開發(fā)人員只需要關(guān)心具體的業(yè)務(wù)邏輯欲芹,而不需要關(guān)系復(fù)雜的分庫分表。
配置方式
sharing-jdbc給我們提供了四種配置分庫分表的形式吟吝,分別是硬編碼形式菱父,SpringXML配置,yaml配置文件形式剑逃,和springboot配置文件形式浙宜。
參照官網(wǎng):https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/configuration/
我比較喜歡使用yaml配置文件的形式,因?yàn)槲艺J(rèn)為這樣不會(huì)給spingboot 配置文件造成”污染“蛹磺,還能達(dá)到‘專人專用“的效果粟瞬,缺點(diǎn)也很明顯,就是不能使用springboot提供的配置提示功能萤捆,不過沒關(guān)系裙品。
為了方便配置文件的使用俗批,我這做了一個(gè)簡單的約定,并編寫了一個(gè)簡答的工具類
約定
Yaml文件約定存放位置及其路徑
resource資源目錄下的sharding文件夾市怎,名字為dataSource-${spring.profile.active}.yml
讀取配置文件類
@Component
public class ShardingDataSourceYamlConfig {
private static final String SHARDING_FILE_NAME = "sharding/dataSource";
private static final String SUFFIX = ".yml";
private static final String WORD_SPLIT = "-";
@Resource
private ConfigurableEnvironment configurableEnvironment;
/**
* 根據(jù)spring-profile-active找到合適的sharding-jdbc配置文件
*
* @return sharding-jdbc config File
* @throws FileNotFoundException FileNotFoundException
* @throws FileAlreadyExistsException FileAlreadyExistsException
*/
public File getShardingFileByProfileActive() throws FileNotFoundException, FileAlreadyExistsException {
// 獲取classpath
String classPath = Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource("")).getPath();
// 獲取所有環(huán)境
String[] profiles = configurableEnvironment.getActiveProfiles();
if (profiles.length == 0) {
// default profile
String shardingConfigFileName = String.format("%s%s%s", classPath, SHARDING_FILE_NAME, SUFFIX);
File defaultShardingFile = new File(shardingConfigFileName);
if (!defaultShardingFile.exists()) {
throw new FileNotFoundException(shardingConfigFileName);
}
return defaultShardingFile;
} else {
// 如果存在多個(gè)符合的配置文件岁忘,因?yàn)樗麤]有上下級(jí)的關(guān)系,所以無法進(jìn)行歸并区匠,這里采用報(bào)錯(cuò)的形式返回
File profileShardingFile = null;
for (String profile : profiles) {
// 構(gòu)建文件地址
String shardingConfigFileName = String.format("%s%s%s%s%s", classPath, SHARDING_FILE_NAME, WORD_SPLIT, profile, SUFFIX);
File file = new File(shardingConfigFileName);
if (file.exists()) {
if (Objects.isNull(profileShardingFile)) {
profileShardingFile = file;
} else {
// 出現(xiàn)多個(gè)符合條件的配置文件干像,拋異常
throw new FileAlreadyExistsException("Sharding-jdbc配置文件找到多個(gè)");
}
}
}
if (Objects.isNull(profileShardingFile)) {
throw new FileNotFoundException("沒有找到Sharding-jdbc配置文件");
}
return profileShardingFile;
}
}
}
準(zhǔn)備好這些,我們就可以進(jìn)行整合了驰弄。