MySql数据库分表分区实践

1. 背景 —— 公司物联网项目

海量设备经过物联网服务接入云端,设备每30s上报一次自身数据(如下称为动态数据)。 物联网服务将设备上报的数据转发给数据处理网关,由数据入库网关执行批量入库操做插入数据库。 项目大体技术架构以下图:mysql

2. 问题

接入的设备数量较大时,上报的动态数据数据量过大,致使单表查询过慢。sql

假设有1万台设备,每台设备每30秒上报一次动态数据,那每分钟就会产生2万条数据,天天会产生2880万条数据,一年将会产生100亿条以上的数据。数据库

这么大的数据量若是进行单表查询数据库分析等操做延迟是彻底没法接受的,故须要寻找一种解决方案。bash

3. 技术背景

3.1 分表

这里的分表指的是根据设备的序列号将必定数量的设备拆分存储在不一样的表中,减小单表的数据量级。服务器

3.2 分区

MySql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(能够经过my.cnf中的datadir来查看)。架构

一张表主要对应着三个文件,一个是frm存放表结构,一个是myd存放表数据,一个是myi存表索引。若是一张表的数据量太大,mydmyi就会变的很大,查找数据就会变的很慢。函数

MySql的分区功能,在物理上将这一张表对应的三个文件,分割成许多个小块,这样查找一条数据时,就不用所有查找了,只要知道这条数据在哪一块,而后在那一块找就好了,这样就能够很大的提升数据查询的效率。性能

MySql5.1及以上版本支持分区功能。 MySql的分区方法主要有:测试

3.2.1 range分区:

range分区意思就是以某个字段为基准的连续分区,好比id小于3的1个分区,id小于6的一个分区,id小于100的一个分区。spa

3.2.2 list分区:

list分区就是以某个字段为基准,该字段从属于一个列表范围内的分区,好比id为1,3,5,7的一个分区,2,4,6,8的一个分区。

3.2.3 hash分区:

hash分区用于确保数据在预先设定数目的分区中平均分布,好比预先设置分区数量为3个,则全部数据都会被平均分布在3个分区内。

3.2.4 key分区:

按照KEY进行分区相似于按照HASH分区,除了HASH分区使用的用户定义的表达式,而KEY分区的 哈希函数是由MySQL 服务器提供。

3.2.5 子分区:

子分区是分区表中每一个分区的再次分割,子分区既可使用HASH希分区,也可使用KEY分区。这也被称为复合分区(composite partitioning)。 子分区需遵循如下规则:

  • 若是一个分区中建立了子分区,其余分区也要有子分区
  • 若是建立了了分区,每一个分区中的子分区数必有相同
  • 同一分区内的子分区,名字不相同,不一样分区内的子分区名子能够相同(5.1.50不适用)

4. 解决方案

4.1 分表设计

设计为每1000个设备一张表,表名为t_data_序号。

假设有1万台设备,则根据设备序列号将数据分散存储在t_data_1 ~ t_data_10 十张表中。

同时增长一张设备-动态数据关系表(表名t_device_table_map)来存储设备和动态数据表的关系,以便对设备数据作增删改查操做时能找到它对应的表,t_device_table_map表的结构以下:

应用平台导入设备时,根据设备数量判断导入设备的动态数据应该存储在哪张表,并将设备和动态数据表关系写入到t_device_table_map中。

数据处理启动时载入t_device_table_map表数据到本身的内存中,而后在将设备上报的数据入库前从自身内存读取该设备属于哪一个动态数据表,再组装Sql执行入库操做。

4.2 分区设计

因为设备的数据是持续性上报的,因此考虑使用Range分区。

分区设计为以数据采集时间为基础,每周一个分区,每张表预设10年的分区。 按每一个设备每30秒上报一条数据计算,每一个分区大约有 10002460*2 = 2880000条数据。

建表语句以下:

CREATE TABLE `t_data_1` (
  `i_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `i_status` bit(1) DEFAULT NULL,
  `c_device_sequence` varchar(32) DEFAULT NULL COMMENT '设备序列号',
  `t_collect_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '数据采集时间',
  ...
  PRIMARY KEY (`i_id`,`t_collect_time`),
  KEY `index_c_device_sequence` (`c_device_sequence`,`t_collect_time`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=398404 DEFAULT CHARSET=utf8
/*!50500 PARTITION BY RANGE  COLUMNS(t_collect_time)
(PARTITION p20171224 VALUES LESS THAN ('2017-12-24 00:00:00') ENGINE = MyISAM,
 PARTITION p20171231 VALUES LESS THAN ('2017-12-31 00:00:00') ENGINE = MyISAM,
 PARTITION p20180107 VALUES LESS THAN ('2018-01-07 00:00:00') ENGINE = MyISAM,
 ...
 PARTITION p20271212 VALUES LESS THAN ('2027-12-12 00:00:00') ENGINE = MyISAM,
 PARTITION p20271219 VALUES LESS THAN ('2027-12-19 00:00:00') ENGINE = MyISAM) */;
/*!40101 SET character_set_client = @saved_cs_client */;

复制代码

5. 测试

以120万条数据测试,分表(10张)分区查询时间为0.1秒左右,见下图:

不分表也不分区,查询时间须要1秒以上,见下图:

分表分区带来的性能提高是很明显的。

6. 思考

分区的数量是否是越多越好呢?确定不是的。

由于MySQL在执行查询操做的时候首先要去检索查询范围在哪些分区内,分区太多,这部分的操做耗时就增长了。此外分区过多,可能会致使内存占用升高的问题。

怎么样分区,分多少个区才最合适,还须要长期的观察和大量数据的实验。

相关文章
相关标签/搜索