关于吴 炳锡

数据库架构师 熟悉MySQL架构设计及数据库架构优化。 丰富的MySQL优化及高可用架构经验。

慢日志按时间截取

 

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

今天处理一个case: 数据库异常,连接数突增。
想着分析一下慢日志,可是一看慢日志都好几G了,而且是短日志格式,找到那个时间点相对比较难。于是写了一个脚本从慢日志按时间提取点日志。脚本:
https://github.com/wubx/mysql-binlog-statistic/blob/master/bin/cutlogbytime

使用方法:
cutlogbytime
#用于从慢日志用截取一个时间段的日志方便分析
./cutlogbytime /path/slowlogfile starttime endtime  > dstfile.log
时间需要写时戳

mysql> select unix_timestamp('2013-04-05');
+------------------------------+
| unix_timestamp('2013-04-05') |
+------------------------------+
| 1365091200 |
+------------------------------+
1 row in set (0.00 sec)

mysql> select unix_timestamp('2013-04-06');
+------------------------------+
| unix_timestamp('2013-04-06') |
+------------------------------+
| 1365177600 |
+------------------------------+
1 row in set (0.00 sec)
./cutlogbytime /path/slowlogfile 1365091200 1365177600 > 20130405_slow.log

这样得到一天的日志文件就小一点的也容易分析了。而且可以根据天的文件,在取一天的某个时间点的日志。

 

Innodb IO优二 — 数据库表设计

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

数据库表设计这块学问比较多,我这里单从互联网角度出发同时结合Innodb的特性给出一些设计方法供大家参考。本文构建大概分两分部分:Innodb的特性及设计中如何利用这种特性。

Innodb特性:

  • Innodb是索引聚集表, 存储结构是BTREE
  • Innodb的表的数据存储是有顺序的,默认是以主建排序,主建即是数据本身,不单独存放。
  • 如果没有主建,Innodb以第一个唯一索引排序,如果连唯一索引也没,Innodb内部会产生一个6字节的字段排序(这个也是性能杀手,所以对这块如果不想花太多时间去想这个事,就添加一个自增的列无业务意义做为主建即可)
  • Innodb表普通索引存储需要包含主建(或是Innodb表聚集的字段)

如何利用这些特性:

  • 对于Insert操作要求比高的表:  建议增加一个自增的列做主建,这样减少数据的写入造成Innodb表在存储上不断的拆叶排序的操作。 通过添加一个自增的列做主建从而达到Innodb表的写入都是顺序IO的形态。所以这种情况,保证在1W/S左右的Insert也是比较容易的。添加一个主建还有另外一个好处,真正的条件将会成为唯一索引或是普通索引, 这样索引单独存放起来后,整体上比原来的表文件会小很多,这样基于条件的查询,可以从一个较少的文件快速定位到需要的行。这样也有机会利用到索引覆盖。
  • 另一种场景,写入少,同时每次读多(读取不是一条记录),这种场景可以考虑不要使用自增的列做为主建,就使用查询条件或是查询条和其它通达到唯一,定义为主建。这样查询就可以一次读到数据。还一种场景如存好友关系,或是股票信息,特别好友关系表类的数据,可以考虑使用两个用户的Id做联合主建,查询时条件中只用自已的Id读所有用户的数据,这样就是一个顺序IO的请求。同样这种情景下对一些数据就可以考虑冗余,减少请求反向的数据操作。
  • 更新最好基于主建或是唯一索引来做,这样才能有机会利用Innodb的行级锁。
  • 互联网中还有一种观点是:页面展现什么,就存成什么样的表。这种在CMS中还能适用,在WEB2.0及相关的应用这种观念就行不通。但可以考虑适当的多处写,实现数据的快速读取及索引表的引入。

从业务形态上来看:

  建议数据在设计阶段就要考虑那些是大表,可以分为多少个业务,怎么能核心功能拆分。这块举个例子:大家经常听到的淘宝的用户库,商品库,收藏夹库,交易库,评论库等。及其它我们经常也能听到的:认证库,好友关系库等等。到业务形态上后,可以根据不同的形态选择不同的软件来做不要只看到mysql了,这块的设计可以把nosql类的东西也要考虑进来,最终设计数据库的模型。

数据库表的设计这块有可能都是各有建解,欢迎有不同意见的同学一块来讨论mail我的邮箱即可。

Innodb IO优化-配置优化

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

