mybatis-plus分表

news/2024/10/6 3:52:14/文章来源:https://www.cnblogs.com/smallfa/p/18286389

Mysql是当前互联网系统中使用非常广泛的关系数据库,具有ACID的特性。

但是mysql的单表性能会受到表中数据量的限制,主要原因是B+树索引过大导致查询时索引无法全部加载到内存。读取磁盘的次数变多,而磁盘的每次读取对性能都有很大的影响。

这时一个简单可行的方案就是分表(当然土豪也可以堆硬件),将一张数据量庞大的表的数据,拆分到多个表中,这同时也减少了B+树索引的大小,减少磁盘读取次数,提高性能。

两种基础分表逻辑

说完了为什么要分表,下面聊聊业务开发中常见的两种基础的分表逻辑。

按日期分表

这种方式通常会在表名的最后加上年月日,主要适用于按日期划分的统计数据或操作记录。在线实时展示的只有最近表中的数据,其他数据用于离线统计等。

按id取模分表

这种方式需要一个id生成器,例如snowflake id或分布式id服务。它保证了相同id的数据都在一张表中,主要适用于保存用户基础信息,系统中的资源信息,购买记录等。当然这种分表方式扩展性较差,后期数据持续增多后需要按id大小分库再分表处理。

下面看下这两种分表逻辑在mybatis-plus中的实现。

Mybatis-plus中的分表实现

说到java的分表中间件,可能有人会想到sharding-jdbc,作为使用很广泛的一个分表中间件,功能也比较完善,但是使用它需要引入额外的jar包和增加学习成本。

实际上mybatis-plus本身就提供了一个分表的解决方案,配置使用都很简单,适合快速开发系统。

动态表名处理器

没错,mybatis-plus提供了动态表名处理器接口TableNameHandler,只需要在系统中实现该接口,并作为插件加载到mybatis-plus中就可以使用,下面来看下详细的步骤。

3.4版本之前的动态表名接口是ITableNameHandler,需要和分页插件配合使用。

3.4版本新增了TableNameHandler,在方法参数上取消了MetaObject。这里用最新的版本为例,使用方式差别不大。

假设我们的系统中有两种分表方式,按日期分表和按id取模分表。通过四个步骤来看下具体的使用示例。

1.创建日期表名处理器

先来看下日期处理的表名处理器,实现TableNameHandler接口后,在dynamicTableName方法中实现动态生成表名的逻辑,方法的返回值就是查询时要使用的表名。

/*** 按天分表解析*/
public class DaysTableNameParser implements TableNameHandler {@Overridepublic String dynamicTableName(String sql, String tableName) {String dateDay = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));return tableName + "_" + dateDay;}
}
2.创建id取模表名处理器

再来看下按id取模表名处理器的实现,这个处理器相对日期处理就要复杂一些,主要原因为需要动态传入用于分表的id值

在之前的版本中可以在方法中通过解析MetaObject中带有的sql查询信息,获取分表使用的值。但是这种方式比较复杂,对于不同的QueryMapper分析的方式不同,比较容易出错。新版本中的方法取消了MetaObject参数,需要使用其他方式传入。

需要注意的是,表名处理器是作为mybatis-plus的插件,在项目启动时实例化的。这意味着,在运行过程中只有一个对象,多线程处理过程中,一个线程对参数的修改,会影响到其他线程。为了解决这个问题,可以使用ThreadLocal来定义参数。

由于现在的框架中大部分会使用线程池,例如springboot web项目中的tomcat。所以在每次使用后,需要手动清除本次数据,防止线程复用时的影响。

具体实现如下:

/*** 按id取模分表处理器*/
public class IdModTableNameParser implements TableNameHandler {private Integer mod;//使用ThreadLocal防止多线程相互影响private static ThreadLocal<Integer> id = new ThreadLocal<Integer>();public static void setId(Integer idValue) {id.set(idValue);}IdModTableNameParser(Integer modValue) {mod = modValue;}@Overridepublic String dynamicTableName(String sql, String tableName) {Integer idValue = id.get();if (idValue == null) {throw new RuntimeException("请设置id值");} else {String suffix = String.valueOf(idValue % mod);//这里清除ThreadLocal的值,防止线程复用出现问题id.set(null);return tableName + "_" + suffix;}}
}
3.加载表名处理器

