寻觅生命中的那一片浅草......

文章属于类别 MySQL

利用undrop-for-innodb恢复MySQL数据

这是一个MySQL数据恢复工具,我们尝试下恢复MySQL数据

1 环境说明

  • VMware 8H2G
  • CentOS 6.2 64bit
  • MySQL version: 5.7.17-11 Percona Server
  • 一个没有任何读写的测试数据库实例
  • innodb_file_per_table on

2 安装

比较简单

cd /dist/src
git clone https://github.com/twindb/undrop-for-innodb.git
cd undrop-for-innodb/
make
gcc `$basedir/bin/mysql_config --cflags` `$basedir/bin/mysql_config --libs` -o sys_parser sys_parser.c

3 环境准备

create database recover;
use recover;
CREATE TABLE `actor` (
  `actor_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(45) NOT NULL,
  `last_name` varchar(45) NOT NULL,
  `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`actor_id`),
  KEY `idx_actor_last_name` (`last_name`)
) ENGINE=InnoDB AUTO_INCREMENT=201 DEFAULT CHARSET=utf8;

insert into actor(first_name, last_name) values('zhang', 'jian');
insert into actor(first_name, last_name) values('zhan', 'jian');
insert into actor(first_name, last_name) values('zha', 'jian');
insert into actor(first_name, last_name) values('zh', 'jian');
insert into actor(first_name, last_name) values('z', 'jian');

checksum table actor;
+-----------+------------+
| Table     | Checksum   |
+-----------+------------+
| per.actor | 2184463059 |
+-----------+------------+
1 row in set (0.00 sec)

# 此处模拟误删除表
DROP TABLE actor;

4 开始恢复

由于我们有表结构SQL了,所以本例没有恢复表结构,参考资料中有恢复表结构的操作

避免磁盘被覆盖写

由于是innodb_file_per_table on,就是drop操作会直接删除文件,此时比较稳妥的处理是关掉所有服务,将文件系统挂载为只读,由于本例是测试环境,没有任何写入,所以没有做以下处理

/etc/init.d/mysql stop
# 或者加-f参数,不过好像比较暴力
mount -o remount,ro /data

开始在磁盘上查找InnoDB页文件

本操作会在/dist/src/undrop-for-innodb/生成pages-sda5

cd /dist/src/undrop-for-innodb/

# 73G为df -h显示的sda5的总大小
# 以下指令本例大概会消耗20+小时
./stream_parser -f /dev/sda5 -s 1G -t 73G

将ibdata1分页

此操作不会影响ibdata1,会在/dist/src/undrop-for-innodb/生成pages-ibdata1

./stream_parser -f /data/database/mysql/ibdata1 

获取TABLE ID

21616为TABLE ID

./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql  | grep 'recover/actor'  
00000001E113    2F0000018C08A0  SYS_TABLES      "recover/actor" 21616   4       33      0       80      ""      21619

获取INDEX ID

其中36728和36729为NDEX ID

./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql  | grep '21616'
00000001E113    2F0000018C071D  SYS_INDEXES     21616   36728   "PRIMARY"       1       3       21619   4294967295
00000001E113    2F0000018C078F  SYS_INDEXES     21616   36729   "idx\_actor\_last\_name"        1       0       21619   4294967295

恢复数据

根据上面找到的INDEX ID找到对应的pages,sakila/actor.sql为建表SQL文件,好像只需要用到36728这个INDEX ID

从输出可以看到,数据找回了

./c_parser -6f pages-sda5/FIL_PAGE_INDEX/0000000000036728.page -t sakila/actor.sql
-- Page id: 3, Format: COMPACT, Records list: Valid, Expected records: (5 5)
00000001E106    A6000001D60110  actor   201     "zhang" "jian"  "2017-12-04 15:58:38"
00000001E107    A7000002D30110  actor   202     "zhan"  "jian"  "2017-12-04 15:58:38"
00000001E109    A9000002D50110  actor   203     "zha"   "jian"  "2017-12-04 15:58:38"
00000001E10B    AA000002D60110  actor   204     "zh"    "jian"  "2017-12-04 15:58:38"
00000001E10E    AC000002D80110  actor   205     "z"     "jian"  "2017-12-04 15:58:38"

5 其他

  • 大内存和读写性能好的磁盘将有效加快恢复进度

6 参考资料

MySQL · 数据恢复 · undrop-for-innodb

github-undrop-for-innodb

PostgreSQL简介

怎么用了PostgreSQL?

PostgreSQL,在国内应该是没有MySQL那么流行,但对它的介绍,通常是和Oracle比,我们也只在2个地方用了:

  1. Zabbix,一开始是MySQL,后来agent端出现很多unreachable,才用了PostgreSQL,但换数据库并没有解决问题,最后是采用了表分区才解决了
  2. 大数据,CDH这个Hadoop发行版,默认使用的是PostgreSQL 8.4(2009年发行),有几个数据库连接 ,长期占用CPU达50%,花了点时间去看,但最终也没有解决。而且版本太旧,目前搜到的资料,都不太适用

安装

官方提供了各系统,各版本的rpm包,直接yum或者rpm即可

