【归档】Mysql大表归档
作为一个企业或者DBA,我们通常会有这种想法,数据是一个公司的核心命脉,应该需要永久保存,很多时候DBA和开发沟通的时候,开发人员也会这么告诉我们,这份数据非常重要,数据需要永久保存。然而,如果将数据库的数据永久保存,那么迟早有一天,你会拥有一个非常大的数据库。作为一个DBA,通常为了业务对数据库的操作性能考虑和存储容量的考虑。我们会建议对数据库里大表进行数据归档,例如将使用的高频数据保留在当前表,对低频数据保留在归档表中。当然有些公司会将数据抽取到Hadoop、Hbase进行归档和离线分析,或者使用infobright作为归档引擎,这里不们不做具体深入分析。本文我们只从DBA运维人员的角度,是用MySQL现有技术来帮大家分析如何对大表进行归档,以提高数据库性能和解决数据库容量问题。
例子
首先我们举个大表的例子,你负责的数据库有一个交易数据存储表,已经有了200GB的数据。假设已经存储了5年的数据,但是在去年,因为业务量的增加,这个表的数据量翻了一翻。现在我们来总结一下,你现在拥有一个存储5年数据的大表,但是你的应用程序通常只需要查询1到3个月的数据,在此之前数据程序是不会去查询,可能有些时候,某个人需要定位问题或对帐会查询1年之内的数据,或者数据分析部门需要对近三年的数据进行汇总生成报表数据,基于以上考虑,为了满足这些需求,表里的数据不能丢,也不能删除 ,我们通常会将这些数据存储在1个表里,来方便给各个查询需求提供数据支持。
但是,这么庞大一张表,这种方式会极大的影响了数据库的性能,所以我们考虑可以尝试将上面三种应用查询场景的数据分别放在3个表里,来解决这种性能问题:
第一个表存放近期三个月数据,用于线上高频业务查询。
第二个归档表保存所有的历史数据用于低频查询,例如排查问题或者对账。
第三个是用于存储报告的汇总表。
有了上面这些表,我们就遵循单一责任的原则,为各种查询的需求提供最好的性能。首先我们有一个最近3个月数据的主表,可以大大提高查询性能和扩展性。举个例子,假如数据的容量在未来3-5年内每年都会翻倍,但是你的主表也只是用到这么多数据的子集。比如你三个月的主表数据为20GB,那么下一年就是40GB,再后一年也只有80GB,这些数据量也是非常容易管理的。另外随着时间的推移,软件和硬件也在不断改进和升级,所以可以通过升级和更新,可以不断保证业务的性能。我们将冷数据和热数据分别存储到不同的数据表中,这样也可以使得扩展性得到长期的保证。
如何实施归档
1.建立归档表_archive
这种方式是通过建立一个以_archive结尾的归档表来实施的。如果使用这种方式,那么一般需要在业务层进行查询的分离改造,比如基于我们的特定归档规则 ,对业务端核心代码改造或者使用proxy方案等来决定是使用主表还是归档表。同时我们还需要一个数据归档过程,当数据过时或者变成冷数据时,将该数据从主表迁移到归档表中。
在业务层查询时,我们可以通过时间字段来进行查询判断,例如将90天之前的数据在归档表中查询,否则就在主表查询。另一种方式可以通过增加status列去判断查询主表还是归档表,如果是inactive则查询归档表,否则就在主表查询。
2.根据日期进行分区
这种方式是通过对表进行分区来实现,虽然这是一种不同的物理数据模型,但是确实有助于将表的数据进行拆分到不同的物理磁盘,并且不需要代码的任何改造。作为DBA,一般对表进行分区是比较常用的方式,我们可以通过日期字段很容易确定哪些是冷数据,并根据日期将不同日期的时间分配到不同的分区中,在查询的时候,我们可以通过日期来从分区中快速定位到对应的数据,同时建立分区表也比较利于DBA对大表进行管理操作。
请看下面的例子,通过下面方式,我们可以将数据按照日期进行分配到正确的分区.
CREATE TABLE `largetable` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`dateCreated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` int default 1,
`sometext` text,
PRIMARY KEY (`id`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; Query OK, 0 rows affected (0.03 sec)
mysql> alter table largetable partition by RANGE(YEAR(dateCreated)) (
-> PARTITION p2016 VALUES LESS THAN (2017),
-> PARTITION p2017 VALUES LESS THAN (2018),
-> PARTITION p2018 VALUES LESS THAN (2019),
-> PARTITION p2019 VALUES LESS THAN (2020),
-> PARTITION p2020 VALUES LESS THAN (2021),
-> PARTITION pmax VALUES LESS THAN MAXVALUE);
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
在上面的例子中,我们根据数据行的创建时间,按年将数据放入到不同的分区,这里需要注意的是在2020年以后,我们还需要在表里添加新的年份,当然我们可以提前加更多的分区或者部署脚本来自动化创建新的分区。
3.通过状态进行分区
我们也可以通过status状态列来进行分区,这种情况下,通常状态列会包含active/inactive两种状态,然后通过update进行状态列的更新(使用replace或者insert+delete也是可以的),将数据放入到正确的分区当中。请看下面的示例:
mysql> CREATE TABLE `largetable` (
-> `id` bigint unsigned NOT NULL AUTO_INCREMENT,
-> `dateCreated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
-> `status` int default 1, — default active
-> `sometext` text,
-> PRIMARY KEY (`id`,`status`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.02 sec)
mysql> alter table largetable partition by list(status) (
-> partition pactive values in (1), — active
-> partition pinactive values in (2) — inactive
-> );
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from largetable partition (pactive);
Empty set (0.00 sec)
mysql> select * from largetable partition (pinactive);
Empty set (0.00 sec)
mysql> insert into largetable(sometext) values (‘hello’);
Query OK, 1 row affected (0.01 sec)
mysql> select * from largetable partition (pinactive);
Empty set (0.00 sec)
mysql> select * from largetable partition (pactive);
+—-+———————+——–+———-+
| id | dateCreated | status | sometext |
+—-+———————+——–+———-+
| 1 | 2017-10-30 10:04:03 | 1 | hello |
+—-+———————+——–+———-+
1 row in set (0.00 sec)
mysql> update largetable set status = 2 where id =1 ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from largetable partition (pactive);
Empty set (0.00 sec)
mysql> select * from largetable partition (pinactive);
+—-+———————+——–+———-+
| id | dateCreated | status | sometext |
+—-+———————+——–+———-+
| 1 | 2017-10-30 10:04:03 | 2 | hello |
+—-+———————+——–+———-+
1 row in set (0.00 sec)
4.通过ID进行分区
最后一种方式,我们介绍下通过自增ID主键列进行分区的方式,请看下面的示例:
mysql> CREATE TABLE `largetable` (
-> `id` bigint unsigned NOT NULL AUTO_INCREMENT,
-> `dateCreated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
-> `status` int default 1,
-> `sometext` text,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.02 sec)
mysql> alter table largetable partition by RANGE(id) (
-> PARTITION p1 VALUES LESS THAN (500000000),
-> PARTITION p2 VALUES LESS THAN (1000000000),
-> PARTITION p3 VALUES LESS THAN (1500000000),
-> PARTITION p4 VALUES LESS THAN (2000000000),
-> PARTITION p5 VALUES LESS THAN (2500000000),
-> PARTITION pmax VALUES LESS THAN MAXVALUE);
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0
上面的例子中,我们通过ID列进行了range分区,如果业务的查询基本都是主键查找,那么这种方式会比较有用。 与按照日期分区相比,按照ID分区还会有助于数据平均分配到各个分区。
最后总结
上述简单介绍了归档表和分区的一些方法,但是关键的一点是要如何选择正确的分区键和分区方式,这一点并不容易,我们需要和业务端进行充分沟通业务场景,并根据场景来选择合适的分区键,我们选择的分区键要能正确分区,并且不会降低表的查询速度。
另外一点,为了让优化器能将查询发送到正确的分区键,在创建分区表的时候,我们需要将分区键添加到主键里,并且理想情况下,该分区键能被包含在所有select/update/delete等语句的where条件里面,否则的话,你的查询将会按照顺序查找表对应的每个分区,这种情况下查询性能就没有那么好了。
---------------------
转自:https://blog.csdn.net/yajie_12/article/details/78798916
【归档】Mysql大表归档的更多相关文章
- [记录]一则清理MySQL大表以释放磁盘空间的案例
一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...
- 优秀后端架构师必会知识:史上最全MySQL大表优化方案总结
本文原作者“ manong”,原创发表于segmentfault,原文链接:segmentfault.com/a/1190000006158186 1.引言 MySQL作为开源技术的代表作之一,是 ...
- MySQL 大表优化方案(长文)
当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...
- 从云数据迁移服务看MySQL大表抽取模式
摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来. 小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨.一开始爆内存被客户怼,再后来迁移效率低下再被怼.MySQL ...
- mysql 大表拆分成csv导出
最近公司有一个几千万行的大表需要按照城市的id字段拆分成不同的csv文件. 写了一个自动化的shell脚本 在/home/hdh 下面 linux-xud0:/home/hdh # lltotal 1 ...
- 详解MySQL大表优化方案( 转)
当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...
- MySQL 大表优化方案探讨
当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...
- Mysql大表查询优化技巧总结及案例分析
http://www.169it.com/article/3219955334.html sql语句使用基本原则:1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 orde ...
- MySQL大表优化方案
转:https://segmentfault.com/a/1190000006158186?hmsr=toutiao.io&utm_medium=toutiao.io&utm_sour ...
随机推荐
- 使用ReadStream方法延时读取文件
const fs = require('fs'); let file = fs.createReadStream("filenpath.js"); file.pause(); fi ...
- 关系型数据库---MYSQL---系统学习
1.概述 1.1 mysql数据库是一种 客户端/服务器体系 的 数据库系统: 服务器部分 在启动运行后没有人机界面,所以终端用户 无法直接使用MySQL: 对MySQL数据库进行访问.操作 ...
- Delphi 消息函数 SendMessage函数和 PostMessage的区别
SendMessage函数 将指定的消息发到窗口.它调用特定窗口的窗口处理函数,并且不会立即返回,直到窗口处理函数处理了这个消息. PostMessage函数 将一个消息放入与创建这个窗口的消息队列相 ...
- mobx中使用class语法或decorator修饰器时报错
之前课程中老师用的babel的版本比较低,我在学习时安装的babel版本较高,因此每当使用class语法或decorator修饰器时都会出现一些报错的情况! ❌ ERROR in ./src/inde ...
- auth 模块使用篇
from django.cintrib import auth #登录模块 只要用auth模块一旦登录 就可以在项目的任意地方用request.user 拿到当前的用户对象 再通过 request ...
- js监听input输入字符变化
<p class="text-input"> <input type="text" id="username" autoC ...
- noip2002 普及组 过河卒
题目描述 棋盘上A点有一个过河卒,需要走到目标B点.卒行走的规则:可以向下.或者向右.同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点.因此称之为"马拦过 ...
- C语言新手写扫雷攻略1
工欲善其事,必先利其器,首先要准备好开发环境,既然是C语言,那就不是WinAPI的扫雷,就是纯的C语言开发,但是以前的C都是TC开发的,现在用肯定是过时很久了,但是也是有解决办法的,某些大神开发出Ea ...
- python re.findall 使用
python re.findall 使用 import re #\w 匹配字母数字及下划线 print(re.findall('\w','hello alan _god !@^&#^$^!*& ...
- CentOS6.8搭建LNMP环境
selinux可能会致使编译安装失败,我们先禁用它.永久禁用,需要重启生效 sed -i ‘s/SELINUX=enforcing/SELINUX=disabled/g’ /etc/selinux/c ...