对于数据库来讲大多瓶颈都出现在IO问题上,所以现在SSD类的设备也才能大行其道。那数据库的IO这块有什么可以优化的吗? 我这里大致谈一下我的看法,希望能达到一个抛砖引玉的效果。
这里谈一下数据库本身的配置方面
具体如下:
配置方面对于IO优化的原则:尽可能能缓存,减少读对数据库的随机IO的请求;同时减少写的随机IO的随时发生,利用各种buffer去缓存。下面来看一下这块的参数:

  • innodb_buffer_pool_size : 这是Innodb最重要的一个配置参数,这个参数控制Innodb本身的缓大小,也影响到,多少数据能在缓存中。建议该参数的配置在物理内存的70%-80%之间。
  • innodb_flush_method: 这个控制Innodb的IO形为,什么:fsync, O_DSYNC之类的,这里不做过多介绍, 建议使用: O_DIRECT, 这样减少操作系统级别VFS的缓存使用内存过多和Innodb本身的buffer的缓存冲突,同时也算是给操作系统减少点压力。
  • innodb_io_capacity: 这个参数据控制Innodb checkpoint时的IO能力,一般可以按一块SAS 15000转的磁盘200个计算,6块盘的SAS做的Raid10这个值可以配到600即可。如果是普通的SATA一块盘只能按100算。(innodb-plugin, Percona有这个参数)
  • innodb_max_dirty_pages_pct : 这个参数据控制脏页的比例如果是innodb_plugin或是MySQL5.5以上的版本,建议这个参数可以设制到75%-90%都行。如果是大量写入,而且写入的数据不是太活跃,可以考虑把这个值设的低一点。 如果写入或是更新的数据也就是热数据就可以考虑把这个值设为:95%
  • innodb_log_file_size : 这个可以配置256M以上,建议有两个以前的日志文件(innodb_log_files_in_group). 如果对系统非常大写的情况下,也可以考虑用这个参数提高一下性能,把文件设的大一点,减少checkpiont的发生。 最大可以设制成:innodb_log_files_in_group * innodb_log_file_size < 512G(percona, MySQL 5.6) 建议设制成: 256M -> innodb_buffer_pool_size/innodb_log_file_in_group 即可。
  • innodb_log_buffer_size : 如果没在大事务,控制在8M-16M即可。

其它对IO有影响的参数(以5.6为准)

  • innodb_adaptive_flushing 默认即可
  • innodb_change_buffer_max_size 如果是日值类服务,可以考虑把这个增值调到 50
  • innodb_change_buffering 默认即可
  • innodb_flush_neighors 默认是开的, 这个一定要开着,充分利用顺序IO去写数据。
  • innodb_lru_scan_depth: 默认即可 这个参数比较专业。
  • innodb_max_purge_lag 默认没启用,如果写入和读取都量大,可以保证读取优先,可以考虑使用这个功能。
  • innodb_random_read_ahead 默认没开启,属于一个比较活跃的参数,如果要用一定要多测试一下。 对用passport类应用可以考虑使用
  • innodb_read_ahead_threshold 默认开启:56 预读机制可以根据业务处理,如果是passprot可以考虑关闭。如果使用innodb_random_read_ahead,建议关闭这个功能
  • innodb_read_io_threads 默认为:4 可以考虑8
  • innodb_write_io_threads 默认为:4 可以考虑8
  • sync_binlog 默认即可: 0
  • innodb_rollback_segments 默认即可: 128

另外5.6的undo log也可以独立配置了,建议单独配置出来。

内存表在同步环境注意事项

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

在一些场景想利用MySQL的内存表存一些数据来加快数据的操作。但如果在复制环境中这可不是一个好事情。
主要原因如下:
1. 内存表在数据库重启或是异常down机的情况下内存表的数据会全部丢失。如果从库重启一下则同步就不能进行了。
2. 另一方面,在主从环境下,如果从库上同时有大的操作或是排序工作,有借助于临时表的的场景,同时主库上内存表也有较大的写入,从库有可能会就出现报那个内存表is full (1114)这样的错误。
3. 特别需要注意在使用内存表的场景主库重启会主动发起一次对内存表的truncate table操作

那如何解决呢:
1. 从我对数据库的了解上来看推荐用Innodb表去替代memory表, Innodb表如果一个表的数据经常被访问,就会被加载到内存里,数据和索引都在内存,访问速度是比较快的。
2. 不要复制Innodb表。 利用replication-igore-table=db.tbaname形式声明不同步那个表。 这种情况下如果在statement级别的复制,禁止出现insert into otbname select c1 from tbname这种语句。
3.把监表单独放到一个实例下,和主要业务数据分开。

