mysql数据库水平拆分、垂直拆分、ShardingSphere-JDBC、mysql存储原理
文章目录
一道经常考的题,必须掌握。
注:拆分虽然不难,但是有很多细节,也不要轻易拆。
什么情况下需要拆分?
主要需要考虑两个维度:
1、条数
如果是mysql库,单表超过1000万就需要拆分了,因为无论如何加索引、优化查询可能还是解决不了问题。
如果是oracle库,单表10亿以内都可以通过优化来解决,这里不这么高了,1亿吧,超过1亿就需要拆分了。
2、字段数
字段数过多也会影响性能。一般来说,单表超过50个字段就需要拆分了。
拆分的方案
主要有两种:
1、水平拆分
单表数据量过大用水平拆分。
表结构基本不变,加分片字段(一个或几个),按条数分到不同片中。
2、垂直拆分
字段过多用垂直拆分。
将原表字段拆到几个表里。
ShardingSphere-JDBC
如果是springboot项目,用ShardingSphere-JDBC实现分片比较成熟。
ShardingSphere-JDBC是一个开源项目。
步骤
1、引入依赖
<dependencies>
<!-- ShardingSphere JDBC Core -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core</artifactId>
<version>5.3.2</version> <!-- 请使用最新稳定版 -->
</dependency>
<!-- 连接池 (推荐 HikariCP) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
<!-- 数据库驱动 -->
<!-- ... -->
</dependencies>
2、创建config-sharding.yaml并进行配置,如下:
# config-sharding.yaml
dataSources:
ds0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
jdbcUrl: jdbc:mysql://localhost:3306/ds0
username: root
password: root
ds1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
jdbcUrl: jdbc:mysql://localhost:3306/ds1
username: root
password: root
rules:
- !SHARDING
tables:
t_order:
actualDataNodes: ds${0..1}.t_order_${0..1}
databaseStrategy:
standard:
shardingColumn: user_id
shardingAlgorithmName: db_hash_mod
tableStrategy:
standard:
shardingColumn: order_id
shardingAlgorithmName: table_mod
shardingAlgorithms:
db_hash_mod:
type: HASH_MOD
props:
sharding-count: 2
table_mod:
type: MOD
props:
sharding-count: 2
3、java代码示例
import org.apache.shardingsphere.driver.api.yaml.YamlShardingSphereDataSourceFactory;
import javax.sql.DataSource;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class ShardingJdbcExample {
public static void main(String[] args) throws Exception {
// 1. 加载YAML配置,创建ShardingSphere数据源
DataSource dataSource = YamlShardingSphereDataSourceFactory.createDataSource(
new File("path/to/your/config-sharding.yaml")
);
// 2. 获取连接并执行SQL,ShardingSphere会自动处理路由和结果归并
try (Connection conn = dataSource.getConnection()) {
// 插入数据
String insertSQL = "INSERT INTO t_order (user_id, order_id, status) VALUES (?, ?, ?)";
try (PreparedStatement ps = conn.prepareStatement(insertSQL)) {
ps.setLong(1, 123L); // user_id
ps.setLong(2, 888L); // order_id
ps.setString(3, "PAID");
ps.executeUpdate();
}
// 查询数据
String selectSQL = "SELECT * FROM t_order WHERE user_id = ?";
try (PreparedStatement ps = conn.prepareStatement(selectSQL)) {
ps.setLong(1, 123L);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
System.out.println("Order ID: " + rs.getLong("order_id"));
}
}
}
}
}
分片中如何实现字典表联查呢?
有现成的策略:广播表。
即:在每个分片中都建立相同的表结构,在配置中标记这些表为广播表,查询的时候就直接可以用了。
1、每个分片中都创建广播表。
2、通过配置声明哪些表为广播表。
数据会自动同步、查询时会自动路由。
为什么mysql单表数据量超过2000万就会明显卡顿?
这是一道常考题,答对加分。
核心:
1、mysql的最小物理存储单位是页。
2、3层树及以内是理想值,4层是临界值。
page的结构如下:
| 组成部分 | 英文名称 | 占用大小 | 说明 |
|---|---|---|---|
| 1、文件头 | File Header | 38 字节 | 页的“身份证”。记录页号、上一页/下一页地址(用于双向链表)、校验和等通用信息。 |
| 2、页头 | Page Header | 56 字节 | 页的“管家”。记录页内有多少记录、空闲空间起始位置、目录槽数量等内部管理信息。 |
| 3、最小/最大记录 | Infimum & Supremum | 26 字节 | 两条虚拟的“伪记录”。分别代表页内数据的最小值和最大值,作为边界标记,确保页内始终有数据可供查找。 |
| 4、用户记录 | User Records | 不定长 | 这里才是存你真实数据的地方。数据按主键顺序排列,包含行记录头和实际列数据。 |
| 5、空闲空间 | Free Space | 不定长 | 页内还没被使用的空间。当插入新数据时,会从这里分配空间。 |
| 6、页目录 | Page Directory | 不定长 | 页内的“索引”。存储了记录的偏移量槽(Slots),用于在页内进行二分查找,加快定位速度。 |
| 7、文件尾 | File Trailer | 8 字节 | 页的“封条”。包含校验和(Checksum)和魔数,用于检测页在磁盘读写后是否完整、损坏。 |
3层树可以存放多少数据?
这里主要和1层2层可以存的索引数有关。
注:索引业界一般按14byte计算(8byte的+6byte的指针)
16*1024/14约等于1170。
1层 只存放索引 1170
2层 只存放索引 1170*1170
3层 存放索引+数据
所以大约是2000万。
为什么多一次io差别这么大?
有人问:一次io约10ms,多一次也就10ms啊,应该慢1/3。
其实并不是,这不是简单的多一次的事情,而是全链路都放慢,效果是指数级的。
1、内存比磁盘快多了,多一次整体会都在等这次io。
2、通常来说,索引会放在内存中,多一层索引会大很多,内存如果放不下会挤出去,还要从io从新查,这就慢了非常多。
3、还有些其他原因,先不说了。
根据id拆分还是user_id拆分?
根据id拆分最简单,但是不太贴合业务。
根据user_id拆是比较主流的做法,因为它本身就有业务属性,相对于纯id拆分,从查询时就可以直接根据该user_id来定位分片。
水平拆分后有哪些问题?
1、要面临分片的问题。
2、复杂的sql不能直接写了。
3、必须要采用其他手段来辅助定位分片。
定位分片的常见手段?
1、分片字段直接定位 # 例如user_id
2、其他字段里面包含分片字段 # 例如order_no里面包含user_id
3、利用redis等缓存主要查询字段和分片字段的关系
其他
文档
ShardingSphere-JDBC git地址:
https://github.com/apache/shardingsphere/tree/master/examples/shardingsphere-jdbc-example
更多推荐



所有评论(0)