标签归档:DB

MySQL 5.6 如何给ibdata1瘦身

前不久刚给ibdata1瘦身,发篇文章总结下。

ibdata1是MySQL使用InnoDB引擎时所产生的文件,其一般存储数据、索引、结构、缓冲数据、共享数据和重做日志等。因为ibdata1只增不减,长期操作数据库,可能会使其越来越大,而浪费空间。

加上使用InnoDB引擎时,没有添加innodb_file_per_table参数也是导致ibdata1过大的原因。

但InnoDB只增不减,也导致给ibdata1瘦身是件比较麻烦的事。

最大的我见过的是40多G的ibdata1文件,实际数据库差不多是20多G,在做了优化后,ibdata1缩小至20多G,所以说减肥还是有必要的。

首先,先略微说下innodb_file_per_table参数,使用该参数可使得InnoDB引擎转变为独立表空间模式(默认为共享表空间),也就是每个数据库的每个表都会生成一个数据空间,就像MyISAM引擎一样。

 
独立表空间优点就是每个表有独立空间,数据和索引都会存在自已的表空间中,可以实现单表在不同的数据库中移动。重要的是空间可以回收,而且不管日常怎么操作,表空间的碎片不会太严重的影响性能,优化表的速度也快,表文件出现问题不会大动干戈,只要修复对应表即可。缺点是单表占用的空间比共享表空间方式稍大,共享表空间在Insert操作上有一些优势。

所以没增加innodb_file_per_table参数的同学,建议还是加上吧。因为增加innodb_file_per_table参数,与我们后续给ibdata1瘦身并无冲突,而且对以后也只有好处。

说了这么多废话,言归正传。谈谈怎么给ibdata1瘦身,唯一的方法是就是备份整个数据库,然后删掉ibdata1和ib_logfile*,再恢复数据库,以此达到瘦身目的。当然了,操作数据库肯定是有风险的,而且也需要生产环境允许MySQL暂停写或访问。

简单的总结就是以下这几点:

1.在/etc/my.cnf中添加“innodb_force_recovery=4”使InnoDB成为只读表,这其实应该说是第一个坑。另外确定“innodb_data_file_path”参数限定的初始ibdata1大小在合理范围,一般稍大于现有数据大小。
2.启动MySQL,使用我给的工具备份除了mysql、information_schema和performance_schema的整个数据库。为以后顺利恢复数据做准备。
3.删除除了mysql、information_schema和performance_schema的整个数据库,这3个排除在外的其实也删不掉。
4.停止MySQL,删除ibdata1和ib_logfile*文件。
5.删除数据库目录中的mysql目录的innodb_index_stats.*、innodb_table_stats.*、slave_master_info.*、slave_relay_log_info.*、slave_worker_info.*文件,这5个是InnoDB的基础表(状态表),这是MySQL 5.6的坑,删除ibdata1后不会自动重建这5个表,而且如果不删除这些旧文件,还不可创建或重建新的。
6.在/etc/my.cnf把“innodb_force_recovery=4”去除。
7.启动MySQL,将第2步备份的数据库还原,然后用从MySQL 5.6导出来的InnoDB基础表备份还原回去重建(第5步删掉的)。
8.搞定,重启下MySQL,确保没有错误,没有异常。

必要的工具,在文末的Github地址下载。

下面详细说说每个步骤,首先是设置InnoDB为只读表,这还是比较必要的。可确保你的数据完整性、安全性。为何是坑呢,因为我遇到过没这样设置,导致后续恢复备份时,数据有异常。

具体操作,在/etc/my.cnf中添加“innodb_force_recovery=4”


[mysqld]

innodb_force_recovery = 4

如此便可,innodb_file_per_table也是在[mysqld]下添加。

innodb_force_recovery的值可以设置为1-6,大的数字包含其前面所有数字的影响。

1. (SRV_FORCE_IGNORE_CORRUPT):忽略检查到的corrupt页。
2. (SRV_FORCE_NO_BACKGROUND):阻止主线程的运行,如主线程需要执行full purge操作,会导致crash。
3. (SRV_FORCE_NO_TRX_UNDO):不执行事务回滚操作。
4. (SRV_FORCE_NO_IBUF_MERGE):不执行插入缓冲的合并操作。
5. (SRV_FORCE_NO_UNDO_LOG_SCAN):不查看重做日志,InnoDB存储引擎会将未提交的事务视为已提交。
6. (SRV_FORCE_NO_LOG_REDO):不执行前滚的操作。