Ubuntu工作机使用FlashCache技术加速

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

Flashcache是facebook的一个开源项目,用于数据库加速中.大致结构,在传统磁盘前面放置一个ssd装置,充当缓存,把热的数据保持在缓存中,写的过程也是在先写ssd然后由ssd同步到传统磁盘.真正的数据最终还是保持在传统磁盘中.这样ssd杯掉也不用担心数据丢失.同时又可以有大容量,高性能的体验.

现在ssd也挺便宜的,新一点的笔记本里都有ssd磁盘.那么这里简介绍一下,怎么用ssd加速home及其它分区,引导这块不涉及.缺点: ssd只是用来做cache不能存入实质的东西.

前提:
ssd有空余的分区或是有个ssd盘不使用.操作系统是ubuntu或是其它linux

大致步骤:
1. 安装git

$sudo apt-get install git-core

2. 下载flashcache源码:

#git clone https://github.com/facebook/flashcache.git

3. 进入目录编辑

#make
#make install

//如果失败看提示,一般是内核源码没安装的原因
4. 加载flashcache模块

#modprobe flashcache

确定加载完成:

#dmesg |tail
......flashcache: flashcache-1.0 initialized

表示成功
另外还可以:

lsmod |grep flashcache

看到相应的信息即成功.

为了开机能加载这个模块,需要这个模块名写入/etc/modules中

#echo "flashcache" >> /etc/modules

好这里完成了基本环境的安装.下面以/home分区加速设制为,其它一样.
我的ssd分了/dev/sda1 用于/ , /dev/sda2用于了swap 还有/dev/sda3 ,/dev/sda4分区出没使用.我在传统磁盘里划出来100G /dev/sdb1用于/home目前还没挂载.
5. 格式化/dev/sdb1 如果原来/home节点已经划分有分区, 只用umount掉即可,然后在fstab去掉自动挂载(flashcache目前不能自动挂载)
#mkfs.ext4 /dev/sdb1
把原来/home的数据复制或是移到动/dev/sdb1上.
#mount /dev/sdb1 /mnt
#cp -r /home/* /mnt
#umount /mnt
需在注意cp过去后权限是不是和原来的一样.

6. 初始化flashcache
#ls -la /dev/disk/by-uuid/
找到/dev/sdb1 对应的uuid

#flashcache_create -v -p back home_flashcache /dev/sda3 /dev/disk/by-uuid/4b75b04e-5930-4042-89b8-40894067dac6

这样即创建完毕.
7. 挂载那个flash设备

#mount /dev/mapper/home_flashcache /home

即可以体验flashcache加载的东西了.
8. 收尾工作,开机自动挂载flashcache加速的设备,利用rc.local完成
在rc.local里添加:

flashcache_load /dev/sda3
mount /dev/mapper/home_flashcache /home

这样即可开始挂载/home节点了.

如果不想使用flashcache技术,可以通过如下命令删除该功能:
umount /home
dmsetup remove cachedev
flashcache_destory
mount 相应的节点即可

说明: 以# 开头的操作表示以root权限进行的操作,以$开头的表始以普通用做的操作.

尝试mysqlbinlog的flashback功能

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

简介:
mysqlbinlog flashback功能是淘宝彭立勋(http://www.penglixun.com/)的一个很强劲的作品.
主要功能: 对rows格式的binlog可以进行逆向操作.delete反向生成insert, update生成反向的update,insert反向生成delete.让dba同学们也有机会简单的恢复数据.可恢复:insert, update,delete相关的操作

演示一下使用过程:

生成带有flashback mysqlbinlog 工具:

项止主页:http://mysql.taobao.org/index.php/Patch_source_code#Add_flashback_feature_for_mysqlbinlog

准备好MySQL-5.5.18的源码,这里用的Percona-MySQL-5.5.18源码

cd mysql-5.5.18
wget http://mysql.taobao.org/images/0/0f/5.5.18_flashback.diff
patch -p0<5.5.18_flashback.diff

即可以看到了mysqlbinlog , 因这里只为要mysqlbinlog这个程序,所以编译MySQL时没加特别的参数.该工具是否具备flashback功能可以确认一下是否有 "-B" 这个参数.

开始实验

mysql test
mysql> select count(*) from pic_url;
+----------+
| count(*) |
+----------+
| 786476 |
+----------+
1 row in set (0.11 sec)
mysql>delete from pic_url;
Query OK, 786476 rows affected (22.81 sec)
mysql>select count(*) from pic_url;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.03 sec)

