作为一个企业或者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大表归档的更多相关文章

  1. [记录]一则清理MySQL大表以释放磁盘空间的案例

    一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...

  2. 优秀后端架构师必会知识:史上最全MySQL大表优化方案总结

    本文原作者“ manong”,原创发表于segmentfault,原文链接:segmentfault.com/a/1190000006158186 1.引言   MySQL作为开源技术的代表作之一,是 ...

  3. MySQL 大表优化方案(长文)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  4. 从云数据迁移服务看MySQL大表抽取模式

    摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来. 小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨.一开始爆内存被客户怼,再后来迁移效率低下再被怼.MySQL ...

  5. mysql 大表拆分成csv导出

    最近公司有一个几千万行的大表需要按照城市的id字段拆分成不同的csv文件. 写了一个自动化的shell脚本 在/home/hdh 下面 linux-xud0:/home/hdh # lltotal 1 ...

  6. 详解MySQL大表优化方案( 转)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  7. MySQL 大表优化方案探讨

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  8. Mysql大表查询优化技巧总结及案例分析

    http://www.169it.com/article/3219955334.html     sql语句使用基本原则:1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 orde ...

  9. MySQL大表优化方案

    转:https://segmentfault.com/a/1190000006158186?hmsr=toutiao.io&utm_medium=toutiao.io&utm_sour ...

随机推荐

  1. 牛客多校第六场-H-Pair

    链接:https://ac.nowcoder.com/acm/contest/887/H来源:牛客网 题目描述 Given three integers A, B, C. Count the numb ...

  2. PHP-全排列

    给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3]输出:[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]] ...

  3. laravel多字段模糊匹配

    use App\Models\Resume; $resume = Resume::query(); $content = $request->input('content'); $resume ...

  4. QT之QCustomPlot(二)

    怎么设置X,Y轴位置 Manages a single axis inside a QCustomPlot. Usually doesn't need to be instantiated exter ...

  5. 构造——cf1213E

    分情况讨论,构造很简单 #include<bits/stdc++.h> using namespace std; #define N 200005 ],t[]; int n,s1,s2,t ...

  6. delphi 调用js脚本

    function ExecScript(Code,Lang,Func:string):string; var script:OleVariant; begin script:=CreateOleObj ...

  7. 天照(amaterasu)

    天照(amaterasu) 有些时候,出题人真的不想写背景. 总而言之,天照现在有一个长度为 $ N $ 序列,她有 $ M $ 次询问,对于第 $ i $ 次询问 $ l_i,r_i,x_i $ 你 ...

  8. OS: 生产者消费者问题(二) ---- 系统V IPC通信-信号量和共享内存

    在上一篇“OS: 生产者消费者问题(多进程+共享内存+信号量)”中提到的方法二: 如果进程之间并没有父子关系,但是协商好了共享存储的 KEY , 那么在每个进程中,就可以通过 KEY 以及 shmge ...

  9. HDU2586---How far away ?(lca算法)

    Problem Description There are n houses in the village and some bidirectional roads connecting them. ...

  10. Java-Class-FC:java.util.Optional

    ylbtech-Java-Class-FC:java.util.Optional 1.返回顶部   2.返回顶部 1.1. import java.util.Optional; 1.2.1. @Api ...