Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > Mysql 设备实时状态表水平分表

Mysql 数据库中设备实时状态表水平分表

作者:weixin_43833540

在使用 Mysql 数据库存储设备上报日志时,存在一张设备实时状态表,随着时间推移,数据量变得十分庞大,为了更好地管理和查询数据,提高数据库性能,需要对该表进行水平分表操作,下面就来介绍一下如何实现

一、 需求概述

在使用 Mysql 数据库存储设备上报日志时,存在一张设备实时状态表,随着时间推移,数据量变得十分庞大。为了更好地管理和查询数据,提高数据库性能,需要对该表进行水平分表操作。同时,存在分页查询的需求,不过仅在针对单个设备状态查询时才需要分页展示结果,以方便查看设备在不同时间段的状态信息,避免一次性返回大量数据影响性能和使用体验。

二、分表键的选择策略详解

1. 哈希取模分片

哈希取模分片是常用的水平分表策略,通过对选定的分片键(如设备编号)进行哈希运算后取模,确定数据存储的分表。

例如,在 Spring Boot + MyBatis 手动分表时,按以下代码逻辑实现哈希取模确定分表(以设备编号后三位数字简单取模为例,实际可采用更严谨算法):

public class TableShardingUtil {
    private static final int TABLE_COUNT = 10;  // 假设分表数量为 10

    public static String getTableNameByDeviceId(String deviceId) {
        int deviceIdSuffix = Integer.parseInt(deviceId.substring(deviceId.length() - 3));  // 获取设备编号后三位并转为整数
        int tableIndex = deviceIdSuffix % TABLE_COUNT;  // 取模确定分表索引
        return "device_status_" + String.format("%03d", tableIndex);  // 构建分表名,格式化为三位数字,如 device_status_001
    }
}

在 MyBatis 的 SQL 语句中利用该方法构建动态表名(XML 映射文件中):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.DeviceStatusMapper">
    <select id="getDeviceStatusByDeviceIdPage" resultMap="DeviceStatusResultMap">
        SELECT * FROM #{tableName}  <!-- 这里使用动态表名 -->
        WHERE device_id = #{deviceId}
        LIMIT #{offset}, #{limit}
    </select>
</mapper>

对应的 Mapper 接口方法传入计算得到的表名:

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;

@Mapper
public interface DeviceStatusMapper {
    List<DeviceStatus> getDeviceStatusByDeviceIdPage(
            @Param("tableName") String tableName,  // 新增表名参数
            @Param("deviceId") String deviceId,
            @Param("offset") int offset,
            @Param("limit") int limit);
}

业务逻辑层调用时先算出表名再传递给 Mapper 方法查询:

import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

@Service
public class DeviceStatusService {
    @Resource
    private DeviceStatusMapper deviceStatusMapper;

    public List<DeviceStatus> getDeviceStatusByDeviceIdPage(String deviceId, int pageNum, int pageSize) {
        String tableName = TableShardingUtil.getTableNameByDeviceId(deviceId);
        int offset = (pageNum - 1) * pageSize;
        return deviceStatusMapper.getDeviceStatusByDeviceIdPage(tableName, deviceId, offset, pageSize);
    }
}

通过这种方式实现基于哈希取模的分片键策略,动态依据设备编号确定分表并进行分页查询操作。

2. 范围分片

3. 一致性哈希分片

三、水平分表及分页查询实现示例

(一)数据库表结构设计

1.设备实时状态表结构(分表前)

假设设备上报的日志主要包含设备的基本信息、状态信息以及上报时间等内容,以下是一个简单的表结构设计示例:

字段名类型说明是否可空主键
idbigint自增唯一标识,每条日志记录的唯一编号
device_idvarchar(50)设备编号,用于唯一标识每一台设备
device_namevarchar(100)设备名称,方便直观了解设备情况
status_codeint设备状态码,不同数值代表不同的运行状态,例如 0 表示正常,1 表示故障等
status_detailtext设备状态详细描述,比如故障具体原因等信息
report_timedatetime设备上报该状态的时间
other_infovarchar(255)其他可能的补充信息,如设备所在位置等(可根据实际情况扩展)

在这个表结构中,id 作为主键保证每条记录的唯一性,便于数据的索引和管理。而 device_id 是区分不同设备的关键字段,后续水平分表就会基于它来进行操作,report_time 用于记录状态上报的时间点,方便后续按时间维度查询和分析设备状态变化情况等。