mysql>show binary logs;
...
| mysql-bin.000011 | 195001447 |
| mysql-bin.000012 | 106 |
+------------------+-----------+

Tips:
定位日值,一般看当前的log如果当前的log文件比较小,那么就是上一个文件至于为什么,这里就不讲了.也可以用mysqlbinlog 去实际查看确认一下.

接下来就是要找到这个delete在log中position的变化区间了.这个没什么技巧,通常使用:

./mysqlbinlog -v --base64-output=decode-rows  /u1/mysql/logs/mysql-bin.000011 >11.sql

然后对11.sql文件进行搜索了表名,找到相应的节点.大表删除通常最后的stop点都在文件最后.找到节点后就可以:

./mysqlbinlog -v --base64-output=decode-rows -B --start-position=377 --stop-position=195001377 /u1/mysql/logs/mysql-bin.000011>B11.sql

同样对B11.sql这个文件验证一下.看看结尾是和预期一样.验证OK后就可以:

./mysqlbinlog -B --start-position=377 --stop-position=195001377 /u1/mysql/logs/mysql-bin.000011|mysql test

如果表比较大,则执行着比较慢.如不出错请耐心等待.执行完毕后:

mysql>select count(*) from pic_url;
+----------+
| count(*) |
+----------+
| 786476 |
+----------+
1 row in set (0.11 sec)

数据又恢复了.

注意:
为防止恢复报错需要把:max_allowed_packet 改到最大值1G;
mysql>set global max_allowed_packet=1024*1024*1024;

#max_allow_packet大小不够时报错如下:
ERROR 1153 (08S01) at line 403133: Got a packet bigger than 'max_allowed_packet' bytes

恢复操作有风险,请在备库操作或是在经验丰富的同学指导下进行.

二进制文件上传不到服务器上,传到github上一个二进制文件:https://github.com/wubx/mysql-binlog-statistic/tree/master/bin  在64的位的linux系统编译的. 有需要的直接下载.

从库无业务延迟严重排查

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

今天给一个客户巡检的情况下发从库没有业务的情况mysqld的cpu的一个core占用100%.查主库慢查询也没有关于写的SQL.
可以说是典的单进程复制把一个cpu占满造成的.知道原因了,就好分析了.
分析一下binlog中写的什么,看看有什么地方可以优化或是加速的.利用工具:pasrebinlog
利用show slave status\G; 查当前同步的到节点,然后对日值进行解析.

git clone https://github.com/wubx/mysql-binlog-statistic.git
cd mysql-binlog-statistic/bin/
parsebinlog /u1/mysql/logs/mysql-bin.000806

...
====================================
Table xx_db.xxtable:
Type DELETE opt: 101246
Type INSERT opt: 103265
================================
...

以最大的数排序看, 定位到: xx_db.xxtable,对于一个日值中能删除10几万,写入10几万.是不是这个表写入比较慢了呢.
在从库上查看innodb的相关情况:

MySQL> show engine innodb status\G;
...
---TRANSACTION 1C0C2DFDF, ACTIVE 3 sec fetching rows
mysql tables in use 1, locked 1
3361 lock struct(s), heap size 407992, 477888 row lock(s), undo log entries 42
MySQL thread id 43, OS thread handle 0x7fc1800c4700, query id 1908504 Reading event from the relay log
TABLE LOCK table xx_db.xxtable trx id 1C0C2DFDF lock mode IX
RECORD LOCKS space id 1002 page no 1975 n bits 1120 index `AK_movieid` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6965 n bits 264 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6967 n bits 256 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6973 n bits 264 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6982 n bits 256 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6983 n bits 256 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6987 n bits 256 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 6999 n bits 256 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
RECORD LOCKS space id 1002 page no 7000 n bits 256 index `GEN_CLUST_INDEX` of table xx_db.xxtable trx id 1C0C2DFDF lock_mode X locks rec but not gap
TOO MANY LOCKS PRINTED FOR THIS TRX: SUPPRESSING FURTHER PRINTS
----------------------------
END OF INNODB MONITOR OUTPUT

...

从Innodb 的monitor output 中也可看到 xx_db.xxtable 这表已经是表级表了,造成并发比较低,而且有大量的: GEN_CLUST_INDEX 而且属于一个事务.  GEN_CLUST_INDEX表示没有主建,内部产生一个主建,对于内部产生的主建很很容易造成page拆分的操作.