接着呢,启动MySQL,备份整个数据库,一般我们会用

mysqldump –lock-all-tables –all-databases > all-dbs.sql
来完整备份数据库。但这样的话,就包含了mysql、information_schema和performance_schema这3个数据库,而在后续还原数据库,带着这3个数据库可能会出错(我有几次遇到过)。

所以我提供了shell脚本工具mysql_dump_all_db.sh,因为怕大家不像我是免密码操作MySQL的,在使用工具前,请先修改脚本中的MySQL帐号和密码,然后再通过

sh mysql_dump_all_db.sh
执行备份操作,备份好的文件,会存放在脚本运行所在目录。

这个工具默认排除mysql、information_schema和performance_schema这3个数据库,如果你有其他想排除的,可以直接修改脚本,增加其他想要排除的数据库。

完了之后呢,删除所有数据库,可以通过phpMyadmin或直接在shell操作MySQL删除,在shell下删除,可以在/tmp/DatabasesToDump.txt查看到所有数据库,由“mysql_dump_all_db.sh”生成。我一般在phpMyadmin删除,简单,不怕错。如你所见,mysql、information_schema和performance_schema这3个数据库是删不掉的,所以要排除,免得麻烦。

 
搞定后,就可以停止MySQL了。删除数据库目录的ibdata1和ib_logfile*文件,一般是在/usr/local/mysql/data,看你怎么配置的了。

接着,在该目录下的mysql目录(/usr/local/mysql/data/mysql)中,把innodb_index_stats.*、innodb_table_stats.*、slave_master_info.*、slave_relay_log_info.*、slave_worker_info.*共计10个文件删除,这些文件已经无用了,而且占着茅坑不拉屎。MySQL 5.6很煞笔的不会重建这5个表,如果你不删除他们,待会将无法重建或恢复这5个表,接着log一直在报错,死循环。所以要把这5个表的10个文件干掉。所以这个是个坑。

在/etc/my.cnf把“innodb_force_recovery=4”去除后,就可以启动MySQL了,这时候ibdata1和ib_logfile*文件会重建。噢,上帝,胜利在望,不要激动,让我们继续吧。

把刚才备份的所有数据库还原,用从MySQL 5.6导出来的InnoDB基础表备份还原回去重建。

我在后面的Github地址有提供,从全新 MySQL 5.6导出的,名字为“mysql_innodb_basic_tables.sql”的备份文件,通过它可以重建刚才删掉的5个InnoDB基础表。

还原数据库文件非常简单,不过我还是略微写下,照顾下小白,在shell下:

mysql < all-dbs.sql
mysql < mysql_innodb_basic_tables.sql
这样就OK了,如没有免密码操作权限,请自行添加-u和-p参数。

好的,做完这些,重启下MySQL,确保没有错误即可。这样就完成了对ibdata1的瘦身。

以上操作环境为:CentOS 6.6 x64、MySQL 5.6.25。

工具存放在Github中(原谅我的渣英文),见: https://github.com/kn007/Reduce-Shrink-Purge-the-ibdata1-file-in-MySQL

写这篇文章就是为了大家少走一点弯路,也把自己遇到的坑说一下。算是个总结,也是给后人的一些经验。原则上不提供技术支持,有问题请自行解决。另外毕竟是数据库,瘦身有风险,操作需谨慎。

公司开发定的数据库MYSQL规范

