---
title: "01-分库分表-用户服务-用户表"
created: 2025-12-10
aliases:
- 分库分表-用户服务-用户表
tags:
- 项目
---
# 分库分表-用户服务-用户表
阅读此文前,建议小伙伴先阅读[[05-技术精华-全面剖析分库分表|技术精华-全面剖析分库分表]]的前置知识,对分库分表 和 shardingsphere 有了大概的理解后,在继续阅读本文
## 介绍
在开始介绍前,需要先知道大麦网中[[00-数据库表关系|数据库表关系]]是怎么样,关于数据库表设计的详细介绍,小伙伴可跳转到相关文档
## 配置
### 引入 ShardingSphere 的相关依赖
```xml
5.3.2
org.apache.shardingsphere
shardingsphere-jdbc-core
${shardingsphere.version}
logback-classic
ch.qos.logback
```
### 根据规则进行分库分表的规则配置
ShardingSphere 官网的规则配置说明:
用户项目相关配置:
```yaml
spring:
datasource:
driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver
url: jdbc:shardingsphere:classpath:shardingsphere-user.yaml
```
shardingsphere-user.yaml配置:
```yaml
dataSources:
# 第一个用户库
ds_0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/damai_user_0?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: xxx
password: xxx
# 第二个用户库
ds_1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/damai_user_1?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: xxx
password: xxx
rules:
# 分库分表规则
- !SHARDING
tables:
# 对d_user_mobile表进行分库分表
d_user_mobile:
# 库为damai_user_0 damai_user_1 表为d_user_mobile_0 至 d_user_mobile_1
actualDataNodes: ds_${0..1}.d_user_mobile_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用mobile作为分片键
shardingColumn: mobile
# 用user_mobile列使用hash取模作为分库算法
shardingAlgorithmName: databaseUserMobileHashModModel
# 分表策略
tableStrategy:
standard:
# 使用mobile作为分片键
shardingColumn: mobile
# 用user_mobile列使用hash取模作为分表算法
shardingAlgorithmName: tableUserMobileHashMod
# 对d_user_email表进行分库分表
d_user_email:
# 库为damai_user_0 damai_user_1 表为d_user_email_0 至 d_user_email_1
actualDataNodes: ds_${0..1}.d_user_email_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用email作为分片键
shardingColumn: email
# 用user_mobile列使用hash取模作为分库算法
shardingAlgorithmName: databaseUserEmailHashModModel
# 分表策略
tableStrategy:
standard:
# 使用email作为分片键
shardingColumn: email
# 用user_mobile列使用hash取模作为分表算法
shardingAlgorithmName: tableUserEmailHashMod
# 对d_user表进行分库分表
d_user:
# 库为damai_user_0 damai_user_1 表为d_user_0 至 d_user_1
actualDataNodes: ds_${0..1}.d_user_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用id作为分片键
shardingColumn: id
# 用id列使用hash取模作为分库算法
shardingAlgorithmName: databaseUserModModel
# 分表策略
tableStrategy:
standard:
# 使用id作为分片键
shardingColumn: id
# 用id列使用hash取模作为分表算法
shardingAlgorithmName: tableUserModModel
# 对d_ticket_user表进行分库分表
d_ticket_user:
# 库为damai_user_0 damai_user_1 表为d_ticket_user_0 至 d_ticket_user_1
actualDataNodes: ds_${0..1}.d_ticket_user_${0..1}
# 分库策略
databaseStrategy:
standard:
# 使用user_id作为分片键
shardingColumn: user_id
# 用user_id列使用hash取模作为分库算法
shardingAlgorithmName: databaseTicketUserModModel
# 分表策略
tableStrategy:
standard:
# 使用user_id作为分片键
shardingColumn: user_id
# 用user_id列使用hash取模作为分表算法
shardingAlgorithmName: tableTicketUserModModel
# 具体的算法
shardingAlgorithms:
# d_user_mobile表分库算法
databaseUserMobileHashModModel:
type: HASH_MOD
props:
# 分库数量
sharding-count: 2
# d_user_mobile表分表算法
tableUserMobileHashMod:
type: HASH_MOD
props:
# 分表数量
sharding-count: 2
# d_user_email表分库算法
databaseUserEmailHashModModel:
type: HASH_MOD
props:
# 分库数量
sharding-count: 2
# d_user_email表分表算法
tableUserEmailHashMod:
type: HASH_MOD
props:
# 分表数量
sharding-count: 2
# d_user表分库算法
databaseUserModModel:
type: MOD
props:
# 分库数量
sharding-count: 2
# d_user表分表算法
tableUserModModel:
type: MOD
props:
# 分表数量
sharding-count: 2
# d_ticket_user表分库算法
databaseTicketUserModModel:
type: MOD
props:
# 分库数量
sharding-count: 2
# d_ticket_user表分表算法
tableTicketUserModModel:
type: MOD
props:
# 分表数量
sharding-count: 2
# 加密规则
- !ENCRYPT
tables:
# d_user表
d_user:
columns:
# 对mobile列进行加密
mobile:
# 密文列mobile
cipherColumn: mobile
# 自定义的加密算法
encryptorName: user_encryption_algorithm
# 对password列进行加密
password:
# 密文列password
cipherColumn: password
# 自定义的加密算法
encryptorName: user_encryption_algorithm
# 对id_number列进行加密
id_number:
# 密文列id_number
cipherColumn: id_number
# 自定义的加密算法
encryptorName: user_encryption_algorithm
# d_user_mobile表
d_user_mobile:
columns:
# 对mobile列进行加密
mobile:
# 密文列id_number
cipherColumn: mobile
# 自定义的加密算法
encryptorName: user_encryption_algorithm
props:
# 打印真实sql
sql-show: true
```
### 总结
- d\_user\_mobile表的分库分表都是用的mobile作为分片键,算法为HASH\_MOD,hash取模
- d\_user\_email表的分库分表都是用的email作为分片键,算法为HASH\_MOD,hash取模
- d\_user表的分库分表都是用的id作为分片键,算法为MOD,取模
小伙伴可能会有疑惑,为什么要额外设计 用户手机表 和 用户邮箱表 呢? 直接把邮箱和手机号放进用户表里不就行了吗?别急,本人都会解答到
![[image-1528bfd4.webp]]
在用户登录时,是可以用手机号和邮箱登录的,也就是需要用手机号和邮箱来查询用户信息,而在订单业务中也需要查询用户信息,使用的是用户id来查询。而我们是使用的用户id作为分片键,使用手机号 和 邮箱 就会造成 全路由 的问题
所谓的全路由,就是查询或者操作数据时,没有分片键的条件,ShardingSphere 无法定位数据具体到在哪个库,哪个表。就只能去所有的分片库,分片表上查询,这种情况的执行效率是非常慢的,会有数据库连接超时、接口超时 各种的问题
### 解决
为了解决 手机号和邮箱登录 而且不造成 全路由 的问题。采取附属表的方案,设置了 用户手机表 和 用户邮箱表 ,通过手机号 和 邮箱 查询到 用户id,然后使用用户id查询用户表,这样就解决了问题
d\_user 用户表
```sql
CREATE TABLE `d_user` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`name` varchar(256) DEFAULT NULL COMMENT '用户名字',
`rel_name` varchar(256) DEFAULT NULL COMMENT '用户真实名字',
`mobile` varchar(512) NOT NULL COMMENT '手机号',
`gender` int(11) NOT NULL DEFAULT '1' COMMENT '1:男 2:女',
`password` varchar(512) DEFAULT NULL COMMENT '密码',
`email_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否邮箱认证 1:已验证 0:未验证',
`email` varchar(256) DEFAULT NULL COMMENT '邮箱地址',
`rel_authentication_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否实名认证 1:已验证 0:未验证',
`id_number` varchar(512) DEFAULT NULL COMMENT '身份证号码',
`address` varchar(256) DEFAULT NULL COMMENT '收货地址',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`edit_time` datetime DEFAULT NULL COMMENT '编辑时间',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常 0:删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
```
d\_user\_mobile 用户手机表
```sql
CREATE TABLE `d_user_mobile` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`mobile` varchar(512) NOT NULL COMMENT '手机号',
`create_time` datetime NOT NULL COMMENT '创建时间',
`edit_time` datetime NOT NULL COMMENT '编辑时间',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常 0:删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户手机表';
```
d\_user\_email 用户邮箱表
```sql
CREATE TABLE `d_user_email` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`email` varchar(512) NOT NULL COMMENT '邮箱',
`create_time` datetime NOT NULL COMMENT '创建时间',
`edit_time` datetime NOT NULL COMMENT '编辑时间',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常 0:删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户邮箱表';
```
d\_ticket\_user 购票人表
```sql
CREATE TABLE `d_ticket_user` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`rel_name` varchar(256) NOT NULL COMMENT '用户真实名字',
`id_type` int(11) NOT NULL DEFAULT '1' COMMENT '证件类型 1:身份证 2:港澳台居民居住证 3:港澳居民来往内地通行证 4:台湾居民来往内地通行证 5:护照 6:外国人永久居住证',
`id_number` varchar(512) NOT NULL COMMENT '证件号码',
`create_time` datetime NOT NULL COMMENT '创建时间',
`edit_time` datetime NOT NULL COMMENT '编辑时间',
`status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常 0:删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='购票人表';
```
## 流程:
![[image-2a3c1c80.webp]]
如果以后登录业务修改的话,比如再增加使用用户名登录,那么再增加一个 用户名 表 即可解决。但使用这种附属表就没有任何问题了吗?显示不是不可能,任何的解决方案都是有相应代价的
### 缺点
- 还是 用户登录 业务,使用手机号登录,需要先用手机号去用户手机表查询到用户id,再使用 用户id去用户表查询用户信息,这样多了一步查询的过程,额外产生了性能的消耗
- 额外多了用户手机表和用户邮箱表,随着数据量的越来越大,表容量的占用也越来越大,需要额外的维护
目前来说 这种使用 附属表路由的方案 是互联网公司比较通用的方案,那么像这种多字段查询的业务都必须使用附属表的方案吗?答案是 不一定
比如 订单业务,订单可以根据订单编号查询,也可以根据用户id查询,这种业务可以用另一种方案,并不需要额外的表来维护,叫分片基因算法。此方案在订单服务的分库分表中得到了应用,小伙伴们可跳转到相应的文档查看
[[04-分库分表-订单服务|分库分表-订单服务]]
疑问:
这分库分表有问题吧,d\_user\_mobile分库分表分片键都是mobile,哈希算法又是相同的,结果不是一样的吗,这不会导致damai\_user\_0里只有d\_user\_mobile\_0有数据吗
想到一个小方法,ds\_1中创建1,3表,ds\_0中创建0,2表,分库用mod2,分表用mod4
---
**企业级项目导航**:⬅️ [[05-订单服务分库分表设计思维全景|05-订单服务分库分表设计思维全景]] | 01-分库分表-用户服务-用户表 | ➡️ [[02-分库分表-用户服务-购票人表|02-分库分表-用户服务-购票人表]]