rpm -ivh https://download.postgresql.org/pub/repos/yum/9.3/redhat/rhel-6-x86_64/pgdg-centos93-9.3-3.noarch.rpm
yum -y install libxslt uuid
yum -y install postgresql93-server postgresql93-contrib postgresql93-libs postgresql93

上面的安装可能会提示证书有问题,此时可以去看看yum仓库的地址,然后手动wget回来安装即可

grep baseurl /etc/yum.repos.d/pgdg-93-centos.repo 

初始化和连接

# 初始化数据,数据库文件目录在/var/lib/pgsql/9.3/data/
/etc/init.d/postgresql-9.3 initdb

# 启动
/etc/init.d/postgresql-9.3 start

连接

# 以系统用户连接
# 连接,切换到postgres,然后就可以直接连接了
su - postgres
psql
# 通过用户密码连接,只能通过环境变量指定密码
PGPASSWORD=xxx /usr/bin/psql -U zabbix
# 把连接信息写到文件
cat /root/.pgpass
localhost:5432:*:test_user:user_password
# 通过指定用户名即可连接 
psql -U test_user

认证

它和MySQL一样,也是基于用户和主机进行认证的,配置文件如下,就是和数据在一起

# 修改此文件
# 需要以root身份进行reload:/etc/init.d/postgresql-9.3  reload

/var/lib/pgsql/9.3/data/pg_hba.conf

文件内容

# "local" is for Unix domain socket connections only
# 类型 数据库             用户           地址          认证方式
local   all             all                        peer
# IPv4 local connections:
host    all             all       127.0.0.1/32   ident
host    replication     replica     192.168.8.198/32    md5

认证类型

local的话,就是不指定主机名或者指定主机名为localhost,它默认使用unix socket进行连接/tmp/.s.PGSQL.5432,5432会随着监听端口改变,例如改为7432,则socket文件名就会变成/tmp/.s.PGSQL.7432

其他的就是通过host:port这样的tcp方式访问

认证方式

数据库和用户没什么要说的,这里讲下认证方式

  • ident,就是用PostgreSQL所在机器的系统用户,例如上面提到的切换到postgres用户,当系统用户和PostgreSQL里的用户名一致,就可以连接
  • peer,和ident一样,只是它是在客户端系统进行识别
  • md5,就是密码认证
  • trust,只验证主机,不验证用户名和密码
  • password,这个没去了解

使用

以下指令为输入psql进入PostgreSQL控制台输入

\?:
    打印PostgreSQL帮助
\h:
    打印SQL帮助
\c   :  
    可以通过\c databasename  切换数据库
\l    :
    数据库列表的详细信息
\q   :
    退出数据库
\d或\dt:
      该数据库下所有表的详细信息
\d  tablename:
      该表的表结构
\df 
    打印函数
\df+ partition_every_day
    打印partition_every_day的信息
\du
    列出所有用户

alter user postgres with password 'xxx'
    修改用户名密码 
      

以下指令在系统终端执行

# 创建数据库
createdb test_db

# 备份数据库,dumpall为备份所有库
pg_dump/pg_dumpall

# 备份结构
pg_dump --schema-only

# 执行sql文件

psql -f xxx.sql

schema

目前我无法说清楚这是什么东西,大概理解为是一种隔离机制吧,默认只有$userpublic,如果你的表有分区,那么你虽然在打印的时候可以看到,但你要drop分区的时候,则会提示找不到

# 显示Schema的搜索路径
SHOW search_path;

# 从上面的输出可以看出,默认没有partitions,我们加入,如果不加入,在drop table的时候,就会提示does not exist
SET search_path TO "$user",partitions,public;

# 可以drop了,CASCADE的作用是把关联的触发条件删除,这触发条件,其实就是什么数据应该插入此分区的,现在已经是2017了,自然不会有2015的数据需要插入,所以可以放心删除
drop table history_uint_2015_01_11 CASCADE;

# 当然也可以不用设置,直接加partitions前缀

drop table partitions.history_uint_2015_01_11 CASCADE;

性能

  • shared_buffers,缓存最近查询的数据页,设置为系统内存的25%,但不应该超过8G
  • effective_cache_size,单次查询可用缓存,如果是专门的PostgreSQL服务器,设置为系统总内存一半
  • work_mem,用于排序、表扫描、哈希关联等操作的最大内存量,如果是多用户并发进行简单查询,那么此设置很小也没有问题,如果执行复杂查询,可以调大
  • maintenance_work_mem,用于vaccum(回收内存和磁盘空间)操作的内存总量。不应该大于1G。

一些工具

pgAdmin

是PostgreSQL官方的图形界面管理客户端

  • 版本3可以管理PostgreSQL 8.4
  • 版本4可以管理PostgreSQL 9.x

pgsniff

从网络流量中提取SQL,测试支持9.3版本,8.4版本不支持

# 最后一个-,意思是输出到终端
pgsniff -d eth1 -l -

还有一个叫pgshark,没测试

书籍

  • PostgreSQL 9 Admin Cookbook,有中文版
  • PostgreSQL即学即用
  • PostgreSQL 9.0性能调校,一定不要看中文版