问题到这里基本上可以得到解决问题的方法了:
给xx_db.xxtable 添加一个主建即可.这里后是给xx_db.xxtable 添加了一个无业务意义的id int 自增主建.这样立马可以看到mysqld占用的cpu单核降到了3%左右, 同时后续同步一切正常,观查一天没出现同步延迟的问题.

MySQL数据库负载很高连接数很多怎么处理

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

在MySQL数据库连接数很多,而且大多属于活跃的状态时MySQL机器基本上负载很高,属于基本上快要死去的状态了.
这时怎么办呢?

有可能两个办法.
第一先限制Innodb的并发处理.如果innodb_thread_concurrency = 0 可以先改成 16或是64 看机器压力,如果
非常大,先改成16让机器的压力下来,然后慢慢增达,适应自已的业务.
处理方法: set global innodb_thread_concurrency=16;

第二: 对于连接数已经超过600或是更多的情况,可以考虑适当的限制一下连接数,让前端报一下错,也别让DB挂了.
DB在了,总是可以用来加载一下数据,当数据加载到了nosql里了,慢慢的DB压力也会降下来的.
限制单用户连接数在500以下. 如:
set global max_user_connections=500;

(MySQL随着连接数的增加性能会是下降的,这也是thread_pool出现的原因)
另外对于有的监控程序会读取information_schema下面的表的程序可以考虑关闭下面的参数
innodb_stats_on_metadata=0
set global innodb_stats_on_metadata=0;

这个参数主要防止对读取information_schema时造成大量读取磁盘进行信息统计(如果慢查询中出现关于information_schema中表时,也可以考虑禁用该参数)

处理依据:
当学校的一个食堂一分钟只能为两个打饭, 忽然来了100个时人来打饭,又没排队, 不出会现了打饭的师傅要用点时间
去选择为那个用户服务了, 人越多,场面就越乱, 难免出现用户大吼该他的场面, 最后有可能就出现不是打饭了,而时之间相互
打架了,打饭的师傅也将收到同时有90个以上的Server too busy. 如果能排一下队.最多也就50分钟能处理完了.

以前办法,应该可以让MySQLD不会挂掉.如果业务支撑受到限制,还是想办法处理一下.

问题:
高峰期的业务业务支撑数就是服务器的最终需求数吗?

MySQL使用为什么要分库分表

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

可以用说用到MySQL的地方,只要数据量一大, 马上就会遇到一个问题,要分库分表.
这里引用一个问题为什么要分库分表呢?MySQL处理不了大的表吗?
其实是可以处理的大表的.我所经历的项目中单表物理上文件大小在80G多,单表记录数在5亿以上,而且这个表
属于一个非常核用的表:朋友关系表.

但这种方式可以说不是一个最佳方式. 因为面临文件系统如Ext3文件系统对大于大文件处理上也有许多问题.
这个层面可以用xfs文件系统进行替换.但MySQL单表太大后有一个问题是不好解决: 表结构调整相关的操作基
本不在可能.所以大项在使用中都会面监着分库分表的应用.

从Innodb本身来讲数据文件的Btree上只有两个锁, 叶子节点锁和子节点锁,可以想而知道,当发生页拆分或是添加
新叶时都会造成表里不能写入数据.
所以分库分表还就是一个比较好的选择了.

那么分库分表多少合适呢?
经测试在单表1000万条记录一下,写入读取性能是比较好的. 这样在留点buffer,那么单表全是数据字型的保持在
800万条记录以下, 有字符型的单表保持在500万以下.

如果按 100库100表来规划,如用户业务:
500万*100*100 = 50000000万 = 5000亿记录.

心里有一个数了,按业务做规划还是比较容易的.

发表在 FAQ

FAQ:为什么binlog大小会大于max_binlog_size

作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

CASE:
mysql的IO处理上有抖动, 从日值上观查binlog最大的有(512M)大于max_binlog_size(128M)

原因:
当mysql在处理大事务时,不会进行binlog切换,所有的日值还是会写到一个数据文件里.
如: bulk insert之类的操作
解决办法:
拆分大事务. 或是把大事务处理放到业务低峰期,避免大事务对业务的冲击.

特别提示: 

看到这种现象可以通过分析binlog查到是什么操作, 可以通过业务的方面考虑一下怎么规避.