Mybatis-Plus实现MySQL分表
Mybatis-Plus 是什么?
MyBatis-Plus是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。简单理解就是增强版的 MyBatis。Mybatis-Plus 优点这里不赘述,不是我们的重点。
MySQL 为啥需要分表?
随着数据量增多,MySQL 会出现如下两种情况:
B+ 树高度增加,访问磁盘 IO 次数增多,B+ 数索引性能下降;
热数据超过 Buffer Pool 缓冲池大小,导致缓存命中率降低。
Mybatis-Plus 如何实现分表?
MyBatis-Plus 没有直接分表功能,可以通过其插件机制和动态表名功能实现分表。
DynamicTableNameHandler 实现动态表名方法
import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler;
import org.apache.commons.lang3.StringUtils;import java.util.Locale;/*** 动态表处理器**/
public class DynamicTableNameHandler implements TableNameHandler {private static final ThreadLocal<String> suffixThreadLocal = new ThreadLocal<>();/*** 设置表名后缀*/public static void set(String suffix) {suffixThreadLocal.set(suffix);}/*** 删除表名后缀**/public static void remove() {suffixThreadLocal.remove();}@Overridepublic String dynamicTableName(String sql, String tableName) {if (StringUtils.isNotEmpty(suffixThreadLocal .get())) {return String.format(Locale.ROOT, "%s_%s", tableName, suffixThreadLocal.get());}return tableName;}
}将 DynamicTableNameHandler 添加到 Mybatis Plus 拦截器处理链上
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.hihonor.kidswatch.db.DynamicTableNameHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** MyBatisPlus配置*/
@Configuration
public class MyBatisPlusConfig {/*** 拦截器配置*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();dynamicTableNameInnerInterceptor.setTableNameHandler(new DynamicTableNameHandler());interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);return interceptor;}
}假设将用户分成10个表,则表名后缀为 userId %10,使用如下:
/*** 执行查询逻辑*/
private void execute(Long userId) {try {DynamicTableNameHandler.set(genSuffix(userId));// 正常sql逻辑DynamicTableNameHandler.remove();} catch (Exception e) {log.error("dynamic table name error", e);} finally {DynamicTableNameHandler.remove();}}/*** 获取表名后缀*/
private String genSuffix(Long userId) {long cnt = userId % 10;return Long.toString(cnt);
}在 finally 里面添加线程变量删除逻辑,是担心在执行 SQL 逻辑的时候,如果出现异常,线程变量没有删除将出现内存溢出的问题。
动态表名处理器是 Mybatis-Plus 提供的轻量级分表解决方案,重量级的可以考虑 ShardingSphere 等专业框架。
