springboot 按月分表的實(shí)現(xiàn)方式
一、項(xiàng)目背景
在實(shí)際工作中,會(huì)遇到業(yè)務(wù)比較集中的情況,隨著時(shí)間推延,這部分業(yè)務(wù)關(guān)聯(lián)的mysql表就會(huì)越來(lái)越大,十分臃腫。盡管在項(xiàng)目架構(gòu)上做了讀寫(xiě)分離,也會(huì)導(dǎo)致查詢的時(shí)候出現(xiàn)比較慢的情況,導(dǎo)致線上慢查詢的出現(xiàn)。
這種情況下導(dǎo)致的慢查詢,單純從sql優(yōu)化的角度是無(wú)法解決的,此時(shí)我們就會(huì)用到分庫(kù)分表。由于我們目前的問(wèn)題是部分mysql表比較大,采用分表的方式即可解決,本文主要討論分表的情況。
1、分表的方式
- 垂直分表
簡(jiǎn)單理解:把同一個(gè)表中的數(shù)據(jù)按列拆分
到不同的表中。
所謂的垂直分表指的是將表結(jié)構(gòu)按照功能模塊、關(guān)系密切程度劃分出來(lái),部署到不同的庫(kù)或者不同的表中。
- 水平分表
簡(jiǎn)單理解:把同一個(gè)表中的數(shù)據(jù)按行拆分
到不同的表中。
所謂的水平分表,即將數(shù)據(jù)按照某種規(guī)則存儲(chǔ)到不同的表中。例如日志表,可以使用按月或者按天分表,即每個(gè)月的日志數(shù)據(jù)單獨(dú)存儲(chǔ)在一張表中。這些表同時(shí)屬于一張主表,擁有相同的表結(jié)構(gòu),但查詢時(shí)可以大大減輕主表查詢的負(fù)擔(dān)。
二、代碼實(shí)現(xiàn)
主要使用mybatis-plus提供的功能來(lái)實(shí)現(xiàn)功能。
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語(yǔ)句執(zhí)行日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # 需要按月分表的表名 mp.tableNames=message
3、MybatisPlusConfig實(shí)現(xiàn)
MybatisPlusConfig配置類實(shí)現(xiàn):
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(); //動(dòng)態(tài)表規(guī)則 初始表名+_+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){ //獲取參數(shù) 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類實(shí)現(xiàn):
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、優(yōu)雅的使用
在使用的時(shí)候,只需要在mysql表對(duì)應(yīng)的entity里添加一個(gè)字段month即可。
如果month不為空就會(huì)按照month的日期所在的月份對(duì)數(shù)據(jù)庫(kù)表明進(jìn)行動(dòng)態(tài)拼接。如果month為空則不進(jìn)行拼接,直接訪問(wèn)總表。
entity類實(shí)現(xiàn):
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; // 根據(jù)該字段所在的月分,區(qū)分訪問(wèn)的表名 private Date month; }
mapper類實(shí)現(xiàn):
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表名拆分
需要手動(dòng)把當(dāng)年需要的數(shù)據(jù)庫(kù)手動(dòng)創(chuàng)建出來(lái),命名規(guī)則對(duì)應(yīng)MybatisPlusConfig類中的拼接規(guī)則。
三、遇到的問(wèn)題
由于我一直用的是mybatis組件,需要升級(jí)為mybatis-plus,在升級(jí)的過(guò)程中出現(xiàn)如下的問(wèn)題。
1、Invalid bound statement (not found)
問(wèn)題原因: pom文件依賴的是mybatis-plus,配置文件中使用的是mybatis的配置,導(dǎo)致mybatis加載失敗。
解決方法:把配置文件的mybatis配置改為mybatis-plus配置
2、resultType=“java.util.Map”,返回字段名被包裝
問(wèn)題原因: 在未升級(jí)成mybatis-plus之前,可以直接放回?cái)?shù)據(jù)庫(kù)中的字段命名。 升級(jí)之后,mybatis-plus將放回字段自動(dòng)映射為entity中的字段命名。
解決方案: 梳理受到影響的代碼邏輯,更新使用的字段命名。
到此這篇關(guān)于springboot 按月分表的實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)springboot 按月分表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作
- SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分庫(kù)分表
- SpringBoot實(shí)現(xiàn)分庫(kù)分表
- springboot+mybatis-plus基于攔截器實(shí)現(xiàn)分表的示例代碼
- SpringBoot整合sharding-jdbc實(shí)現(xiàn)自定義分庫(kù)分表的實(shí)踐
- SpringBoot整合sharding-jdbc實(shí)現(xiàn)分庫(kù)分表與讀寫(xiě)分離的示例
- springboot整合shardingjdbc實(shí)現(xiàn)分庫(kù)分表最簡(jiǎn)單demo
- springboot jpa分庫(kù)分表項(xiàng)目實(shí)現(xiàn)過(guò)程詳解
相關(guān)文章
Java實(shí)現(xiàn)添加文字水印&圖片水印的方法詳解
為圖片添加水印的主要作用是保護(hù)圖片版權(quán),防止圖片被未經(jīng)授權(quán)的人使用或傳播。本文為大家介紹了Java實(shí)現(xiàn)添加文字水印&圖片水印的具體方法,需要的可以參考一下2023-02-02Spring mvc攔截器實(shí)現(xiàn)原理解析
這篇文章主要介紹了Spring mvc攔截器實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03java 讀取網(wǎng)頁(yè)內(nèi)容的實(shí)例詳解
這篇文章主要介紹了java 讀取網(wǎng)頁(yè)內(nèi)容的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下2017-09-09java.lang.NullPointerException 如何處理空指針異常的實(shí)現(xiàn)
這篇文章主要介紹了java.lang.NullPointerException 如何處理空指針異常的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12IDEA設(shè)置maven修改settings.xml配置文件無(wú)法加載倉(cāng)庫(kù)的解決方案
這篇文章主要介紹了IDEA設(shè)置maven修改settings.xml配置文件無(wú)法加載倉(cāng)庫(kù)的解決方案,幫助大家更好的利用IDEA進(jìn)行JAVA的開(kāi)發(fā)學(xué)習(xí),感興趣的朋友可以了解下2021-01-01