springboot 按月分表的实现方式

作者:终成一个大象 时间:2023-11-25 00:03:47 

一、项目背景

在实际工作中,会遇到业务比较集中的情况,随着时间推延,这部分业务关联的mysql表就会越来越大,十分臃肿。尽管在项目架构上做了读写分离,也会导致查询的时候出现比较慢的情况,导致线上慢查询的出现。

这种情况下导致的慢查询,单纯从sql优化的角度是无法解决的,此时我们就会用到分库分表。由于我们目前的问题是部分mysql表比较大,采用分表的方式即可解决,本文主要讨论分表的情况。

1、分表的方式

  • 垂直分表

简单理解:把同一个表中的数据按列拆分到不同的表中。

所谓的垂直分表指的是将表结构按照功能模块、关系密切程度划分出来,部署到不同的库或者不同的表中。

  • 水平分表

简单理解:把同一个表中的数据按行拆分到不同的表中。

所谓的水平分表,即将数据按照某种规则存储到不同的表中。例如日志表,可以使用按月或者按天分表,即每个月的日志数据单独存储在一张表中。这些表同时属于一张主表,拥有相同的表结构,但查询时可以大大减轻主表查询的负担。

二、代码实现

主要使用mybatis-plus提供的功能来实现功能。

1、pom文件依赖

<dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>3.1.1</version>
       </dependency>

2、配置文件

# mybatis-plus 配置
mybatis-plus.configuration.call-setters-on-nulls=true
# xml 文件路径
mybatis-plus.mapper-locations=classpath*:mapping/*.xml
# entity 文件路径
mybatis-plus.type-aliases-package=com.geniuworks.bot.entity
# 打印sql语句执行日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

# 需要按月分表的表名
mp.tableNames=message

3、MybatisPlusConfig实现

MybatisPlusConfig配置类实现:

package com.geniuworks.bot.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.parsers.DynamicTableNameParser;
import com.baomidou.mybatisplus.extension.parsers.ITableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.geniuworks.bot.entity.Tables;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
* @Author dingws
* @PackageName
* @Package
* @Date 2022/1/5 1:53 下午
* @Version 1.0
*/
@Configuration
@Slf4j
public class MybatisPlusConfig {

@Autowired
   private Tables tableNames;

/**
    *
    * @return
    */
   @Bean
   public PaginationInterceptor paginationInterceptor(){
       PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
       DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
       dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2){{
           //涉及表集合
           List<String> tables = tableNames.getTableNames();
           //动态表规则 初始表名+_+code
           tables.forEach(tableTitle -> put(tableTitle,(metaObject, sql, tableName) -> tableName + String.valueOf(getParamValue("month",metaObject))));
       }});
       paginationInterceptor.setSqlParserList(Collections.singletonList(dynamicTableNameParser));
       return paginationInterceptor;
   }

/**
    *
    * @param title
    * @param metaObject
    * @return
    */
   private Object getParamValue(String title, MetaObject metaObject){
       //获取参数
       Object originalObject = metaObject.getOriginalObject();
       JSONObject originalObjectJSON = JSON.parseObject(JSON.toJSONString(originalObject));
       JSONObject boundSql = originalObjectJSON.getJSONObject("boundSql");
       JSONObject parameterObject = boundSql.getJSONObject("parameterObject");
       SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM");
       if(parameterObject.get(title) == null){
           return "";
       }
       Date date = parameterObject.getObject(title, Date.class);
       log.info("param value = " + formatter.format(date));
       return "_" + formatter.format(date);
   }
}

Tables类实现:

package com.geniuworks.bot.entity;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
* @Author dingws
* @PackageName
* @Package
* @Date 2022/1/5 2:18 下午
* @Version 1.0
*/
@Configuration
@ConfigurationProperties("mp")
@Data
public class Tables {
   private List<String> tableNames;
}

4、优雅的使用

在使用的时候,只需要在mysql表对应的entity里添加一个字段month即可。

