mysql 归档_MySQL数据归档的几种操作方法介绍
使用MySQL的过程,经常会遇到一个问题,比如说某张”log”表,用于保存某种记录,随着时间的不断的累积数据,但是只有最新的一段时间的数据是有用的;这个时候会遇到性能和容量的瓶颈,需要将表中的历史数据进行归档。下面来说说几种常见的数据归档方式。
一、使用分区,再利用分区交换技术能够很好地把指定分区中的数据移动到指定表中,这个需要在项目之处就进行此操作。
具体可以看博客分区章节,这几不做介绍。
二、利用存储过程和事件来定期进行数据的导出删除操作。
1 、创建一个新表,表结构和索引与旧表一模一样
create table table_new like table_old;
1
createtabletable_newliketable_old;
2 、新建存储过程,查询30天的数据并归档进新数据库,然后把30天前的旧数据从旧表里删除
delimiter $
create procedure sp()
begin
insert into tb_new select * from table_old where rectime < NOW() - INTERVAL 30 DAY;
delete from db_smc.table_old where rectime < NOW() - INTERVAL 30 DAY;
end
1
2
3
4
5
6
delimiter$
createproceduresp()
begin
insertintotb_newselect*fromtable_oldwhererectime
deletefromdb_smc.table_oldwhererectime
end
3、创建EVENT,每天晚上凌晨00:00定时执行上面的存储过程
create event if not exists event_temp
on schedule every 1 day
on completion preserve
do call sp();
1
2
3
4
createeventifnotexistsevent_temp
onscheduleevery1day
oncompletionpreserve
docallsp();
备注:第一次执行存储过程的时候因为历史数据过大, 可能发生意外让该次执行没有成功。重新执行时会遇到报错ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction,应急解决方案如下:
1、执行show full processlist;查看所有MySQL线程。
2、执行SELECT * FROM information_schema.INNODB_TRX\G; 查看是否有错误线程,线程id在show full processlist;的结果中状态为sleep。
3、kill进程id。
另外写存储过程的时候可以控制事务的大小,比如说可以根据时间字段每次归档一天或者更小时间段的数据,这样就不会有大事务的问题,里面还可以加入日志表,每次归档操作的行为都写入日志表,以后查起来也一目了然。
三、使用percona-toolkit的pt-archiver工具来进行历史数据归档,支持删除和不删除元数据的选择。
pt-archiver使用的场景:
1、清理线上过期数据。
2、清理过期数据,并把数据归档到本地归档表中,或者远端归档服务器。
3、两张表之间的数据不完全相同,希望合并。此时加上–ignore或–replace选项,可以轻松实现。
4、导出线上数据,到线下数据作处理。
其它作用:
1、用于清理过期数据purge
$ pt-archiver --source h=10.99.73.9,P=3306,u=mha,p=123456,D=sbtest,t=sbtest \
--no-check-charset \
--where 'id<50000' \
--purge \
--limit=2 \
--statistics
1
2
3
4
5
6
$pt-archiver--sourceh=10.99.73.9,P=3306,u=mha,p=123456,D=sbtest,t=sbtest\
--no-check-charset\
--where'id<50000'\
--purge\
--limit=2\
--statistics
注意:--source后的DSN之间不能空格出现,否则会出错。 --where条件的值,有字符串的,要用引号括起来。--limit表示,每组一次删除多少条数据(注意:如果数据比较多时,也可以设置大一些,减少循环次数),最终的清理操作,还是通过Where pK=xx来处理的。
2、用于把数据导出文件,不用删除原表中数据
$ pt-archiver --source h=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest \
--dest h=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest_like \
--where 'id>50000' \
--progress 5000 \
--no-delete \
--file "/tmp/pt-archiver.dat" \
--limit=10000 \
--txn-size=10000 \
--statistics
1
2
3
4
5
6
7
8
9
$pt-archiver--sourceh=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest\
--desth=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest_like\
--where'id>50000'\
--progress5000\
--no-delete\
--file"/tmp/pt-archiver.dat"\
--limit=10000\
--txn-size=10000\
--statistics
参数说明:
--statistics:结束的时候给出统计信息:开始的时间点,结束的时间点,查询的行数,归档的行数,删除的行数,以及各个阶段消耗的总的时间和比例,便于以此进行优化。
--where:给出表中要处理的数据的查询条件。
--progress:每处理progress指定的行数后,就打印一次信息。
--no-delete:表示不删除原来的数据,注意:如果不指定此参数,所有处理完成后,都会清理原表中的数据。
--limit:表示每次事务删除多少条数据,默认1条(注意:如果数据比较多时,也可以设置大一些,减少循环次数)。
--txn-size:每个事务提交的数据行数(包括读写操作),批量提交,增加该值可以提升归档性能。
--file:数据存放的文件,最好指定绝对路径,文件名可以灵活地组合(另外,我测试过写文件与不写文件速度几乎差不多,原本以为不写文件速度会快)。
%d Day of the month, numeric (01..31)
%H Hour (00..23)
%i Minutes, numeric (00..59)
%m Month, numeric (01..12)
%s Seconds (00..59)
%Y Year, numeric, four digits
%D Database name
%t Table name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
%dDayofthemonth,numeric(01..31)
%HHour(00..23)
%iMinutes,numeric(00..59)
%mMonth,numeric(01..12)
%sSeconds(00..59)
%YYear,numeric,fourdigits
%DDatabasename
%tTablename
注意字符集问题
Tips:如果你的数据库字符集是utf8的话,需要在运行pt-archive的机器上,在/etc/my.cnf文件中的[client]下面添加default-character-set = utf8,否则导出的文件内容中文会乱码,我就被这个问题坑了。
测试归档
首先压测10万数据。
mysql> select count(1) from sbtest;
+----------+
| count(1) |
+----------+
| 100000 |
+----------+
1 row in set (0.04 sec)
1
2
3
4
5
6
7
mysql>selectcount(1)fromsbtest;
+----------+
|count(1)|
+----------+
|100000|
+----------+
1rowinset(0.04sec)
创建一张归档表,表结构跟原表一样。
mysql> CREATE TABLE `sbtest_like` like sbtest;
1
mysql>CREATETABLE`sbtest_like`likesbtest;
开始进行归档表操作,不删除原有表数据记录(如果想删除原表数据需要去掉--no-delete参数即可)
$ pt-archiver --source h=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest \
--dest h=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest_like \
--where 'id>50000' \
--progress 5000 \
--no-delete \
--file "/tmp/pt-archiver.dat" \
--limit=10000 \
--txn-size=10000 \
--statistics
TIME ELAPSED COUNT
2019-01-16T04:25:28 0 0
2019-01-16T04:25:29 0 5000
2019-01-16T04:25:29 1 10000
2019-01-16T04:25:30 2 15000
2019-01-16T04:25:31 3 20000
2019-01-16T04:25:32 4 25000
2019-01-16T04:25:32 4 30000
2019-01-16T04:25:33 5 35000
2019-01-16T04:25:34 6 40000
2019-01-16T04:25:35 7 45000
2019-01-16T04:25:36 8 49999
Started at 2019-01-16T04:25:28, ended at 2019-01-16T04:25:36
Source: D=sbtest,P=3306,h=10.10.0.109,p=...,t=sbtest,u=root
Dest: D=sbtest,P=3306,h=10.10.0.109,p=...,t=sbtest_like,u=root
SELECT 49999
INSERT 49999
DELETE 0
Action Count Time Pct
inserting 49999 5.2583 65.16
commit 10 0.1377 1.71
print_file 49999 0.1275 1.58
select 6 0.0629 0.78
other 0 2.4839 30.78
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$pt-archiver--sourceh=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest\
--desth=10.10.0.109,P=3306,u=root,p=123456,D=sbtest,t=sbtest_like\
--where'id>50000'\
--progress5000\
--no-delete\
--file"/tmp/pt-archiver.dat"\
--limit=10000\
--txn-size=10000\
--statistics
TIMEELAPSEDCOUNT
2019-01-16T04:25:2800
2019-01-16T04:25:2905000
2019-01-16T04:25:29110000
2019-01-16T04:25:30215000
2019-01-16T04:25:31320000
2019-01-16T04:25:32425000
2019-01-16T04:25:32430000
2019-01-16T04:25:33535000
2019-01-16T04:25:34640000
2019-01-16T04:25:35745000
2019-01-16T04:25:36849999
Startedat2019-01-16T04:25:28,endedat2019-01-16T04:25:36
Source:D=sbtest,P=3306,h=10.10.0.109,p=...,t=sbtest,u=root
Dest:D=sbtest,P=3306,h=10.10.0.109,p=...,t=sbtest_like,u=root
SELECT49999
INSERT49999
DELETE0
ActionCountTimePct
inserting499995.258365.16
commit100.13771.71
print_file499990.12751.58
select60.06290.78
other02.483930.78
看一下最终处理结果:
mysql> select count(*) from sbtest where id>50000;
+----------+
| count(*) |
+----------+
| 50000 |
+----------+
1 row in set (0.01 sec)
mysql> select count(*) from sbtest_like;
+----------+
| count(*) |
+----------+
| 49999 |
+----------+
1 row in set (0.01 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql>selectcount(*)fromsbtestwhereid>50000;
+----------+
|count(*)|
+----------+
|50000|
+----------+
1rowinset(0.01sec)
mysql>selectcount(*)fromsbtest_like;
+----------+
|count(*)|
+----------+
|49999|
+----------+
1rowinset(0.01sec)
从结果看,跟我们理解的有点偏差,少归档了一条数据。但是如果你把归档条件改为 <50000 ,那么结果就又是正确的了,如下展示。
mysql> select count(*) from sbtest where id<50000;
+----------+
| count(*) |
+----------+
| 49999 |
+----------+
1 row in set (0.02 sec)
mysql> select count(*) from sbtest_like;
+----------+
| count(*) |
+----------+
| 49999 |
+----------+
1 row in set (0.01 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mysql>selectcount(*)fromsbtestwhereid<50000;
+----------+
|count(*)|
+----------+
|49999|
+----------+
1rowinset(0.02sec)
mysql>selectcount(*)fromsbtest_like;
+----------+
|count(*)|
+----------+
|49999|
+----------+
1rowinset(0.01sec)
这就要引入另外一个参数了:--safe-auto-increment
此参数的意思就是不归档最大AUTO_INCREMENT的行,默认为Yes。是为了防止在服务器重新启动时重新使用AUTO_INCREMENT值。如果你需要归档最大AUTO_INCREMENT的行,加上--no-safe-auto-increment参数即可。
生产环境中一般都是根据日期来归档数据,比如常见需求保留30天即可,此时where可以这么写 CreateTime <= date_add(now(), interval -30 day)。
除了用pt-archiver归档之外,还有一个特别大的用处,我给称之为“无锁导入数据”。在数据归档中还有一种需求(我经常遇到),为了不影响业务在某些情况下会对一些日志表或者其他表做归档,当表特别大时第一次处理此表就不太好处理,并且就算把表数据删除了后而表文件还是无法缩小。这个时候就可以用MySQL的神奇rename命令对表进行重命名,当然是业务允许情况下,如rename table Deal to Deal_201801, Deal_2018 to Deal,此操作是一个原子操作且特别快。做完这个动作之后,一般还会有一个需求就是把原表中某一段时间的数据导入到新的表中,可能是业务跑批需要或者后台查询需要。导数据该怎么弄呢?很自然可能想到使用insert into Deal select * from Deal_201801 where ...导入操作,但是不好意思,在导入数据的时候可能无法对新表进行操作,会导致业务异常。
如果换其他方式呢?写Python或Shell把数据读出来写入到文件,然后再从文件读出循环插入到新表,这当然是可以的。但当数据特别多时,也需要写多线程了。其实这个时候就可以借助pt-archiver进行数据导入了,从老的表读出来然后直接插入到新的表,他的原理与我们上面说的方式类似,但是它更友好,且更快。
四、使用union或union all来进行结果合并
当历史数据进行归档后,这个时候就有需求了。当需要查看历史数据和现有表数据时有没有什么好的方法呢?其实可以使用union或union all来进行多表结果合并操作。
在数据库中,union和union all关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。union在进行表联接时会筛选掉重复的记录,包括左表去重(会给左表所有字段创建为一个主键),然后再返回结果。
select * from test_union1
union
select * from test_union2
1
2
3
select*fromtest_union1
union
select*fromtest_union2
这个语句的执行流程是这样的:
1. 创建一个内存临时表,这个临时表会存入左表字段,创建主键。
2. 执行右表,并存入临时表中。在存入临时表时,如果已经存在了相同条目就会违反唯一性约束,所以插入失败;然后继续执行插入。
3. 临时表中按行取出数据,返回结果,并删除临时表。
可以看到,这里的内存临时表起到了暂存数据的作用,而且计算过程还用上了临时表主键的唯一性约束,实现了 union 的语义。如果表数据量大的话可能会导致用磁盘进行排序。
而union all只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。 从效率上说,union all要比union快很多,也不需要临时表了。所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用union all,语法如下:
select * from test_union1
union all
select * from test_union2
1
2
3
select*fromtest_union1
unionall
select*fromtest_union2
使用union组合查询的结果集有两个最基本的规则:
1. 所有查询中的列数和列的顺序必须相同。
2. 数据类型必须兼容。
虽然这个可以简便解决数据查询问题,但是还是需要代码层面的调整。
union还有一个地方可能会用到,如web项目中经常会碰到整站搜索的问题,即客户希望在网站的搜索框中输入一个词语,然后在整个网站中只要包含这个词的页面都要出现在搜索结果中。由于一个web项目不可能用一张表就全部搞定的,所以这里一般都是要用union联合搜索来解决整个问题的。
如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
mysql 归档_MySQL数据归档的几种操作方法介绍相关推荐
- 归一化mysql函数_数据归一化和两种常用的归一化方法
数据归一化和两种常用的归一化方法 一.总结 一句话总结: min-max标准化:x* =(x-min)/(max-min):新数据加入,需重新计算max和min Z-score标准化:x* =(x-μ ...
- linux新增mysql用户_MySQL创建用户的三种方法
前言:MySQL创建用户的方法分成三种:INSERT USER表的方法.CREATE USER的方法.GRANT的方法. 一.账号名称的构成方式 账号的组成方式:用户名+主机(所以可以出现重复的用户名 ...
- php mysql隔离_mysql隔离级别有几种
mysql隔离级别有4种,分别是:1.Read Uncommitted(读取未提交内容):2.Read Committed(读取提交内容):3.Repeatable Read(可重读):4.Seria ...
- php mysql修复_MySQL数据表损坏的巧妙修复
此文章主要向大家描述的是正确修复损坏的MySQL数据表的实际操作流程,我们大家都知道断电或者非正常关机往往会导致MySQL(和PHP搭配之最佳组合)数据库出现错误.其主要有两种方法: 一种方法使用My ...
- java 备份 mysql 日志_MySQL 数据备份与还原
点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:逆心 链接:https://www.cnblogs.com/kissdodog 公众 ...
- 下载 mysql 拖_mysql数据库托从
安装数据库备份工具,因为数据库中的数据量达到270G在使用传统的备份工具mysqldump备份,时间耗费比较长,综合考虑,我们选择比较熟悉的第三方工具xtrabackup进行备份恢复 yum -y i ...
- sp MySQL 导入_mysql数据导入redis
先编写sql脚本,查询好需要导入redis的数据,同时在脚本中设定好redis的类型:最外层的查询需要设定好查询的字段数量,key value各算一个. 我用的是hash结构,将表明以key的形式存储 ...
- mysql 统计_mysql数据统计级别技巧
本文提及技巧只适用数据统计,OLAP场景,不建议用于业务sql. 分组top问题 知识点:巧用局部变量,实现Hive窗口分析函数功能row_number() over(partition by e ...
- mysql有热备设置_Mysql数据热备配置与操作方法
Server: 1.grant all on *.* to postfix@'192.168.128.174' identified by 'postfix'; --新建授权用户 2.# cd /va ...
- spi的dma方式前四个字节_前嗅教你大数据:常见几种编码介绍
为什么要编码? 大家可以先思考个问题: 计算机是如何表示我们人类能够理解的符号的,也就是我们人类使用的语言. 人类的语言有太多了,因而表示这些语言的符号太多. 我们无法用计算机中一个基本的存储单元-- ...
最新文章
- 20181023-2 贡献分配
- 数据集的使用方法和技巧
- boost::python::ndarray相关的测试程序
- 基于Xml 的IOC 容器-分配注册策略
- 键盘发展简史:144年独孤求败的QWERT键盘
- 北京活动预告丨来ACOUG 年会过个温暖的冬天吧!
- pgp 私钥需要什么样的保护措施_参与以太坊 2.0 存款合约前需要了解的相应风险...
- https被修改成http排查过程
- Akka和Actor一起工作的消息《ten》译
- 计算机科学与技术高中选课,2019-2021年新高考专业选课要求 大学个专业选科要求解读...
- MATLAB符号运算——微分
- python在线评测系统_怎样做一个 Online Judge(在线评测系统)?
- 小瘦牛虚拟无线路由器官方版
- 伪标签Pseudo Label
- 绿盟漏扫使用手册_【技术干货】Oracle数据库漏洞扫描指南
- StarUML 3 中文文档 包图
- 个人建站系列步骤流程(二.申请域名+实名认证)
- 微信h5隐藏导航栏和状态栏_导航栏未在SwiftUI中隐藏
- element-plus分页组件默认显示英文,设置成中文显示。
- html怎么让一行文字有滚动的效果,网页HTML代码:滚动文字的制作