我们公司相当多的项目用的是mysql数据库,但是大家在开发过程中对mysql的认识问题,往往在数据库设计时对字段的定义不一致,在开发时对sql语句的执行出现问题,特地把一些通用性的、值得注意的问题做一下总结
一、数据库的设计规范
1、必须使用InnoDB存储引擎
原因:支持事务安全、行级锁、并发性能更好(查询不加锁,完全不影响查询),内存缓存页优化使得资源利用率更高,mysql5.6版本开始支持 全文索引
2、必须使用utf-8的字符编码
原因:这个无需过多解释,和网站以及其他系统完全统一,避免转码带来不必要的麻烦,而且系统数据接口都是使用json格式。
3、数据库、表、字段名必须有意义并且必须加入中文注释
原因:避免自己遗忘,方便他人进行开发,要不然一段时间之后谁还知道这是用来干什么的
4、禁止使用存储过程、视图、触发器
原因:高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引,计算还是使用程序来实现。使用存储过程等非常难于进行调试和测试。
5、禁止存储文件和图片
原因:存储路径在速度和空间方面会有更好的提升
6、数据库中表的数量不能高于500
原因:做好前期设计,尽量把一些相关度低的表进行分库处理
7、库名、表名、字段名的命名规则
所有的名字都使用小写并且间隔使用下划线风格,不超过32个字符,必须要见名知意,尽量使用英文,但是绝对禁止拼音英文混用命名。
 二、表的设计规则
8、表中的字段数不能超过30
原因:如果字段过多,就要把一些不常用的字段进行分表处理
9、表明和索引名统一
例如:表名table_xxx,非唯一索引名index_xxx,唯一索引名unique_xxx
10、所有表必须至少有一个主键,例如自增主键
原因:
a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率
c) 无主键的表删除,在row模式的主从架构,会导致备库夯住
11、禁止使用外键,如果要有外键完整性约束,必须使用程序进行控制
原因:外键会导致表之间耦合度增加,update与delete操作都会涉及相关联的表,非常影响sql 的性能,甚至会造成死锁。高并发情况下非常影响数据库性能,大数据高并发业务场景数据库使用以性能优先
三、字段的设计规范
12、所有字段都要定义为NOT NULL并提供默认值
原因:
1)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
2)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多
3)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识
4)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!=’shenjian’,如果存在name为null值的记录,查询结果就不会包含name为null值的记录
13、在多字段的表中禁止使用TEXT、BLOB类型
原因:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能
14、使用整数禁止使用小数存储货币
原因:价格乘以100来使用整数存储,小数在运算过程中会导致钱对不上
15、手机号必须使用varchar(20)进行存储
原因:
1)涉及到国家代号,可能出现类似+86
2)手机号会去做数学运算么?不会,所以不要使用int(11)
3)varchar可以支持模糊查询,例如:like“138%”
16、禁止使用ENUM,可使用TINYINT代替
原因:
1)增加新的ENUM值要做DDL操作
2)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?
四、索引的设计规范
17、表中索引的数量最好控制在5个以内
原因:
1)、索引也占用很大的空间
2)、索引在创建修改数据的情况需要大量更新索引
18、一个索引关联的字段在5个以内
原因:字段超过5个时,实际已经起不到有效过滤数据的作用了
19、禁止在更新十分频繁、或者区分度不高的属性上建立索引
原因:
1)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
2)“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似
20、建立组合索引,必须把区分度高的字段放在前面
解读:能够更加有效的过滤数据
五、sql优化
21、禁止使用SELECT *,只获取必要的字段,需要显示说明列属性
原因:
1)读取不需要的列会增加CPU、IO、NET消耗
2)不能有效的利用覆盖索引
3)使用SELECT *容易在增加或者删除字段后出现程序BUG
22、禁止使用INSERT INTO t_xxx VALUES(yyy),必须显示指定插入的列属性
原因:容易在增加或者删除字段后出现程序BUG
23、禁止使用属性隐式转换
原因:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引,
where 条件语句里,字段属性和赋给的条件,当数据类型不一样,这时候是没法直接比较的,需要进行一致转换,这种情况是无法使用索引的。
24、禁止在WHERE条件的属性上使用函数或者表达式
原因:SELECT uid FROM t_user WHERE from_unixtime(day)>=’2017-02-15′ 会导致全表扫描,而不能使用索引
正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15 00:00:00’)
25、禁止负向查询,以及%开头的模糊查询
解读:
a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描,而不使用索引
b)%开头的模糊查询,同样会导致全表扫描,不能使用索引
26、禁止在大表中使用JOIN查询,禁止大表使用子查询
解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能
27、禁止使用OR条件,都改为IN查询
原因:旧版本Mysql的OR查询是不能命中索引的,即使新版能命中索引,为何要让数据库耗费更多的CPU呢?

28、应用程序必须捕获SQL异常的功能,并有相应处理

http://www.architecy.com/archives/456