如果month不为空就会按照month的日期所在的月份对数据库表明进行动态拼接。如果month为空则不进行拼接,直接访问总表。

entity类实现:

package com.geniuworks.bot.entity;

import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
   private String id;

private String sessionId;

private Date createdTime;

private String content;

// 根据该字段所在的月分,区分访问的表名
   private Date month;
}

mapper类实现:

package com.geniuworks.bot.mapper;

import com.geniuworks.bot.entity.Message;
import com.geniuworks.bot.vo.MessageVo;
import com.geniuworks.bot.vo.StatisticsVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.Date;
import java.util.List;
import java.util.Map;

@Mapper
public interface MessageMapper {

/**
    * insert record to table
    * @param record the record
    * @return insert count
    */
   int insert(Message record);

/**
    * insert record to table selective
    * @param record the record
    * @return insert count
    */
   int insertSelective(Message record);

/**
    * update record selective
    * @param record the updated record
    * @return update count
    */
   int updateByPrimaryKeySelective(Message record);

/**
    * update record
    * @param record the updated record
    * @return update count
    */
   int updateByPrimaryKey(Message record);

5、mysql表名拆分

需要手动把当年需要的数据库手动创建出来,命名规则对应MybatisPlusConfig类中的拼接规则。

springboot 按月分表的实现方式

三、遇到的问题

由于我一直用的是mybatis组件,需要升级为mybatis-plus,在升级的过程中出现如下的问题。

1、Invalid bound statement (not found)

问题原因: pom文件依赖的是mybatis-plus,配置文件中使用的是mybatis的配置,导致mybatis加载失败。

解决方法:把配置文件的mybatis配置改为mybatis-plus配置

2、resultType=&ldquo;java.util.Map&rdquo;,返回字段名被包装

问题原因: 在未升级成mybatis-plus之前,可以直接放回数据库中的字段命名。 升级之后,mybatis-plus将放回字段自动映射为entity中的字段命名。

解决方案: 梳理受到影响的代码逻辑,更新使用的字段命名。

来源:https://blog.csdn.net/Martin_chen2/article/details/122491174

标签:springboot,按月分表
0
投稿

猜你喜欢

  • Spring的@Value如何从Nacos配置中心获取值并自动刷新

    2022-03-11 02:17:03
  • SpringBoot+JSON+AJAX+ECharts+Fiddler实现前后端分离开发可视化

    2021-11-12 14:49:17
  • JSON中optString和getString方法的区别

    2021-09-06 03:46:14
  • SpringBoot文件访问映射如何实现

    2022-07-22 00:36:07
  • Android使用ViewPager实现图片滑动预览效果

    2023-02-15 15:28:28
  • java中找不到符号的解决方案

    2023-09-01 17:50:11
  • Spring Boot缓存实战 EhCache示例

    2023-08-30 12:23:35
  • 微服务链路追踪Spring Cloud Sleuth整合Zipkin解析

    2022-09-23 22:29:36
  • Android简单实现天气预报App

    2022-11-05 05:50:32
  • java实现在线聊天系统

    2021-07-30 08:20:54
  • Android手势识别器GestureDetector使用详解

    2022-01-16 14:25:17
  • java 服务器接口快速开发之servlet详细教程

    2022-11-07 09:37:28
  • java实现KFC点餐小程序

    2022-12-11 21:06:51
  • 解析C#设计模式之单例模式

    2021-12-15 17:41:12
  • java实现转圈打印矩阵算法

    2022-11-27 06:38:21
  • Android 资源 id详解及的动态获取

    2023-06-25 00:32:21
  • Java生成10个1000以内的随机数并用消息框显示数组内容然后求和输出

    2023-09-30 21:20:19
  • C#中DataSet转化为实体集合类的方法

    2022-05-09 00:50:46
  • Java实现多任务执行助手

    2023-05-17 14:15:30
  • Android入门之计时器Chronometer的使用教程

    2023-09-06 10:56:43
  • asp之家 软件编程 m.aspxhome.com