基于Hash散列,数据库路由组件设计 作者:小傅哥
博客: https://bugstack.cn (opens new window) 沉淀、分享、成长,让自己和他人都能有所收获!😄 # 一、前言 什么?Java 面试就像造火箭🚀
单纯了! 以前我也一直想 Java 面试就好好面试呗, 嘎哈么 总考一些工作中也用不到的玩意,会用 Spring 、 MyBatis 、 Dubbo 、 MQ ,把业务需求实现了不就行了! 但当工作几年后,需要提升自己( 要加钱 )的时候,竟然开始觉得自己只是一个调用 API 攒接口的工具人。没有知识宽度,没有技术纵深,也想不出来更没有意识,把日常开发的业务代码中通用的共性逻辑提炼出来,开发成公用的组件,更没有去思考日常使用的一些组件是用什么技术实现的。 所以有时候你说面试好像就是在造火箭,这些技术日常根本用不到,其实很多时候不是这个技术用不到,而是因为你没用( 嗯,以前我也没用 )。当你有这个想法想突破自己的薪资待遇瓶颈时,就需要去 了解了解必备的数据结构 、 学习学习Java的算法逻辑 、 熟悉熟悉通用的设计模式 、再结合像 Spring、ORM、RPC,这样的源码实现逻辑,把相应的技术方案赋能到自己的日常业务开发中,把共性的问题用聚焦和提炼的方式进行解决,这些才是你在 CRUD 之外的能力体现( 加薪筹码 )。 怎么? 好像听上去有道理,那么举个 栗子 ,来一场 数据库路由 的需求分析和逻辑实现! # 二、需求分析 如果要做一个数据库路由,都需要做什么技术点? 首先我们要知道为什么要用分库分表,其实就是由于业务体量较大,数据增长较快,所以需要把用户数据拆分到不同的库表中去,减轻数据库压力。 分库分表操作主要有垂直拆分和水平拆分: 垂直拆分:指按照业务将表进行分类,分布到不同的数据库上,这样也就将数据的压力分担到不同的库上面。最终一个数据库由很多表的构成,每个表对应着不同的业务,也就是专库专用。 水平拆分:如果垂直拆分后遇到单机瓶颈,可以使用水平拆分。相对于垂直拆分的区别是:垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中。如:user_001、user_002 而本章节我们要实现的也是水平拆分的路由设计,如图 1-1
那么,这样的一个数据库路由设计要包括哪些技术知识点呢? 是关于 AOP 切面拦截的使用,这是因为需要给使用数据库路由的方法做上标记,便于处理分库分表逻辑。 数据源的切换操作,既然有分库那么就会涉及在多个数据源间进行链接切换,以便把数据分配给不同的数据库。 数据库表寻址操作,一条数据分配到哪个数据库,哪张表,都需要进行索引计算。在方法调用的过程中最终通过 ThreadLocal 记录。 为了能让数据均匀的分配到不同的库表中去,还需要考虑如何进行数据散列的操作,不能分库分表后,让数据都集中在某个库的某个表,这样就失去了分库分表的意义。 综上,可以看到在数据库和表的数据结构下完成数据存放,我需要用到的技术包括: AOP 、 数据源切换 、 散列算法 、 哈希寻址 、 ThreadLoca l以及 SpringBoot的Starter开发方式 等技术。而像 哈希散列 、 寻址 、 数据存放 ,其实这样的技术与 HashMap 有太多相似之处, 那么学完源码造火箭的机会来了 如果你有过深入分析和学习过 HashMap 源码、Spring 源码、中间件开发,那么在设计这样的数据库路由组件时一定会有很多思路的出来。 接下来我们一起尝试下从源码学习到造火箭! # 三、技术调研 在 JDK 源码中,包含的数据结构设计有:数组、链表、队列、栈、红黑树,具体的实现有 ArrayList、LinkedList、Queue、Stack,而这些在数据存放都是顺序存储,并没有用到哈希索引的方式进行处理。而 HashMap、ThreadLocal,两个功能则用了哈希索引、散列算法以及在数据膨胀时候的拉链寻址和开放寻址,所以我们要分析和借鉴的也会集中在这两个功能上。 # 1. ThreadLocal
@Test
public void test_idx() {
int hashCode = 0;
for (int i = 0; i < 16; i++) {
hashCode = i * 0x61c88647 + 0x61c88647;
int idx = hashCode & 15;
System.out.println("斐波那契散列:" + idx + " 普通散列:" + (String.valueOf(i).hashCode() & 15));
}
}
斐波那契散列:7 普通散列:0
斐波那契散列:14 普通散列:1
斐波那契散列:5 普通散列:2
斐波那契散列:12 普通散列:3
斐波那契散列:3 普通散列:4
斐波那契散列:10 普通散列:5
斐波那契散列:1 普通散列:6
斐波那契散列:8 普通散列:7
斐波那契散列:15 普通散列:8
斐波那契散列:6 普通散列:9
斐波那契散列:13 普通散列:15
斐波那契散列:4 普通散列:0
斐波那契散列:11 普通散列:1
斐波那契散列:2 普通散列:2
斐波那契散列:9 普通散列:3
斐波那契散列:0 普通散列:4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26