【若依微服务框架】若依框架中实现用户表的分库分表使用ShardingSphere实现
·
使用ShardingSphere实现若依框架用户表分库分表
若依框架中实现用户表的分库分表可以通过Apache ShardingSphere来完成。下面介绍具体实现步骤:
1. 添加依赖
首先在pom.xml中添加ShardingSphere-JDBC依赖:
<!-- ShardingSphere-JDBC -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.3.2</version>
</dependency>
2. 配置分库分表规则
在application.yml中配置ShardingSphere的分库分表规则:
spring:
shardingsphere:
mode:
type: Memory
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/ry_user_0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/ry_user_1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
rules:
sharding:
tables:
sys_user:
actual-data-nodes: ds$->{0..1}.sys_user_$->{0..1}
database-strategy:
standard:
sharding-column: dept_id
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: table-inline
sharding-algorithms:
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{dept_id % 2}
table-inline:
type: INLINE
props:
algorithm-expression: sys_user_$->{user_id % 2}
props:
sql-show: true
这个配置实现了:
- 两个数据库:
ds0和ds1 - 每个数据库中有两张用户表:
sys_user_0和sys_user_1 - 分库规则:根据
dept_id取模,决定数据存放在哪个数据库 - 分表规则:根据
user_id取模,决定数据存放在哪张表
3. 创建分库分表
在MySQL中创建两个数据库和对应的用户表:
-- 创建第一个数据库
CREATE DATABASE ry_user_0 DEFAULT CHARACTER SET utf8mb4;
-- 创建第二个数据库
CREATE DATABASE ry_user_1 DEFAULT CHARACTER SET utf8mb4;
-- 在每个数据库中创建用户表
CREATE TABLE `sys_user_0` (
`user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID',
`user_name` varchar(30) NOT NULL COMMENT '用户账号',
`nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
`user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)',
`email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
`phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码',
`sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
`avatar` varchar(100) DEFAULT '' COMMENT '头像地址',
`password` varchar(100) DEFAULT '' COMMENT '密码',
`status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
`login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP',
`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`user_id`),
KEY `idx_dept_id` (`dept_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
CREATE TABLE `sys_user_1` LIKE `sys_user_0`;
4. 修改实体类
修改SysUser实体类,确保字段与分表结构一致:
package com.ruoyi.system.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class SysUser extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 用户ID */
private Long userId;
/** 部门ID */
private Long deptId;
/** 用户账号 */
private String userName;
/** 用户昵称 */
private String nickName;
/** 用户类型(00系统用户) */
private String userType;
/** 用户邮箱 */
private String email;
/** 手机号码 */
private String phonenumber;
/** 用户性别(0男 1女 2未知) */
private String sex;
/** 头像地址 */
private String avatar;
/** 密码 */
private String password;
/** 帐号状态(0正常 1停用) */
private String status;
/** 删除标志(0代表存在 2代表删除) */
private String delFlag;
/** 最后登录IP */
private String loginIp;
/** 最后登录时间 */
private Date loginDate;
// 省略getter和setter方法
}
5. 修改Mapper接口
修改SysUserMapper接口,确保SQL语句正确处理分表情况:
package com.ruoyi.system.mapper;
import com.ruoyi.system.domain.SysUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface SysUserMapper {
/**
* 查询用户列表
*/
List<SysUser> selectUserList(SysUser user);
/**
* 根据用户ID查询用户
*/
SysUser selectUserById(Long userId);
/**
* 根据用户名查询用户
*/
SysUser selectUserByUserName(String userName);
/**
* 新增用户
*/
int insertUser(SysUser user);
/**
* 修改用户
*/
int updateUser(SysUser user);
/**
* 删除用户
*/
int deleteUserById(Long userId);
/**
* 批量删除用户
*/
int deleteUserByIds(Long[] userIds);
/**
* 重置用户密码
*/
int resetUserPwd(@Param("userName") String userName, @Param("password") String password);
}
6. 修改Service层
确保Service层正确处理分表后的业务逻辑:
package com.ruoyi.system.service;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import java.util.List;
public interface ISysUserService {
/**
* 查询用户列表
*/
TableDataInfo selectUserList(SysUser user);
/**
* 根据条件分页查询已分配用户角色列表
*/
TableDataInfo selectAllocatedList(SysUser user);
/**
* 根据条件分页查询未分配用户角色列表
*/
TableDataInfo selectUnallocatedList(SysUser user);
/**
* 通过用户名查询用户
*/
SysUser selectUserByUserName(String userName);
/**
* 通过用户ID查询用户
*/
SysUser selectUserById(Long userId);
/**
* 查询用户所属角色组
*/
String selectUserRoleGroup(String userName);
/**
* 查询用户所属岗位组
*/
String selectUserPostGroup(String userName);
/**
* 校验用户名称是否唯一
*/
String checkUserNameUnique(String userName);
/**
* 校验手机号码是否唯一
*/
String checkPhoneUnique(SysUser user);
/**
* 校验email是否唯一
*/
String checkEmailUnique(SysUser user);
/**
* 校验用户是否允许操作
*/
void checkUserAllowed(SysUser user);
/**
* 新增保存用户信息
*/
int insertUser(SysUser user);
/**
* 修改保存用户信息
*/
int updateUser(SysUser user);
/**
* 修改用户状态
*/
int updateUserStatus(SysUser user);
/**
* 修改用户基本信息
*/
int updateUserProfile(SysUser user);
/**
* 修改用户头像
*/
boolean updateUserAvatar(String userName, String avatar);
/**
* 重置用户密码
*/
int resetPwd(SysUser user);
/**
* 重置用户密码
*/
int resetUserPwd(String userName, String password);
/**
* 通过用户ID删除用户
*/
int deleteUserById(Long userId);
/**
* 批量删除用户信息
*/
int deleteUserByIds(Long[] userIds);
/**
* 导入用户数据
*/
String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
/**
* 获取用户信息
*/
LoginUser getLoginUser(String userName);
}
7. 处理分布式ID
为确保分表后ID的唯一性,建议使用雪花算法等分布式ID生成器:
package com.ruoyi.common.core.utils.idgen;
import org.apache.shardingsphere.infra.util.exception.ShardingSpherePreconditions;
import org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;
public final class SnowflakeKeyGenerateAlgorithm implements KeyGenerateAlgorithm {
public static final String TYPE = "SNOWFLAKE";
private static final long EPOCH = 1577836800000L;
private static final long SEQUENCE_BITS = 12L;
private static final long WORKER_ID_BITS = 10L;
private static final long SEQUENCE_MASK = (1L << SEQUENCE_BITS) - 1;
private static final long WORKER_ID_LEFT_SHIFT = SEQUENCE_BITS;
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private Properties props = new Properties();
private AtomicLong sequence = new AtomicLong(0L);
private long workerId;
private long lastTimestamp = -1L;
@Override
public void init() {
workerId = Long.parseLong(props.getProperty("worker-id", "1"));
ShardingSpherePreconditions.checkArgument(workerId >= 0L && workerId < (1L << WORKER_ID_BITS),
"Worker ID must be between 0 and %s", (1L << WORKER_ID_BITS) - 1);
}
@Override
public synchronized Comparable<?> generateKey() {
long currentTimestamp = timeGen();
ShardingSpherePreconditions.checkState(lastTimestamp <= currentTimestamp,
"Clock moved backwards, last time is %d milliseconds, current time is %d milliseconds", lastTimestamp, currentTimestamp);
if (lastTimestamp == currentTimestamp) {
if (0L == (sequence.incrementAndGet() & SEQUENCE_MASK)) {
currentTimestamp = waitUntilNextTime(currentTimestamp);
}
} else {
sequence.set(0L);
}
lastTimestamp = currentTimestamp;
return ((currentTimestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) | (workerId << WORKER_ID_LEFT_SHIFT) | sequence.get();
}
private long waitUntilNextTime(final long lastTimestamp) {
long time = timeGen();
while (time <= lastTimestamp) {
time = timeGen();
}
return time;
}
private long timeGen() {
return System.currentTimeMillis();
}
@Override
public String getType() {
return TYPE;
}
@Override
public Properties getProps() {
return props;
}
@Override
public void setProps(final Properties props) {
this.props = props;
}
}
8. 处理关联查询
对于涉及用户表的关联查询,需要修改相关SQL和业务逻辑:
/**
* 查询用户及其角色信息
*/
public List<SysUser> selectUserAndRoleList(SysUser user) {
// 由于分表,这里需要特殊处理关联查询
// 可以通过先查询用户,再查询角色的方式实现
List<SysUser> userList = userMapper.selectUserList(user);
for (SysUser sysUser : userList) {
List<SysRole> roles = roleMapper.selectRolesByUserId(sysUser.getUserId());
sysUser.setRoles(roles);
}
return userList;
}
9. 注意事项
-
跨库事务:分库后事务会变成分布式事务,需要使用Seata等分布式事务框架
-
全局查询:某些查询可能需要跨库跨表,性能会受影响
-
数据迁移:分表前需要考虑历史数据的迁移问题
-
分片键选择:合理选择分片键非常重要,本例中选择了
dept_id和user_id -
分页查询:分页查询需要特殊处理,ShardingSphere提供了相应的支持
通过以上步骤,你可以在若依框架中成功实现用户表的分库分表,提高系统的性能和扩展性。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)