通过XFW访问MySQL无返回的问题

1. 症状概述

最近我们新加了一台机,在xx的机器上访问MySQL,可以正常连上,执行show databases等马上有结果的指令,可以正常返回,但如果表记录又上千万,select count(1) from xxx的时候,则会没有返回。

犹记得几个月前,我们在C机有个脚本通过http方式提交数据到远程机器S,S将结果存到MySQL,同样也是经过XFW,由于处理时间比较长(大概1分钟吧),C这边一直卡住, 因为没有收到S的返回,但在S通过tcpdump抓包发现,S是有返回的,而且尝试多次返回,说明什么?连接断了呗,但C和S都感知不到这个断开的操作,感觉是XFW检测到此数据连接没有数据发送,把它断了。

最后是在S上加了索引,使得存入MySQL操作秒返回,才解决了这个问题。天下武功,唯快不破,和XFW斗亦如是。

2. 排查过程

2.1 MySQL版本

首先说明下本次新加的机器,我们升级MySQL到5.7,以往的机器都是5.5,那是不是5.7的问题呢?

2.1.1 SUSE系统

以下都是在C(SUSE系统)上连接:

1.连MySQL 5.7(A)时,实际早就挂了(服务器端top看MySQL的CPU占用),但客户端这边还是阻塞状态,过了很久才显示:

ERROR 2013 (HY000): Lost connection to MySQL server during query

2.连MySQL 5.5时(B),偶尔会卡住,在服务器端抓包,没有数据过来,意味着实际上还没有连到过去,但如果能连过去,一般可以获取到返回

但我们后来通过tcpdump抓包发现,第一点的结论其实是错的,因为在MySQL的CPU占用降下去之后,从3306端口是有数据发出去的,也就是并没有挂,查询有结果,并有发出去,但客户端没有收到,C端等到超时了才退出并报错

2.1.2 CentOS系统

刚好有1台CentOS系统的机器,测试下,结果和上面SUSE系统一样

2.2 TCP的问题

2.2.1 SUSE系统

基于上面一开始提到的http提交数据的问题, 我们又感觉是tcp的问题,怎么验证?我们在A上开了ssh的密码登录,然后在C上ssh到A,然后A上执行mysql连接,再进行查询,结果发现,也是没有返回,最后提示

Disconnected; connection lost (Connection closed.).

那很明显了,就是tcp连接断开了

2.2.2 CentOS系统

上网查询发现CentOS上,ssh有一个维持心跳的参数,于是试了下

# 5s发送一次心跳包
ssh -o ServerAliveInterval=5 -pxxx username@A

通过上面的参数连接后,发现此时可以正常获取结果了

记得关闭A上的ssh密码登录

3. 解决

3.1 libkeepalive.so

通过以上测试,发现问题出现在tcp,那就有解决的方向了,但MySQL是没有这种维持心跳的参数的,但我们找到了一个libkeepalive.so的软件,通过它封装下,可以设置应用层的心跳,那就不用设置内核参数了

LD_PRELOAD=libkeepalive.so KEEPIDLE=5 KEEPCNT=3 KEEPINTVL=5 mysql  -h xxx
  • KEEPIDLE,空闲多久就开始发送心跳,此处为5
  • KEEPINTVL,多久发送一次心跳,此处为5
  • KEEPCNT,一共尝试多少次,此处为3次,如果3次都发送不成功,则认为失败,不再尝试

但比较遗憾, 没有生效,无论是SUSE还是CentOS

3.2 内核参数

我们在C端把内核参数设置下,感觉是比较重(需要修改内核参数)的解决办法。

# 多久探测一次,此处为5s
sysctl -w net.ipv4.tcp_keepalive_intvl=5
# 空闲多久后开始探测,此处为5s
sysctl -w net.ipv4.tcp_keepalive_time=5

通过上述设置,再直接用mysql连接就正常了。

以下是系统默认值,net.ipv4.tcp_keepalive_probes的意思是,每「net.ipv4.tcp_keepalive_time 」间隔,就探测一次,探测「net.ipv4.tcp_keepalive_probes」次,此处为9次,就停止探测

net.ipv4.tcp_keepalive_time = 30
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 5

为避免影响其他程序,建议只在测试时通过命令行设置,在测试完后,通过sysctl -p还原为系统默认配置

其他

ZZ:拷贝ibd文件的方式迁移数据

在mysql5.5之前,mysql实例中innodb引擎表的迁移是个头疼的问题,要么使用mysqldump导出,要么使用物理备份的方法,但是在mysql5.6之后的版本中,可以使用一个新特性,方便地迁移Innodb表,这样一来大的innodb表的迁移就显得很easy,这个特性就是innodb表空间传输:

详细请参考:Innodb 表空间传输迁移数据

zz:InnoDB个性化备份

来自云栖:InnoDB个性化备份
主要看下面,在slave上执行 stop slave,等一会,就可以通过cp目录的方式备份,好犀利

2018年十一月
« 2月    
 1234
567891011
12131415161718
19202122232425
2627282930