广告位联系
返回顶部
分享到

springboot按月分表的实现方式介绍

java 来源:互联网 作者:佚名 发布时间:2023-09-03 12:47:23 人浏览
摘要

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

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

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

1、分表的方式

  • 垂直分表

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

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

  • 水平分表

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

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

二、代码实现

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

1、pom文件依赖

1

2

3

4

5

<dependency>

    <groupId>com.baomidou</groupId>

    <artifactId>mybatis-plus-boot-starter</artifactId>

    <version>3.1.1</version>

</dependency>

2、配置文件

1

2

3

4

5

6

7

8

9

10

11

# 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配置类实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

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类实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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类实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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类实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

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类中的拼接规则。

三、遇到的问题

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

1、Invalid bound statement (not found)

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

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

2、resultType=“java.util.Map”,返回字段名被包装

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

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


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • Java线程的停止实现原理介绍
    线程停止的原理 使用interrupt来通知,而不是强制 java提供了interrrupt让一个线程来通知另一个线程停止 如果想中断一个线程,但是那个线程不
  • springboot按月分表的实现方式介绍

    springboot按月分表的实现方式介绍
    在实际工作中,会遇到业务比较集中的情况,随着时间推延,这部分业务关联的mysql表就会越来越大,十分臃肿。尽管在项目架构上做了读写
  • jmap执行失败如何获取heapdump

    jmap执行失败如何获取heapdump
    在之前的OOM问题复盘中,我们添加了jmap脚本来自动dump内存现场,方便排查OOM问题。 但当我反复模拟OOM场景测试时,发现jmap有时可以dump成功
  • Spring refresh()源码解析介绍

    Spring refresh()源码解析介绍
    正文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public void refresh() throws BeansException, IllegalStateExcept
  • 线程阻塞唤醒工具LockSupport使用介绍
    LockSupport 简介 LockSupport是 Java 并发编程中一个非常重要的组件,我们熟知的并发组件Lock、线程池、CountDownLatch等都是基于AQS实现的,而AQS内
  • RocketMQ的存储模型全介绍
    RocketMQ有Producer、Consumer、NameSrv、Broker四个部分。其中Broker用于存储消息,维护消息队列和订阅关系,是RocketMQ四个部分中最重要的一个部分
  • 规则引擎Drools的应用介绍
    现在有这么个需求,网上购物,需要根据不同的规则计算商品折扣,比如VIP客户增加5%的折扣,购买金额超过1000元的增加10%的折扣等,而且
  • Spring populateBean属性赋值和自动注入
    protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mb
  • ThreadPoolExecutor参数的用法及说明
    一、ThreadPoolExecutor核心参数说明 1、corePoolSize:核心线程数 * 核心线程会一直存活,及时没有任务需要执行 * 当线程数小于核心线程数时,即
  • springcloud gateway无法路由问题的解决
    现在企业微服务架构基本上都是用springcloud体系了,在国内基本上新项目都用springcloud alibaba,而且基本上都是所有服务聚合在一个父项目中
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计