微信搜寻【阿丸笔记】,关注Java/MySQL/中间件各系列原创实战笔记,干货满满。
分库分表的文章网上十分多,然而大多内容比拟零散,以解说知识点为主,没有残缺地阐明一个大表的切分、新架构设计、上线的残缺过程。
因而,我联合去年做的一个大型分库分表我的项目,来复盘一下残缺的分库分表从架构设计 到 公布上线的实战总结。
1.前言
为什么须要做分库分表。这个置信大家多少都有所理解。
海量数据的存储和拜访成为了MySQL数据库的瓶颈问题,日益增长的业务数据,无疑对MySQL数据库造成了相当大的负载,同时对于零碎的稳定性和扩展性提出很高的要求。
而且单台服务器的资源(CPU、磁盘、内存等)总是无限的,最终数据库所能承载的数据量、数据处理能力都将遭逢瓶颈。
目前来说个别有两种计划。
一种是更换存储,不应用MySQL,比方能够应用HBase、polarDB、TiDB等分布式存储。
如果出于各种起因思考,还是想持续应用MySQL,个别会采纳第二种形式,那就是分库分表。
文章结尾就说了,网上分库分表文章很多,对知识点解说比拟多,因而,本文将不再过多赘述分库分表计划的范式解决。
而是专一于梳理分库分表从架构设计 到 公布上线的残缺过程,同时总结其中的注意事项和最佳实际。包含:
- 业务重构
- 技术架构设计
- 革新和上线
- 稳定性保障
- 项目管理
尤其是各个阶段的最佳实际,都是血与泪凝聚的经验教训。
2.第一阶段:业务重构(可选)
对于微服务划分比拟正当的分库分表行为,个别只须要关注存储架构的变动,或者只须要在个别利用上进行业务革新即可,个别不须要着重思考“业务重构” 这一阶段,因而,这一阶段属于“可选”。
本次我的项目的第一大难点,在于业务重构。
而本次拆分我的项目波及到的两张大表A和B,单表将近八千万的数据,是从单体利用时代遗留下来的,从一开始就没有很好的畛域驱动/MSA架构设计,逻辑发散十分重大,到当初曾经波及50+个在线服务和20+个离线业务的的间接读写。
因而,如何保障业务革新的彻底性、全面性是重中之重,不能呈现有脱漏的状况。
另外,表A 和 表B 各自有二、三十个字段,两表的主键存在一一对应关系,因而,本次分库分表我的项目中,还须要将两个表进行重构交融,将多余/无用的字段剔除。
2.1 查问统计
在线业务通过分布式链路追踪零碎进行查问,依照表名作为查问条件,而后依照服务维度进行聚合,找到所有相干服务,写一个文档记录相干团队和服务。
这里特地留神下,很多表不是只有在线利用在应用,很多离线算法和数据分析的业务也在应用,这里须要一并的梳理好,做好线下跨团队的沟通和调研工作,免得切换后影响失常的数据分析。
2.2 查问拆分与迁徙
创立一个jar包,依据2.1的统计后果,与服务owner单干将服务中的相干查问都迁徙到这个jar包中(本我的项目的jar包叫projected),此处为1.0.0-SNAPSHOT版本。
而后将本来服务内的xxxMapper.xxxMethod( ) 全副改成projectdb.xxxMethod( )进行调用。
这样做有两个益处:
- 不便做后续的查问拆分剖析。
- 不便后续间接将jar包中的查问替换为革新后 中台服务 的rpc调用,业务方只需降级jar包版本,即可疾速从sql调用改为rpc查问。
这一步花了几个月的理论,务必梳理各个服务做全面的迁徙,不能脱漏,否则可能会导致拆分剖析不全面,脱漏了相干字段。
查问的迁徙次要因为本次拆分我的项目波及到的服务太多,须要收拢到一个jar包,更不便前期的革新。如果理论分库分表我的项目中仅仅波及一两个服务的,这一步是能够不做的。
2.3 联结查问的拆分剖析
依据2.2收拢的jar包中的查问,结合实际状况将查问进行分类和判断,把一些历史遗留的问题,和曾经废除的字段做一些整顿。
以下举一些思考点。
1)哪些查问是无奈拆分的?例如分页(尽可能地革新,切实改不了只能以冗余列的模式)
2)哪些查问是能够业务上join拆分的?
3)哪些表/字段是能够交融的?
4)哪些字段须要冗余?
5)哪些字段能够间接废除了?
6)依据业务具体场景和sql整体统计,辨认要害的分表键。其余查问走搜寻平台。
思考后失去一个查问革新总体思路和计划。
同时在本我的项目中须要将两张表交融为一张表,废除冗余字段和有效字段。
2.4 新表设计
这一步基于2.3对于查问的拆分剖析,得出旧表交融、冗余、废除字段的后果,设计新表的字段。
产出新表设计构造后,必须发给各个相干业务方进行review,并保障所有业务方都通过该表的设计。有必要的话能够进行一次线下review。
如果新表的过程中,对局部字段进行了废除,必须告诉所有业务方进行确认。
对于新表的设计,除了字段的梳理,也须要依据具体查问,从新设计、优化索引。
2.5 第一次降级
新表设计实现后,先做一次jar包内sql查问的革新,将旧的字段全副更新为新表的字段。
此处为2.0.0-SNAPSHOT版本。
而后让所有服务降级jar包版本,以此来保障这些废除字段的确是不应用了,新的表构造字段可能齐全笼罩过来的业务场景。
特地留神的是,因为波及服务泛滥,能够将服务依照 非核心 与 外围 辨别,而后分批次上线,避免出现问题导致重大故障或者大范畴回滚。
2.5 最佳实际
2.6.1 尽量不扭转原表的字段名称
在做新表交融的时候,一开始只是简略归并表A 和 表B的表,因而很多字段名雷同的字段做了重命名。
起初字段精简过程中,删除了很多反复字段,然而没有将重命名的字段改回来。
导致前期上线的过程中,不可避免地须要业务方进行重构字段名。
因而,新表设计的时候,除非必不得已,不要批改原表的字段名称!
2.6.2 新表的索引须要认真斟酌
新表的索引不能简略照搬旧表,而是须要依据查问拆分剖析后,从新设计。
尤其是一些字段的交融后,可能能够归并一些索引,或者设计一些更高性能的索引。
2.6 本章小结
至此,分库分表的第一阶段告一段落。这一阶段所需工夫,齐全取决于具体业务,如果是一个历史包袱惨重的业务,那可能须要破费几个月甚至半年的工夫能力实现。
这一阶段的实现品质十分重要,否则可能导致我的项目前期须要重建表构造、从新全量数据。
这里再次阐明,对于微服务划分比拟正当的服务,分库分表行为个别只须要关注存储架构的变动,或者只须要在个别利用上进行业务革新即可,个别不须要着重思考“业务重构” 这一阶段。
3.第二阶段:存储架构设计(外围)
对于任何分库分表的我的项目,存储架构的设计都是最外围的局部!
3.1 整体架构
依据第一阶段整顿的查问梳理后果,咱们总结了这样的查问法则。
- 80%以上的查问都是通过或者带有字段pk1、字段pk2、字段pk3这三个维度进行查问的,其中pk1和pk2因为历史起因存在一一对应的关系
- 20%的查问千奇百怪,包含含糊查问、其余字段查问等等
因而,咱们设计了如下的整体架构,引入了数据库中间件、数据同步工具、搜索引擎(阿里云opensearch/ES)等。
下文的阐述都是围绕这个架构来开展的。
3.1.1 mysql分表存储
Mysql分表的维度是依据查问拆分剖析的后果确定的。
咱们发现pk1pk2pk3能够笼罩80%以上的次要查问。让这些查问依据分表键间接走mysql数据库即可。
原则上个别最多保护一个分表的全量数据,因为过多的全量数据会造成存储的节约、数据同步的额定开销、更多的不稳定性、不易扩大等问题。
然而因为本我的项目pk1和pk3的查问语句都对实时性有比拟高的要求,因而,保护了pk1和pk3作为分表键的两份全量数据。
而pk2和pk1因为历史起因,存在一一对应关系,能够仅保留一份映射表即可,只存储pk1和pk2两个字段。
3.1.2 搜寻平台索引存储
搜寻平台索引,能够笼罩残余20%的零散查问。
这些查问往往不是依据分表键进行的,或者是带有含糊查问的要求。
对于搜寻平台来说,个别不存储全量数据(尤其是一些大varchar字段),只存储主键和查问须要的索引字段,搜寻失去后果后,依据主键去mysql存储中拿到须要的记录。
当然,从前期实际后果来看,这里还是须要做一些衡量的:
1)有些非索引字段,如果不是很大,也能够冗余进来,相似笼罩索引,防止多一次sql查问;
2)如果表构造比较简单,字段不大,甚至能够思考全量存储,进步查问性能,升高mysql数据库的压力。
这里特地提醒,搜索引擎和数据库之间同步是必然存在提早的。所以对于依据分表id查问的语句,尽量保障间接查询数据库,这样不会带来一致性问题的隐患。
3.1.3 数据同步
个别新表和旧表间接能够采纳 数据同步 或者 双写的形式进行解决,两种形式有各自的优缺点。
个别依据具体情况抉择一种形式就行。
本次我的项目的具体同步关系见整体存储架构,包含了四个局部:
1)旧表到新表全量主表的同步
一开始为了缩小代码入侵、不便扩大,采纳了数据同步的形式。而且因为业务过多,放心有未统计到的服务没有及时革新,所以数据同步能防止这些状况导致数据失落。
然而在上线过程中发现,当提早存在时,很多新写入的记录无奈读到,对具体业务场景造成了比较严重的影响。(具体起因参考4.5.1的阐明)
因而,为了满足利用对于实时性的要求,咱们在数据同步的根底上,从新在3.0.0-SNAPSHOT版本中革新成了双写的模式。
2)新表全量主表到全量副表的同步
3)新表全量主表到映射表到同步
4)新表全量主表到搜索引擎数据源的同步
2)、3)、4)都是从新表全量主表到其余数据源的数据同步,因为没有强实时性的要求,因而,为了不便扩大,全副采纳了数据同步的形式,没有进行更多的多写操作。
3.2 容量评估
在申请mysql存储和搜寻平台索引资源前,须要进行容量评估,包含存储容量和性能指标。
具体线上流量评估能够通过监控零碎查看qps,存储容量能够简略认为是线上各个表存储容量的和。
然而在全量同步过程中,咱们发现须要的理论容量的需要会大于预估,具体能够看3.4.6的阐明。
具体性能压测过程就不再赘述。
3.3 数据校验
从上文能够看到,在本次我的项目中,存在大量的业务革新,属于异构迁徙。
从过来的一些分库分表我的项目来说,大多是同构/对等拆分,因而不会存在很多简单逻辑,所以对于数据迁徙的校验往往比拟漠视。
在齐全对等迁徙的状况下,个别的确比拟少呈现问题。
然而,相似这样有比拟多革新的异构迁徙,校验相对是重中之重!!
因而,必须对数据同步的后果做校验,保障业务逻辑革新正确、数据同步一致性正确。这一点十分十分重要。
在本次我的项目中,存在大量业务逻辑优化以及字段变动,所以咱们独自做了一个校验服务,对数据的全量、增量进行校验。
过程中提前发现了许多数据同步、业务逻辑的不统一问题,给咱们本次我的项目安稳上线提供了最重要的前提保障!!
3.4 最佳实际
3.4.1 分库分表引起的流量放大问题
在做容量评估的时候,须要关注一个重要问题。就是分表带来的查问流量放大。
这个流量放大有两方面的起因:
- 索引表的二次查问。比方依据pk2查问的,须要先通过pk2查问pk1,而后依据pk1查问返回后果。
- in的分批查问。如果一个select...in...的查问,数据库中间件会依据分表键,将查问拆分落到对应的物理分表上,相当于本来的一次查问,放大为屡次查问。(当然,数据库会将落在同一个分表的id作为一次批量查问,而这是不稳固的合并)
因而,咱们须要留神:
- 业务层面尽量限度in查问数量,防止流量过于放大;
- 容量评估时,须要思考这部分放大因素,做适当冗余,另外,后续会提到业务革新上线分批进行,保障能够及时扩容;
- 分64、128还是256张表有个正当预估,拆得越多,实践上会放大越多,因而不要无谓地分过多的表,依据业务规模做适当预计;
- 对于映射表的查问,因为存在显著的冷热数据,所以咱们又在两头加了一层缓存,缩小数据库的压力
3.4.2 分表键的变更计划
本我的项目中,存在一种业务状况会变更字段pk3,然而pk3作为分表键,在数据库中间件中是不能批改的,因而,只能在中台中批改对pk3的更新逻辑,采纳先删除、后增加的形式。
这里须要留神,删除和增加操作的事务原子性。当然,简略解决也能够通过日志的形式,进行告警和校准。
3.4.3 数据同步一致性问题
咱们都晓得,数据同步中一个关键点就是(音讯)数据的程序性,如果不能保障承受的数据和产生的数据的程序严格统一,就有可能因为(音讯)数据乱序带来数据笼罩,最终带来不统一问题。
咱们自研的数据同步工具底层应用的音讯队列是kakfa,,kafka对于音讯的存储,只能做到部分有序性(具体来说是每一个partition的有序)。咱们能够把同一主键的音讯路由至同一分区,这样一致性个别能够保障。然而,如果存在一对多的关系,就无奈保障每一行变更有序,见如下例子。
那么须要通过反查数据源获取最新数据保障一致性。
然而,反查也不是“银弹“,须要思考两个问题。
1)如果音讯变更来源于读写实例,而反查 数据库是查只读实例,那就会存在读写实例提早导致的数据不统一问题。因而,须要保障 音讯变更起源 和 反查数据库 的实例是同一个。
2)反查对数据库会带来额定性能开销,须要认真评估全量时候的影响。
3.4.4 数据实时性问题
提早次要须要留神几方面的问题,并依据业务理论状况做评估和掂量。
1)数据同步平台的秒级提早
2)如果音讯订阅和反查数据库都是落在只读实例上,那么除了上述数据同步平台的秒级提早,还会有数据库主从同步的提早
3)宽表到搜寻平台的秒级提早
只有可能满足业务场景的计划,才是适合的计划。
3.4.5 分表后存储容量优化
因为数据同步过程中,对于单表而言,不是严格依照递增插入的,因而会产生很多”存储空洞“,使得同步完后的存储总量远大于预估的容量。
因而,在新库申请的时候,存储容量多申请50%。
具体起因能够参考我的这篇文章 为什么MySQL分库分表后总存储大小变大了?
3.5 本章小结
至此,分库分表的第二阶段告一段落。
这一阶段踩了十分多的坑。
一方面是设计高可用、易扩大的存储架构。在我的项目停顿过程中,也做了屡次的批改与探讨,包含mysql数据冗余数量、搜寻平台的索引设计、流量放大、分表键批改等问题。
另一方面是“数据同步”自身是一个非常复杂的操作,正如本章最佳实际中提及的实时性、一致性、一对多等问题,须要引起高度重视。
因而,更加依赖于数据校验对最终业务逻辑正确、数据同步正确的测验!
在实现这一阶段后,能够正式进入业务切换的阶段。须要留神的是,数据校验依然会在下一阶段施展关键性作用。
4.第三阶段:革新和上线(谨慎)
前两个阶段实现后,开始业务切换流程,次要步骤如下:
1)中台服务采纳单读 双写 的模式
2)旧表往新表开着数据同步
3) 所有服务降级依赖的projectDB版本,上线RPC,如果呈现问题,降版本即可回滚(上线胜利后,单读新库,双写新旧库)
4)查看监控确保没有 中台服务 以外的其余服务拜访旧库旧表
5)进行数据同步
6)删除旧表
4.1 查问革新
如何验证咱们前两个阶段设计是否正当?是否齐全笼罩查问的批改 是一个前提条件。
当新表设计结束后,就能够以新表为规范,批改老的查问。
以本我的项目为例,须要将旧的sql在 新的中台服务中 进行革新。
1)读查问的革新
可能查问会波及以下几个方面:
a)依据查问条件,须要将pk1和pk2的inner join改为对应分表键的新表表名
b)局部sql的废除字段解决
c)非分表键查问改为走搜寻平台的查问,留神保障语义统一
d)留神写单测防止低级谬误,次要是DAO层面。
只有新表构造和存储架构能齐全适应查问革新,能力认为后面的设计临时没有问题。
当然,这里还有个前提条件,就是相干查问曾经全副收拢,没有脱漏。
2) 写查问的革新
除了相干字段的更改以外,更重要的是,须要革新为旧表、新表的双写模式。
这里可能波及到具体业务写入逻辑,本我的项目尤为简单,须要革新过程中与业务方充沛沟通,保障写入逻辑正确。
能够在双写上各加一个配置开关,不便切换。如果双写中发现新库写入有问题,能够疾速敞开。
同时,双写过程中不敞开 旧库到新库 的数据同步。
为什么呢?次要还是因为咱们我的项目的特殊性。因为咱们波及到几十个服务,为了升高危险,必须分批上线。因而,存在比拟麻烦的两头态,一部分服务是老逻辑,一部分服务是新逻辑,必须保障两头态的数据正确性,具体见4.5.1的剖析。
4.2 服务化革新
为什么须要新建一个 服务来 承载革新后的查问呢?
一方面是为了革新可能不便的降级与回滚切换,另一方面是为了将查问收拢,作为一个中台化的服务来提供相应的查问能力。
将革新后的新的查问放在服务中,而后jar包中的本来查问,全副替换成这个服务的client调用。
同时,降级jar包版本到3.0.0-SNAPSHOT。
4.3 服务分批上线
为了升高危险,须要安顿从非核心服务到外围服务的分批上线。
留神,分批上线过程中,因为写服务往往是外围服务,所以安顿在前面。可能呈现非核心的读服务上线了,这时候会有读新表、写旧表的中间状态。
1) 所有相干服务应用 重构分支 降级projectdb版本到3.0.0-SNAPSHOT并部署内网环境;
2) 业务服务依赖于 中台服务,须要订阅服务
3) 开重构分支(不要与失常迭代分支合并),部署内网,内网预计测试两周以上
应用一个新的 重构分支 是为了在内网测试两周的时候,不影响业务失常迭代。每周更新的业务分支能够merge到重构分支上部署内网,而后外网应用业务分支merge到master上部署。
当然,如果从线上线下代码分支统一的角度,也能够重构分支和业务分支一起测试上线,对开发和测试的压力会较大。
4)分批上线过程中,如果碰到依赖抵触的问题,须要及时解决并及时更新到该文档中
5)服务上线前,必须要求业务开发或者测试,明确评估具体api和危险点,做好回归。
这里再次揭示,上线实现后,请不要漏掉离线的数据分析业务!请不要漏掉离线的数据分析业务!请不要漏掉离线的数据分析业务!
4.4 旧表下线流程
1)查看监控确保没有中台服务以外的其余服务拜访旧库旧表
2)查看数据库上的sql审计,确保没有其余服务依然读取旧表数据
3)进行数据同步
4)删除旧表
4.5 最佳实际
4.5.1 写完立刻读可能读不到
在分批上线过程中,遇到了写完立刻读可能读不到的状况。因为业务泛滥,咱们采纳了分批上线的形式升高危险,存在一部分利用曾经降级,一部分利用尚未降级的状况。未降级的服务依然往旧表写数据,而降级后的利用会从新表读数据,当提早存在时,很多新写入的记录无奈读到,对具体业务场景造成了比较严重的影响。
提早的起因次要有两个:
1)写服务还没有降级,还没有开始双写,还是写旧表,这时候会有读新表、写旧表的中间状态,新旧表存在同步提早。
2)为了防止主库压力,新表数据是从旧表获取变更、而后反查旧表只读实例的数据进行同步的,主从库自身存在肯定提早。
解决方案个别有两种:
1)数据同步改为双写逻辑。
2)在读接口做弥补,如果新表查不到,到旧表再查一次。
4.5.2 数据库中间件惟一ID替换自增主键(划重点,敲黑板)
因为分表后,持续应用单表的自增主键,会导致全局主键抵触。因而,须要应用分布式惟一ID来代替自增主键。各种算法网上比拟多,本我的项目采纳的是数据库自增sequence生成形式。
数据库自增sequence的分布式ID生成器,是一个依赖Mysql的存在, 它的基本原理是在Mysql中存入一个数值, 每有一台机器去获取ID的时候,都会在以后ID上累加肯定的数量比如说2000, 而后把以后的值加上2000返回给服务器。这样每一台机器都能够持续反复此操作取得惟一id区间。
然而仅仅有全局惟一ID就功败垂成了吗?显然不是,因为这里还会存在新旧表的id抵触问题。
因为服务比拟多,为了升高危险须要分批上线。因而,存在一部分服务还是单写旧表的逻辑,一部分服务是双写的逻辑。
这样的状态中,旧表的id策略应用的是auto_increment。如果只有单向数据来往的话(旧表到新表),只须要给旧表的id预留一个区间段,sequence从一个较大的起始值开始就能防止抵触。
但该我的项目中,还有新表数据和旧表数据的双写,如果采纳上述计划,较大的id写入到旧表,旧表的auto_increment将会被重置到该值,这样单鞋旧表的服务产生的递增id的记录必然会呈现抵触。
所以这里替换了单方的区间段,旧库从较大的auto_increment起始值开始,新表抉择的id(也就是sequence的范畴)从大于旧表的最大记录的id开始递增,小于旧表auto_increment行将设置的起始值,很好的防止了id抵触问题。
1)切换前:
sequence的起始id设置为以后旧表的自增id大小,而后旧表的自增id须要改大,预留一段区间,给旧表的自增id持续应用,避免未降级业务写入旧表的数据同步到新库后产生id抵触;
2)切换后
无需任何革新,断开数据同步即可
3)长处
只用一份代码;
切换能够应用开关进行,不必降级革新;
如果万一中途旧表的autoincrement被异样数据变大了,也不会造成什么问题。
4)毛病
如果旧表写失败了,新表写胜利了,须要日志辅助解决
4.6 本章小结
实现旧表下线后,整个分库分表的革新就实现了。
在这个过程中,须要始终保持对线上业务的敬畏,认真思考每个可能产生的问题,想好疾速回滚计划(在三个阶段提到了projectdb的jar包版本迭代,从1.0.0-SNAPSHOT到3.0.0-SNAPSHOT,蕴含了每个阶段不同的变更,在不同阶段的分批上线的过程中,通过jar包版本的形式进行回滚,施展了巨大作用),防止造成重大故障。
5.稳定性保障
这一章次要再次强调稳定性的保障伎俩。作为本次我的项目的重要指标之一,稳定性其实贯通在整个我的项目周期内,基本上在上文各个环节都曾经都有提到,每一个环节都要引起足够的器重,认真设计和评估计划,做到成竹在胸,而不是靠天吃饭:
1)新表设计必须跟业务方充沛沟通、保障review。
2)对于“数据同步”,必须有数据校验保障数据正确性,可能导致数据不正确的起因上文曾经提到来很多,包含实时性、一致性的问题。保证数据正确是上线的大前提。
3)每一阶段的变动,都必须做好疾速回滚都预案。
4)上线过程,都以分批上线的模式,从非核心业务开始做试点,防止故障扩充。
5)监控告警要配置全面,呈现问题及时收到告警,疾速响应。不要疏忽,很重要,有几次呈现选过数据的小问题,都是通过告警及时发现和解决的
6)单测,业务功能测试等要充沛
6.项目管理之跨团队合作
对于“跨团队合作”,本文专门拎进去作为一章。
因为在这样一个跨团队的大型项目革新过程中,迷信的团队合作是保障整体我的项目按时、高质量实现的不可短少的因素。
上面,分享几点心得与领会。
6.1 所有文档后行
团队合作最忌“空口无凭”。
无论是团队分工、进度安顿或是任何须要多人合作的事件,都须要有一个文档记录,用于追踪进度,把控流程。
6.2 业务沟通与确认
所有的表构造革新,必须跟相干业务方沟通,对于可能存在的历史逻辑,进行全面梳理;
所有探讨确定后的字段革新,必须由每个服务的Owner进行确认。
6.3 责任到位
对于多团队多人次的合作项目,每个团队都应该明确一个对接人,由我的项目总负责人与团队惟一对接人沟通,明确团队残缺进度和实现品质。
7.瞻望
其实,从全文的篇幅就可能看出,本次的分库分表我的项目因为简单的业务逻辑革新,费大量的工夫和精力,并且非常容易在革新过程中,引起不稳固的线上问题。
本文复盘了整个分库分表从拆分、设计、上线的整体过程,心愿能对大家有所帮忙。
看到这里,咱们会想问一句。所以,有没有更好的形式呢?
兴许,将来还是须要去联合业界新的数据库中间件技术,可能疾速实现分库分表。
兴许,将来还能够引入新的数据存储技术与计划(polardb、tidb、hbase),基本不再须要分库分表呢?
持续跟进新技术的倒退,我置信会找到答案。
都看到最初了,原创不易,点个关注,点个赞吧~文章继续更新,能够微信搜寻「阿丸笔记 」第一工夫浏览,回复【笔记】获取Canal、MySQL、HBase、JAVA实战笔记,回复【材料】获取一线大厂面试材料。
常识碎片从新梳理,构建Java常识图谱:github.com/saigu/JavaK…(历史文章查阅十分不便)