2.分表后的表结构

根据 device_id 作为分表键进行水平分表,分表后的每张表结构与原始表结构基本一致,只是数据根据分表规则分散到了不同的表中。

例如,假设按照设备编号对 10 取模的方式将数据分到 10 张表中,表名可以分别命名为 device_status_0device_status_1device_status_2…… device_status_9

device_status_0 为例,其表结构如下:

字段名类型说明是否可空主键
idbigint自增唯一标识,每条日志记录的唯一编号
device_idvarchar(50)设备编号,用于唯一标识每一台设备
device_namevarchar(100)设备名称,方便直观了解设备情况
status_codeint设备状态码,不同数值代表不同的运行状态,例如 0 表示正常,1 表示故障等
status_detailtext设备状态详细描述,比如故障具体原因等信息
report_timedatetime设备上报该状态的时间
other_infovarchar(255)其他可能的补充信息,如设备所在位置等(可根据实际情况扩展)

其他 device_status_1device_status_9 等表结构均与之相同,只是每张表中存储的数据是根据 device_id 取模规则分配过来的对应设备的状态日志信息。

(二)使用 springboot + mybatis 手动水平分表并实现分页

  1. 分表设计
    首先要确定分表键,对于设备实时状态表来说,设备编号(device_id)是比较合适的分表键选择。因为往往是针对单个设备的操作和查询较多,以设备编号进行分表能让同一设备的数据集中存储在一张分表中,方便后续查询和管理。可以按照一定规则,比如根据设备编号对分表数量取模的方式,将数据均匀分散到不同的分表中,例如有 10 张分表,设备编号为 1001 的设备,通过 1001 % 10 确定其存储在对应的分表中。
  2. 代码实现
    在 Spring Boot 项目中,配置好 MyBatis 相关依赖和数据库连接信息。创建针对不同分表的 Mapper 接口和对应的 XML 映射文件。在查询单个设备状态并分页时,需要在 Mapper 接口中定义相应的方法,例如:
List<DeviceStatus> getDeviceStatusByPage(@Param("deviceId") String deviceId, @Param("offset") int offset, @Param("limit") int limit);

在 XML 映射文件中编写 SQL 语句,通过传入的设备编号确定要查询的分表,结合传入的偏移量(offset)和每页数量(limit)来实现分页查询,示例 SQL 如下:

SELECT * FROM device_status_${deviceId % 10} 
WHERE device_id = #{deviceId} 
LIMIT #{offset}, #{limit};

在 Service 层调用该 Mapper 方法,传入相应参数即可实现单个设备状态的分页查询,通过这种手动方式灵活控制分表和分页逻辑,但需要自行处理较多的细节,如分表规则的维护等。

(三)使用 springboot + sharing-jdbc + mybatis 实现水平分表并分页查询

  1. 分表配置
    同样选择设备编号(device_id)作为分表键。在 Spring Boot 项目中引入 Sharding-JDBC 相关依赖,然后通过配置文件(如 application.yml)进行分表规则配置。例如:
sharding:
  tables:
    device_status:
      actual-data-nodes: device_status_$->{0..9}.device_status
      table-strategy:
        inline:
          sharding-column: device_id
          algorithm-expression: device_status_$->{device_id % 10}

这样 Sharding-JDBC 会按照配置的规则自动根据设备编号对数据进行分表存储。
2. 分页查询实现
在 MyBatis 的 Mapper 接口中定义查询方法,和上面类似,例如:

List<DeviceStatus> getDeviceStatusByPageWithSharding(@Param("deviceId") String deviceId, @Param("offset") int offset, @Param("limit") int limit);

在 XML 映射文件中编写 SQL 语句时,无需像手动分表那样关注具体分表的选择,只需要按照常规的查询语法编写,Sharding-JDBC 会在底层根据配置的分表规则自动路由到正确的分表上进行查询并实现分页,示例 SQL 如下:

SELECT * FROM device_status 
WHERE device_id = #{deviceId} 
LIMIT #{offset}, #{limit};

在 Service 层调用该方法即可轻松实现单个设备状态的分页查询,Sharding-JDBC 帮助简化了分表相关的很多复杂操作,提高了开发效率。

到此这篇关于Mysql 数据库中设备实时状态表水平分表的文章就介绍到这了,更多相关Mysql 设备实时状态表水平分表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文