本文主要將業(yè)界知名的開源分庫分表中間件—ShardingJdbc集成至SpringBoot工程中,利用ShardingJdbc的數(shù)據(jù)庫切分能力來實現(xiàn)庫表水平切分和擴(kuò)展的目標(biāo)妇穴,提高分布式系統(tǒng)整體的并發(fā)量,解決數(shù)據(jù)庫中的單表因數(shù)據(jù)量過大而帶來得各種瓶頸和影響(本文所述的ShardingJdbc中間件以其1.X版本為參考凉翻,2.X版本和1.X版本有較大區(qū)別蔽莱,在后面的文章中會有介紹)。
分庫分表中間件ShardingJdbc介紹
ShardingJdbc是當(dāng)當(dāng)開源的數(shù)據(jù)庫水平切分的中間件捶惜,其代表了客戶端類的分庫分表技術(shù)框架(這一點(diǎn)與MyCat不同,MyCat本質(zhì)上是一種數(shù)據(jù)庫代理)荔烧。ShardingJdbc定位為輕量級數(shù)據(jù)庫驅(qū)動吱七,由客戶端直連數(shù)據(jù)庫,以jar包形式提供服務(wù)鹤竭,未使用中間層踊餐,無需額外部署,無其他依賴臀稚,業(yè)務(wù)系統(tǒng)開發(fā)人員與數(shù)據(jù)庫運(yùn)維人員無需改變原有的開發(fā)與運(yùn)維方式吝岭。因此ShardingJdbc即為增強(qiáng)版的JDBC驅(qū)動,可以實現(xiàn)舊代碼遷移零成本的目標(biāo)吧寺。
下面的是ShardingJdbc的架構(gòu)圖:
從上面的架構(gòu)圖中窜管,可以看出ShardingJdbc與業(yè)務(wù)工程集成起來十分方便與快捷,同時其提供的分片規(guī)則配置稚机、SQL解析幕帆、SQL改寫、SQL路由赖条、SQL執(zhí)行以及結(jié)果歸并等強(qiáng)大的功能失乾,使得業(yè)務(wù)開發(fā)人員無需在這些方面花費(fèi)較大多的精力,而可以更加專注于業(yè)務(wù)流程的開發(fā)纬乍。其主要的特點(diǎn)如下:
(1)分片規(guī)則配置
ShardingJdbc的分片邏輯非常靈活碱茁,支持分片策略自定義、復(fù)數(shù)分片鍵蕾额、多運(yùn)算符分片等功能早芭。
(2)JDBC規(guī)范重寫
ShardingJdbc中間件對標(biāo)準(zhǔn)JDBC規(guī)范的重寫思路是針對DataSource、Connection诅蝶、Statement退个、PreparedStatement和ResultSet五個核心接口封裝,將多個JDBC實現(xiàn)類集合(如:MySQL JDBC實現(xiàn)/DBCP JDBC實現(xiàn)等)納入ShardingJdbc實現(xiàn)類管理调炬。
(3)SQL解析
SQL解析作為分庫分表類中間件框架的核心之一语盈,其性能和兼容性是最重要的衡量指標(biāo)。目前常見的SQL解析器主要有fdb/jsqlparser和Druid缰泡。在ShardingJdbc1.5.0之前的版本是采用解析速度相對最快的Druid作為SQL解析器刀荒。ShardingJdbc在1.5.0.M1 正式發(fā)布時代嗤,將SQL解析引擎從Druid替換成了自研的。新引擎僅解析分片上下文缠借,對于 SQL 采用“半理解”理念干毅,進(jìn)一步提升性能和兼容性,同時降低了代碼復(fù)雜度泼返。
(4)SQL改寫
這里一部分是將分表的邏輯表名稱替換為實際真實的分表名稱硝逢。另一部分是根據(jù)SQL解析結(jié)果替換一些在分片環(huán)境中不正確的功能。其中绅喉,1.5.0之前的版本渠鸽,SQL改寫是在SQL路由之前完成的,而在1.5.x中調(diào)整為SQL路由之后柴罐,因為SQL改寫可以根據(jù)路由至單庫表還是多庫表而進(jìn)行進(jìn)一步優(yōu)化徽缚。
(5)SQL路由
SQL路由是指根據(jù)分片規(guī)則配置,將待執(zhí)行的SQL定位至真正的DB數(shù)據(jù)源革屠。
(6)SQL執(zhí)行
這里指的是路由至真實的DB數(shù)據(jù)源后凿试,ShardingJdbc將采用多線程并發(fā)執(zhí)行SQL,并完成對addBatch等批量方法的處理似芝。
(7)結(jié)果歸并
ShardingJdbc支持通遍歷類红省、排序類、聚合類和分組類四種結(jié)果并歸方式国觉。普通遍歷類最為簡單,只需按順序遍歷ResultSet的集合即可虾啦。排序類結(jié)果則將結(jié)果先排序再輸出麻诀,因為各分庫的分片結(jié)果均按照各自條件完成排序,所以采用歸并排序算法整合最終結(jié)果傲醉。聚合類分為3種類型蝇闭,比較型、累加型和平均值型硬毕。分組類相對最為復(fù)雜呻引,需要將所有的ResultSet結(jié)果放入內(nèi)存,使用Map-Reduce算法分組吐咳,最后根據(jù)排序和聚合條件做相關(guān)處理逻悠。最為消耗內(nèi)存,損失性能的地方就是這里了韭脊,可以考慮使用limit合理的限制分組數(shù)據(jù)大小童谒。
在Spring Boot中實踐ShardingJdbc
本節(jié)將主要詳細(xì)介紹在SpringBoot工程中如何集成ShardingJdbc這款切分庫表的中間件,并使用其完成對庫表的切分/路由沪羔,以及在業(yè)務(wù)開發(fā)中的使用饥伊。
版本環(huán)境
Spring Boot 1.4.1.RELEASE
Druid 1.0.12
JDK 1.8
添加ShardingJdbc的pom依賴
因為當(dāng)當(dāng)開源了ShardingJdbc的源碼,我們可以通過maven倉庫來獲得jar包依賴。當(dāng)訪問http://mvnrepository.com/artifact/com.dangdang/sharding-jdbc-core(該鏈接為1.X版本的)選擇自己項目需要的版本(在本次集成中選擇的版本為1.4.2)琅豆,點(diǎn)擊進(jìn)入后復(fù)制maven內(nèi)容到pom.xml內(nèi)即可辽俗。同時辨泳,這里需要注意的是在ShardingJdbc 1.5.0.M1正式發(fā)布以前,SQL解析引擎仍然是采用阿里的Druid數(shù)據(jù)源連接池,還需要在Pom中引入Druid的依賴(版本為1.0.12)项乒,此外需要保證ShardingJdbc與Druid版本相對應(yīng),否則在執(zhí)行SQL解析時候會導(dǎo)致ShardingJdbc的運(yùn)行時異常猜揪。Pom文件的引入如下圖所示:
yaml文件配置
在MySql數(shù)據(jù)庫中創(chuàng)建相應(yīng)的分庫和分表后麸澜,需要在SpringBoot工程中的yaml配置文件(或property配置文件)中完成對分庫數(shù)據(jù)源的配置,具體如下:
? ?testShardingDB0:
? ? ?type: com.alibaba.druid.pool.DruidDataSource
? ? ?driverClassName: com.mysql.jdbc.Driver
? ? ?url: jdbc:mysql://{ip}:{port} /res_test_cloud_bill_sharding0?useUnicode=true&characterEncoding=utf-8
? ? ?username: xxxxxx
? ? ?password: xxxxxx
? ? ?max-idle: 500
? ? ?max-active: 100
? ? ?min-idle: 50
? ? ?initial-size: 50
? ? ?validation-query: SELECT 1
? ? ?max-wait: -1
? ? ?test-on-borrow: true
? ? ?test-on-return: true
? ? ?test-while-idle: true
? ? ?num-tests-per-eviction-run: 3
? ? ?time-between-eviction-runs-millis: 30000
? ? ?min-evictable-idle-time-millis: 18000000
? ? ?removeAbandoned: true
? ? ?removeAbandonedTimeout: 180
? ? ?# 打開PSCache,并且指定每個連接上PSCache的大小
? ? ?poolPreparedStatements: false
? ? ?maxPoolPreparedStatementPerConnectionSize: -1
? ? ?# 配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計,'wall'用于防火墻
? ? ?filters: stat,wall,log4j
? ? ?# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
? ? ?connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
? ? ?# 合并多個DruidDataSource的監(jiān)控數(shù)據(jù)
? ? ?useGlobalDataSourceStat: true
###分庫1配置,具體的ip/host:port和數(shù)據(jù)庫用戶名和密碼根據(jù)實際填寫
? ?testShardingDB1:
? ? ?type: com.alibaba.druid.pool.DruidDataSource
? ? ?driverClassName: com.mysql.jdbc.Driver
? ? ?url: jdbc:mysql:// {ip}:{port}/res_test_cloud_bill_sharding1?useUnicode=true&characterEncoding=utf-8
? ? ?username: xxxxxx
? ? ?password: xxxxxx
? ? ?max-idle: 500
? ? ?max-active: 100
? ? ?min-idle: 50
? ? ?initial-size: 50
? ? ?validation-query: SELECT 1
? ? ?max-wait: -1
? ? ?test-on-borrow: true
? ? ?test-on-return: true
? ? ?test-while-idle: true
? ? ?num-tests-per-eviction-run: 3
? ? ?time-between-eviction-runs-millis: 30000
? ? ?min-evictable-idle-time-millis: 18000000
? ? ?removeAbandoned: true
? ? ?removeAbandonedTimeout: 180
? ? ?# 打開PSCache,并且指定每個連接上PSCache的大小
? ? ?poolPreparedStatements: false
? ? ?maxPoolPreparedStatementPerConnectionSize: -1
? ? ?# 配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計,'wall'用于防火墻
? ? ?filters: stat,wall,log4j
? ? ?# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
? ? ?connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
? ? ?# 合并多個DruidDataSource的監(jiān)控數(shù)據(jù)
? ? ?useGlobalDataSourceStat: true
ShardingJdbc分庫分表的Bean配置
引入了ShardingJdbc中間件后翼雀,在SpringBoot工程中可以通過配置Bean來初始化分庫分表的數(shù)據(jù)源饱苟,其中需要設(shè)置邏輯表與實際分表的映射關(guān)系和分庫分表的ID路由策略。
@Configuration
@MapperScan(basePackages ? = "com.chinamobile.bcop.test.sharding.xxxxxxxx", ? sqlSessionTemplateRef = "shardingSqlSessionTemplate")
public ? class MybatisTestShardingConfig {
? ?@Bean(name = "testShardingDB0")
? ?@ConfigurationProperties(prefix = "spring.datasource.tesetShardingDB0")
? ?public DataSource testShardingDataSource0(){
? ? ? ?return new ? DruidDataSource();
? ?}
? ?@Bean(name = "testShardingDB1")
? ?@ConfigurationProperties(prefix = ? "spring.datasource.testShardingDB1")
? ?public DataSource testShardingDataSource1(){
? ? ? ?return new ? DruidDataSource();
? ?}
? ?@Bean(name = "dataSourceRule")
? ?public DataSourceRule ? dataSourceRule(@Qualifier("testShardingDB0") DataSource ds0, @Qualifier("testShardingDB1") ? DataSource ds1){
? ? ? ?Map dataSourceMap = new HashMap<>();
? ? ? ?dataSourceMap.put("shardingDataSource0", ? ds0);
? ? ? ?dataSourceMap.put("shardingDataSource1", ? ds1);
? ? ? ?return new ? DataSourceRule(dataSourceMap, "shardingDataSource0");
? ?}
? ?@Bean(name = ? "shardingRule")
? ?public ShardingRule ? shardingRule(@Qualifier("dataSourceRule")DataSourceRule ? dataSourceRule){
? ?//表策略
? ?//設(shè)置分表映射狼渊,將test_msg_queue_bill_record_0~test_msg_queue_bill_record_4
? ?// 幾個實際的表映射到test_msg_queue_bill_record邏輯表
? ?//0~4幾個表是真實的表箱熬,test_msg_queue_bill_record是個虛擬不存在的表,只是供使用
? ?TableRule orderTableRule = TableRule.builder("test_msg_queue_bill_record")
? ? ? ?.actualTables(Arrays.asList(
? ? ? ?"test_msg_queue_bill_record_0",
? ? ? ?"test_msg_queue_bill_record_1",
? ? ? ?"test_msg_queue_bill_record_2",
? ? ? ?"test_msg_queue_bill_record_3",
? ? ? ?"test_msg_queue_bill_record_4"))
? ? ? ?.dataSourceRule(dataSourceRule)
? ? ? ?.build();
? ?//綁定表策略,在查詢時會使用主表策略計算路由的數(shù)據(jù)源狈邑,因此需要約定綁定表策略的表的規(guī)則需要一致城须,可以一定程度提高效率
? ?List bindingTableRules = new ArrayList();
? ?bindingTableRules.add(new ? BindingTableRule(Arrays.asList(orderTableRule)));
? ?return ? ShardingRule.builder()
? ? ? ? ? ? ? ?.dataSourceRule(dataSourceRule) ? ?
? ? ? ? ? ? ? ?.tableRules(Arrays.asList(orderTableRule))
? ? ? ? ? ? ? ?.bindingTableRules(bindingTableRules)
? ? ? ? ? ? ? ?.databaseShardingStrategy(new DatabaseShardingStrategy("CUSTOMER_ID", new ? CustomizedDbShardingStrategy()))
? ? ? ? ? ? ? ?.tableShardingStrategy(new ? TableShardingStrategy("USER_ID", new ? CustomizedTableShardingStrategy()))
? ? ? ? ? ? ? ?.build();
? ?}
? ?@Bean(name = "testShardingDataSource")
? ?public DataSource ? shardingDataSource(@Qualifier("shardingRule")ShardingRule ? shardingRule){
? ? ? ?return ? ShardingDataSourceFactory.createDataSource(shardingRule);
? ?}
? ?//需要手動聲明配置事務(wù)
? ?@Bean(name = "testShardingTransactionManager")
? ?public DataSourceTransactionManager ? transactitonManager(@Qualifier("testShardingDataSource") DataSource ? dataSource){
? ? ? ?return new ? DataSourceTransactionManager(dataSource);
? ?}
//MyBatis ? SqlSessionFactory/SqlSessionTemplate/MybatisDAO的Bean初始化省略
//……..
在本文的示例中,按照業(yè)務(wù)要求先對原來的單庫單表進(jìn)行拆分米苹,由原來的單庫單表—testmsgqueuebillrecord糕伐,分成兩個庫的多表(每個庫中均有testmsgqueuebillrecord0~testmsgqueuebillrecord4五個分表,這里只是示例蘸嘶,在真實的業(yè)務(wù)場景下需要根據(jù)業(yè)務(wù)數(shù)據(jù)的總體容量來設(shè)定分庫分表的規(guī)模良瞧,究竟是分5個庫每個庫5表,還是分10個庫每個庫10表來滿足業(yè)務(wù)要求)训唱。由上面的分庫分表配置Bean的代碼可見褥蚯,使用Druid連接池初始化兩個分庫的數(shù)據(jù)源后設(shè)置ShardingJdbc數(shù)據(jù)源的分片規(guī)則。其中况增,testmsgqueuebillrecord表為邏輯表赞庶,分別與testmsgqueuebillrecord0~testmsgqueuebillrecord4的實際分表名建立映射。如果執(zhí)行的SQL為“select * from testmsgqueuebillrecord” 就能遍歷查完hwmsgqueuebillrecord0~4五個分表澳骤。最后歧强,在設(shè)置切分規(guī)則時候,分別以customerId和userId作為切分?jǐn)?shù)據(jù)庫和切分?jǐn)?shù)據(jù)表的路由規(guī)則宴凉。根據(jù)上面的配置可以得到下面的分庫分表邏輯視圖:
具體的庫表分片路由策略
ShardingJdbc對分片路由策略完全采用開放式的誊锭,將這一部分的控制權(quán)交給業(yè)務(wù)側(cè)開發(fā)人員手中。業(yè)務(wù)開發(fā)人員可以根據(jù)具體業(yè)務(wù)的需求弥锄,設(shè)置靈活的分庫分表路由策略丧靡。在上一節(jié)的示例代碼中蟆沫,可以看到在設(shè)置分庫分表規(guī)則的時候有兩個比較顯著的類——CustomizedDbShardingStrategy和CustomizedTableShardingStrategy。正是這兩個類完成了自定義的庫表的自定義分片路由策略温治,CustomizedDbShardingStrategy的示例代碼如下:
public ?class CustomizedDbShardingStrategy implements ? SingleKeyDatabaseShardingAlgorithm {
? ?@Override
? ?public String ? doEqualSharding(Collection databaseNames, ? ShardingValue shardingValue) {
? ? ? ? //在這里設(shè)置具體的條件為“=”的分庫策略
? ? ? ? //code here
? ? ? ?}
? ?@Override
? ?public Collection ? doInSharding(Collection databaseNames, ? ShardingValue shardingValue) {
? ? ? ?//在這里設(shè)置具體SQL語句條件為“In”的分庫策略
? ? ? ?//code here
? ?}
? ?@Override
? ?public Collection ? doBetweenSharding(Collection databaseNames, ? ShardingValue shardingValue) {
? ? ? ?//在這里設(shè)置具體的SQL語句條件為“between”的分庫策略
? ? ? ?//code here
? ?}
CustomizedTableShardingStrategy的示例代碼如下:
public class CustomizedTableShardingStrategy implements SingleKeyTableShardingAlgorithm {
? ?@Override
? ?public String doEqualSharding(Collection tableNames, ShardingValue shardingValue) {
? ? ? ?//在這里設(shè)置具體的條件為“=”的分表策略
? ? ? ?//code here
? ?}
? ?@Override
? ?public Collection doInSharding(Collection tableNames, ShardingValue shardingValue) {
? ? ? ?//在這里設(shè)置具體SQL語句條件為“In”的分表策略
? ? ? ?//code here
? ?}
? ?@Override
? ?public Collection doBetweenSharding(Collection tableNames, ShardingValue shardingValue) {
? ? ? ?//在這里設(shè)置具體的SQL語句條件為“between”的分表策略
? ? ? ?//code here
? ?}
}
先看分庫表路由策略中的doEqualSharding方法饭庞,比如對于SQL語句—“Insert into testmsgqueuebillrecord(CUSTOMERID,USERID,xxxx1,xxxx2……)values(‘testcustid111111’,’testuserid22222’,’xxxx1’,’xxxx2’……)”,doEqualSharding方法中的形參databaseNames為分庫的名稱(比如shardingDataSource0數(shù)據(jù)源對應(yīng)的實際分庫名稱為restestcloudbillsharding0),shardingValue為分庫的鍵值(在前一節(jié)分庫分表Bean配置中設(shè)定)熬荆,也就是對應(yīng)該SQL語句中的“testcustid111111”舟山,我們可以在doEqualSharding方法中設(shè)置適合業(yè)務(wù)需求的鍵值分片路由規(guī)則,比如以“testcustid111111”的哈希值作為分片路由規(guī)則來選擇該插入SQL究竟應(yīng)該路由至實際的哪個分庫的哪個分表來執(zhí)行卤恳。而另外的兩個方法累盗,doInSharding和doBetweenSharding則是作用在SQL條件為“Between”和“In”的語句上。
分布式主鍵生成
對于傳統(tǒng)的數(shù)據(jù)表設(shè)計而言突琳,主鍵自增都是基本需求若债。對MySQL而言,分庫分表之后拆融,不同表生成全局唯一主鍵是較為頭疼的問題蠢琳。因為同一個邏輯表所對應(yīng)的不同實際分表之間的自增鍵是無法感知,這樣會造成有部分分表的主鍵值重復(fù)镜豹。我們當(dāng)然可以通過分表的約束規(guī)則來達(dá)到數(shù)據(jù)不重復(fù)傲须,但是這需要引入額外的方法來解決重復(fù)性問題。
目前趟脂,有許多第三方技術(shù)方案可以解決該問題泰讽,比如flickr的全局主鍵生成方案和uuid的全局主鍵生成方案。ShardingJdbc在設(shè)計之初也有考慮過這個問題昔期,提供了自己的實現(xiàn)方案菇绵,其分布式全局ID自動生成器可以根據(jù)時間偏移量、工作進(jìn)程id和同一毫秒的自增量來生成一個64位的Long型數(shù)值以保證其全局唯一性镇眷。
在工程中需要在pom中添加依賴如下:
在配置中需要完成對其Bean對象的初始化,具體代碼如下:
@Bean(name = "shardingIdGenerator")
public IdGenerator getIdGenerator() {
? ?return new CommonSelfIdGenerator();
}
使用ShardingJdbc編寫自定義的Dao
經(jīng)過上面幾節(jié)內(nèi)容后翎嫡,這一節(jié)就可以使用配置后的ShardingJdbc所對應(yīng)的MybatisDao來完成一些業(yè)務(wù)的應(yīng)用了欠动。
public abstract class AbstractResBillShardingDao {
? ?@Qualifier(value = "hwShardingMyBatisDao")
? ?@Autowired
? ?protected IMybatisDAO mybatisShardingDao;
? ?@Qualifier(value = "shardingIdGenerator")
? ?@Autowired
? ?private IdGenerator idGenerator;
? ?public int shardingInsertRows (List resBillRecordList, String resType) {
? ? ? ?int resultRowNum = 0;
? ? ? ?try {
? ? ? ? ? ?String shardingInsertSqlMapper = MyBatisMapperMapping.ResBatchInsertMapper.getBatchInsertMapperByTypeCode(resType);
? ? ? ? ? ?if(StringUtils.isEmpty(shardingInsertSqlMapper)) {
? ? ? ? ? ? ? ?log.info("無法匹配到對應(yīng)的sqlmapper的statement,直接返回,無法匹配的資源類型為:{}",resType);
? ? ? ? ? ? ? ?return resultRowNum;
? ? ? ? ? ?}
? ? ? ? ? ?for(BillRecord msgQueueBillRecord:resBillRecordList) {
? ? ? ? ? ? ? ?//設(shè)置分布式id主鍵msgQueueBillRecord.setId(idGenerator.generateId().longValue());
? ? ? ? ? ?resultRowNum+=mybatisShardingDao.insert(shardingInsertSqlMapper, msgQueueBillRecord);
? ? ? ? ? ?}
? ? ? ?} catch (Exception ex) {
? ? ? ? ? ?log.error("批量插入數(shù)據(jù)出現(xiàn)異常", ex);
? ? ? ?}
? ? ? ?return resultRowNum;
? ?}
}
可以看到在這個Dao的類中,批量插入方法—shardingInsertBills惑申,通過自動注入的mybatisShardingDao和idGenerator來完成對數(shù)據(jù)記錄的切分插入具伍,這里需要注意ShardingJdbc目前尚未支持批量插入的SQL語句,因此需要在代碼中自己完成遍歷數(shù)據(jù)集合后的單行插入圈驼,這里idGenerator用于生成每個待插入記錄的全局唯一健值人芽。工程運(yùn)行的截圖日志如下:
使用ShardingJdbc的限制
ShardingJdbc中間件并非是萬能,它還是有一些SQL和JDCB接口的使用限制绩脆,其最大的使用限制在于如下幾點(diǎn):
(1)對于DataSource接口不支持超時相關(guān)的操作萤厅。
(2)對于Connection接口不支持存儲過程橄抹、游標(biāo)、函數(shù)惕味、savePoint楼誓、自定義類型映射等。
(3)對于Statement和PreparedStatement接口名挥,不支持返回多結(jié)果集的語句和國際化操作疟羹。
(4)對于ResultSet接口,不支持對于結(jié)果集指針位置判斷禀倔;不支持通過非next方法改變結(jié)果指針位置榄融;不支持修改結(jié)果集內(nèi)容。
(5)SQL語句限制:不支持HAVING救湖;不支持OR愧杯,UNION 和 UNION ALL;不支持特殊INSERT捎谨,尤其是是批量插入的SQL語句民效;不支持DISTINCT聚合;不支持dual虛擬表涛救;不支持SELECT LASTINSERTID()畏邢;不支持CASE WHEN。
總結(jié)
本文主要介紹了如何在Spring Boot工程中完成數(shù)據(jù)庫切分中間件—ShardingJdbc集成以及如何進(jìn)行分庫分表配置检吆、庫表分片路由策略設(shè)置和分布式主鍵生成關(guān)鍵點(diǎn)的闡述舒萎。限于筆者的才疏學(xué)淺,對本文內(nèi)容可能還有理解不到位的地方蹭沛,如有闡述不合理之處還望留言一起探討臂寝。在這里在給大家推薦一個架構(gòu)交流群:617434785,里面會分享一些資深架構(gòu)師錄制的視頻錄像:有Spring摊灭,MyBatis咆贬,Netty源碼分析,高并發(fā)帚呼、高性能掏缎、分布式、微服務(wù)架構(gòu)的原理煤杀,JVM性能優(yōu)化這些成為架構(gòu)師必備的知識體系眷蜈。還能領(lǐng)取免費(fèi)的學(xué)習(xí)資源。相信對于已經(jīng)工作和遇到技術(shù)瓶頸的碼友沈自,在這個群里會有你需要的內(nèi)容酌儒。