表名处理器实际是mybatis-plus的插件,需要在初始化时创建实例并加载。因为系统中存在两种分表类型,在初始化时可以指定每张表使用的表名处理器。具体实现如下:

@Configuration
@MapperScan(basePackages = "com.yourcom.proname.repository.mapper.mainDb*", sqlSessionFactoryRef = "mainSqlSessionFactory")
public class MainDb {@Bean(name = "mainDataSource")@ConfigurationProperties(prefix = "dbconfig.maindb")public DataSource druidDataSource() {return DruidDataSourceBuilder.create().build();}@Bean(name = "mainTransactionManager")public DataSourceTransactionManager masterTransactionManager(@Qualifier(value = "mainDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "mainSqlSessionFactory")@ConfigurationPropertiesBinding()public SqlSessionFactory sqlSessionFactory(@Qualifier(value = "mainDataSource") DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();factoryBean.setDataSource(dataSource);//加载插件factoryBean.setPlugins(mybatisPlusInterceptor());return factoryBean.getObject();}@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();HashMap<String, TableNameHandler> map = new HashMap<String, TableNameHandler>();//这里为不同的表设置对应表名处理器map.put("user_daily_record", new DaysTableNameParser());map.put("user_consume_flow", new IdModTableNameParser(10));dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);return interceptor;}
}
4.在controller中使用

下面通过controller中的三个接口,展示下使用方式:

@RestController
public class TableTestController {@ResourceIUserDailyRecordService userDailyRecordService;@ResourceIUserConsumeFlowService userConsumeFlowService;@GetMapping("user/record/today")public CommonResVo<UserDailyRecord> getRecordToday(Integer userId) throws Exception {//这里在查询时,会根据系统当前时间,自动生成当天的表名UserDailyRecord userDailyRecord = userDailyRecordService.getOne(new LambdaQueryWrapper<UserDailyRecord>().eq(UserDailyRecord::getUserId, userId));return CommonResVo.success(userDailyRecord);}@GetMapping("user/consume/flow")public CommonResVo<List<UserConsumeFlow>> getConsumeFlow(Integer userId) throws Exception {//设置用于分表的id值IdModTableNameParser.setId(userId);List<UserConsumeFlow> userConsumeFlowList = userConsumeFlowService.list(new LambdaQueryWrapper<UserConsumeFlow>().eq(UserConsumeFlow::getUserId, userId));return CommonResVo.success(userConsumeFlowList);}/*** 新增数据*/@PostMapping("user/consume/flow")public CommonResVo<Boolean> addConsumeFlow(@RequestBody UserConsumeFlow userConsumeFlow) throws Exception {Integer userId = userConsumeFlow.getUserId();//设置用于分表的id值IdModTableNameParser.setId(userId);userConsumeFlowService.save(userConsumeFlow);return CommonResVo.success(true);}
}
 

这篇对mybatis-plus动态表名处理器的介绍,通过实现TableNameHandler接口,可以按实际情况灵活定义表名的生成规则,希望对大家有帮助。

项目完整示例地址:https://gitee.com/dothetrick/web-demo/tree/tabel-shading

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/738888.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis PageHelper编译SQL引发的一次性能问题.18286262

起源 最近一直在跟大佬们做公司项目的性能优化,我这种小卡乐咪基本上负责的就是慢接口优化,但实际上只有以下几种情况需要进行接口代码级别的改造:循环查库、RPC 数据库设计不合理 业务流程太长,代码耦合性太高等随着对接口分析的深入,我们越来越发现系统中有很多拖后腿的…

蓝牙音箱App设计总结

前言 最近做了一个关于带Sound bar的智能电视的蓝牙项目,就是将电视Sound bar当作蓝牙音箱,将手机、电脑等设备的声音传输到电视,通过电视Soundbar播放声音。做这个项目的时候遇到了各种大大小小的问题,好在都解决了。本篇文章总结了在设计蓝牙相关的项目时需要了解的小知识…

设计模式学习(二)工厂模式——抽象工厂模式

介绍抽象工厂模式,并说明其优缺点目录背景抽象工厂模式优点与缺点 背景 现在我需要开发一个相机操作模块,它可能在Windows下运行,也可能在Linux下运行。由于在厂家提供的SDK中,Windows下的SDK和Linux下的SDK是有区别的,因此我们要创建两个类去封装这两个不同平台下的API。…

aippt 实现原理 AI生成PPT开源项目

AI生成PPT原理与代码实现通过 AI 生成 PPT 火了好长一段时间了,该类型产品也越来越多,我分析了几个主流的 aippt 产品,其中有一家公司的技术原理让我眼前一亮:文多多 AI 生成 PPT,官网: https://docmee.cn 该产品在 github 上有对应开源项目:https://github.com/veasion…

自定义流程表单开发优势体现在什么地方?

一起来了解自自定义流程表单开发的优势特点。提质、增效、降本,应该是很多职场办公需要实现的发展目标。那么,应用什么样的软件平台可以实现?低代码技术平台、自定义流程表单开发是目前流行于职场行业中的软件产品,可视化操作界面、够灵活、易维护等优势特点明显,在推进企…

Matlab马尔可夫链蒙特卡罗法(MCMC)估计随机波动率(SV,Stochastic Volatility) 模型|附代码数据

全文下载链接:http://tecdat.cn/?p=16708 最近我们被客户要求撰写关于随机波动率的研究报告,包括一些图形和统计输出。 波动率是一个重要的概念,在金融和交易中有许多应用。它是期权定价的基础。波动率还可以让您确定资产配置并计算投资组合的风险价值 (VaR) 甚至波动率本身…

2024.7.5 鲜花

菜就多练空白とカタルシス——TOGENASHI TOGEARI。震惊,K某He 强推竟然是这首歌,三天重复上百遍…… どれだけ手に入れても どれだけ自分のものにしてもしてもしても 追いつけないな 高望みしすぎなんて 腐ったような言葉 誰しも誰よりも優れて欲しくはないんだよ 理由はただ…

泛娱乐出海新风口,视频云技术需要怎样的融合创新?

泛娱乐的音视频技术随着出海在演进,交互和内容的技术是内核,也在融合。 泛娱乐的音视频技术随着出海在演进,交互和内容的技术是内核,也在融合。 面向出海,虽然娱乐社交这个行业由来已久,但近几年的商业模式发生了巨大变化,比如行业刚兴起时,大家要先把DAU做大之后再…

米尔瑞米派集聚5种操作系统,兼顾学习开发和项目产品需要的派

米尔电子发布的瑞萨第一款MPU生态板卡-瑞米派(Remi Pi),采用瑞萨RZ/G2L双核A55芯片,接口丰富,全面兼容树莓派的扩展模块。瑞米派支持五种系统,兼顾学习开发和项目产品需要。软件提供五种软件系统分别为:基于Yocto构建的两种系统,一种是支持通用功能的精简型系统,另一种…

echarts中Label标签与数据项颜色设置为同一种颜色

echarts5中默认标签颜色不会跟数据项颜色保持一致,而是全都是黑色。想要实现label颜色和它的数据项颜色一致,需要手动继承颜色,设置label{ color: inherit}即可解决label标签颜色与数据项颜色一致。 https://echarts.apache.org/examples/zh/editor.html?c=pie-simple 注意…

GaussDB AI新特性:gs_index_advise推荐索引

GaussDB的AI新特性,可以把SQL文本嵌套在函数中,数据库会返回一个创建合适索引的列gs_index_advise(text) 描述:针对单条查询语句推荐索引。 参数: SQL语句字符串 返回值类型: record 一、通常的SQL优化会通过参数调优的方式进行调整,例如如下参数set enable_fast_query_s…

Packing Python to exe(打包Python成EXE文件)

Python文件要执行需要Python环境,如果package成EXE文件则可以随意放在任意主机上去执行。package步骤如下: 1. 安装pythoninstaller (pip install pyinstaller) 2.安装auto-py-to-exe(有UI界面,很方便使用)(pip install auto-py-to-exe) 3.然后直接运行命令auto-py-to-e…