前言:
此刻看官们对“java bigdecimal取模”大致比较讲究,同学们都需要了解一些“java bigdecimal取模”的相关资讯。那么小编在网络上收集了一些有关“java bigdecimal取模””的相关知识,希望看官们能喜欢,兄弟们一起来学习一下吧!前言
笔者维护的许多系统,大多在线上稳定运行多年,拥有大量的用户,便拥有海量的数据,那么如何在数量多的情况确保数据库的存取性能呢?这便是本文要介绍的一个数据库分表方案了。
本文基于Mybaits实现,前置知识可以参考前文:
SpringBoot集成MyBatis的相关要点
思路
分表的最大难点,在于确定你存取的数据在哪张表上。
解决方案在于将一个不可或缺的字段(主键)作为分表依据,我们可以通过对其进行哈希计算,再取模表的数量,即可确定数据在第几张表上了,最后再对表名进行拼接,即可确定表名。
案例
1.domain层
我们定义了一个和数据库表字段对应的JavaBean
package com.example.itspringbootmybatisshared.domain;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;import java.math.BigDecimal;import java.util.Date;/** * 用户信息 * * @author hongcunlin */@Data@NoArgsConstructor@AllArgsConstructor@Builderpublic class UserInfo { /** * 自增ID */ private Long id; /** * 用户名 */ private String name; /** * 用户余额 */ private BigDecimal money; /** * 创建时间 */ private Date createTime = new Date();}
2.mapper层
我们定义了两个数据库访问层的接口
package com.example.itspringbootmybatisshared.mapper;import com.example.itspringbootmybatisshared.domain.UserInfo;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Component;/** * @author hongcunlin */@Mapper@Componentpublic interface UserInfoMapper { /** * 新增用户 * * @param userInfo 用户信息 * @param tableName 表名 */ void addUserInfo(@Param("userInfo") UserInfo userInfo, @Param("tableName") String tableName); /** * 查询用户信息 * * @param id 用户id * @param tableName 表名 * @return 用户信息 */ UserInfo getUserInfoById(@Param("id") Long id, @Param("tableName") String tableName);}
编写对应的SQL
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ";><!--mapper--><mapper namespace="com.example.itspringbootmybatisshared.mapper.UserInfoMapper"> <!--获取所有用户信息--> <select id="getAll" resultType="com.example.itspringbootmybatisshared.domain.UserInfo"> select * from user_info; </select> <!-- 新增用户,注意tableName不能预编译 --> <insert id="addUserInfo"> insert into ${tableName} (id, name, money, create_time) values (#{userInfo.id, jdbcType=BIGINT}, #{userInfo.name, jdbcType=VARCHAR}, #{userInfo.money, jdbcType=DECIMAL}, #{userInfo.createTime, jdbcType=DATE}) </insert> <!-- 查询用户,注意tableName不能预编译 --> <select id="getUserInfoById" resultType="com.example.itspringbootmybatisshared.domain.UserInfo"> select * from ${tableName} where id = #{id} </select></mapper>
3.Service层
我们定义了用户信息操作的接口与实现,主要是对mapper做了一层表名的封装,统一处理表名的相关逻辑
package com.example.itspringbootmybatisshared.service;import com.example.itspringbootmybatisshared.domain.UserInfo;/** * 用户Service层接口 * * @author hongcunlin */public interface UserInfoService { /** * 新增用户信息 * * @param userInfo 用户信息 */ void addUserInfo(UserInfo userInfo); /** * 查询用户信息 * * @param id 用户id * @return 用户信息 */ UserInfo getUserInfoById(Long id);}
package com.example.itspringbootmybatisshared.service.impl;import com.example.itspringbootmybatisshared.domain.UserInfo;import com.example.itspringbootmybatisshared.mapper.UserInfoMapper;import com.example.itspringbootmybatisshared.service.UserInfoService;import com.example.itspringbootmybatisshared.utils.TableUtils;import org.springframework.stereotype.Service;import javax.annotation.Resource;/** * 用户Service层实现 * * @author hongcunlin */@Servicepublic class UserInfoServiceImpl implements UserInfoService { /** * 用户mapper层 */ @Resource private UserInfoMapper userInfoMapper; /** * 新增用户信息 * * @param userInfo 用户信息 */ @Override public void addUserInfo(UserInfo userInfo) { userInfoMapper.addUserInfo(userInfo, TableUtils.getTabNameById(userInfo.getId())); } /** * 查询用户信息 * * @param id 用户id * @return 用户信息 */ @Override public UserInfo getUserInfoById(Long id) { return userInfoMapper.getUserInfoById(id, TableUtils.getTabNameById(id)); }}
4.工具层
这是本文的核心,就是如何确定表在哪里。
我们定义了2个常量,分别是表名的基础名称、表数量。
我们定义了1个获取表名的方法,便是通过对用户的id进行hash计算,在%表数量,最后再拼接表名的基础组成部分。
package com.example.itspringbootmybatisshared.utils;/** * 分表工具 * 定位表 * * @author hongcunlin */public class TableUtils { /** * 表名基本组成部分 */ private static final String TAB_NAME_BASE = "user_info_"; /** * 表数量 */ private static final Integer TAB_NUM = 8; /** * 根据id获取表名 * * @param id 用户id * @return 表名 */ public static String getTabNameById(Long id) { return TAB_NAME_BASE + (id.hashCode() % TAB_NUM + 1); }}测试
我们先测试新增数据
package com.example.itspringbootmybatisshared;import com.example.itspringbootmybatisshared.domain.UserInfo;import com.example.itspringbootmybatisshared.service.UserInfoService;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import java.math.BigDecimal;@SpringBootTestpublic class UserAddTest { @Resource UserInfoService userInfoService; @Test void addUserInfoTest() { UserInfo userInfo = new UserInfo(); userInfo.setId(10000L); userInfo.setMoney(new BigDecimal("10.00")); userInfo.setName("中国电信"); userInfoService.addUserInfo(userInfo); }}
运行完上面的单元测试,我查询数据库,可以看到该表user_info_1已有该数据了
我们再进行查询数据测试
package com.example.itspringbootmybatisshared;import com.example.itspringbootmybatisshared.domain.UserInfo;import com.example.itspringbootmybatisshared.service.UserInfoService;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;@SpringBootTestpublic class UserQueryTest { @Resource UserInfoService userInfoService; @Test void getUserInfoTest() { UserInfo userInfo = userInfoService.getUserInfoById(10000L); System.out.println(userInfo); }}
运行完上面的单元测试,可以看到顺利打印出刚才新增的数据
说明分表读写是正常的。
最后
分表很好的解决了我们数据量大存取的性能问题。
此外,分库的解决方案和分表类似,只不过多了1层确定数据是在哪个库上的逻辑,而这逻辑和确定数据在哪张表上是一样的,依旧是依赖一个业务主键进行哈希计算、取模确定的。
然而分表也有一个缺点,就是你以后的写入、查询,都需要附带一个业务主键,由它来确定库表在的具体位置,不过数据基本都有主键的,而那个主键通常就可以用来业务主键,逻辑上并没有增加多少障碍。
标签: #java bigdecimal取模