MYSQL优化浅谈html
msyql是开发经常使用的关系型数据库,快速、稳定、开源等优势就不说了。
我的认为,项目上线,标志着一个项目真正的开始。从运维,到反馈,到再分析,再版本迭代,再优化… 这是一个漫长且考验耐心的过程。在这个过程当中,做为数据存储的关键–>数据库的优化起到尤其重要的做用。
语文学的很差,废话也很少说,下面结合实例我们说说MYSQL须要从哪些方面进行优化。
在说优化以前先声明一下环境java
名称 | 版本 | 备注 |
---|---|---|
系统1 | WIN 7——x64 SP1 | 我的电脑 |
系统2 | centos6.4 | 我的电脑 |
数据库 | 5.5.22 MySQL Community Server (GPL) | 社区版 |
内存 | 8GB | |
CPU | i7-3520 | 四线程 |
优化须要考虑的几个方面
1. SQL优化
2. 索引优化
3. 数据库结构优化
4. 系统配置优化
5. 硬件优化mysql
本次使用我本身的库LMS为优化对象linux
mysql> use lms
Database changed
mysql> show tables; +------------------------+ | Tables_in_lms | +------------------------+ | lm_a_mresource | | lm_a_mresourcerole | | lm_a_role | | lm_a_user | | lm_a_userrole | | lm_c_dept部门 | | lm_c_suppliers | | lm_c_usecomp | | lm_d_applyforpayment | | lm_d_arrival | | lm_d_billing | | lm_d_cgcontract | | lm_d_cgcontractcredit | | lm_d_dlcontract | | lm_d_payed | | lm_d_plan需求计划 | | lm_d_planchangedetails | | lm_d_procurementsource | | lm_d_received | | lm_d_xscontract | +------------------------+ 20 rows in set (0.01 sec)
如何发现有问题的SQL呢?
1. 经过设置slow_query_log开启慢查询日志git
mysql> show variables like 'slow_query_log'; +----------------+-------+ | Variable_name | Value | +----------------+-------+ | slow_query_log | OFF | +----------------+-------+ 1 row in set (0.00 sec) mysql> set global slow_query_log=on; Query OK, 0 rows affected (0.07 sec)
mysql> show variables like 'log_queries_not_using_indexes'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | log_queries_not_using_indexes | ON | +-------------------------------+-------+ 1 row in set (0.00 sec) mysql> set global log_queries_not_using_indexes=on; Query OK, 0 rows affected (0.00 sec)
mysql> set global long_query_time=0.1; Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'long_query_time'; +-----------------+----------+ | Variable_name | Value | +-----------------+----------+ | long_query_time | 0.100000 | +-----------------+----------+ 1 row in set (0.00 sec)
经过以上设计,基本上能够开始优化工做了。
set global 只是全局session生效,重启后失效,若是须要以上配置永久生效,须要在mysql.ini(linux mysql.cnf)中配置正则表达式
[mysqld]
slow_query_log = 1 log_queries_not_using_indexes = 1 long_query_time = 0.1 slow_query_log_file = c:\mysql\log\mysqlslowquery.log
我在配置文件中设置了。因此我重启让配置生效。
ps:slow_query_log_file这个路径要有效且有权限,不然重启后没法写入logsql
C:\Users\sjm>net stop mysql MySQL 服务正在中止.. MySQL 服务已成功中止。 C:\Users\sjm>net start mysql MySQL 服务正在启动 ... MySQL 服务已经启动成功。
若是是linux,就经过如下命令或service等其余方式重启shell
启动:/etc/init.d/mysqld(mysql) start 中止:/etc/init.d/mysqld(mysql) stop 重启:/etc/init.d/mysqld(mysql) restart
最后查看慢查询日志所在的位置,经过查看slow_query_log_file数据库
mysql> show variables like 'slow_query_log_file' \G *************************** 1. row *************************** Variable_name: slow_query_log_file Value: c:\mysql\log\mysqlslowquery.log 1 row in set (0.00 sec)
测试json
mysql> select sleep(1); +----------+ | sleep(1) | +----------+ | 0 | +----------+ 1 row in set (1.00 sec)
经过以上超出0.1秒的测试,slow_query_log_file就会记录该信息,格式以下:
//Time int类型时间戳 加 时间
# Time: 150610 10:24:07
//执行SQL的主机信息
# User@Host: root[root] @ localhost [::1]
//SQL执行信息依次是 执行时间,锁定时间,返回记录数,扫描行数
# Query_time: 1.000057 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0
//SQL执行用时
SET timestamp=1433903047; //SQL内容 select sleep(1);
如下是windows环境须要安装其余支持软件的步骤,非windows直接忽略
windows要执行mysqldumpslow须要安装ActivePerl由于windows上这是一个perl脚本,须要安装ActivePerl才能执行,linux下就能够直接执行mysqldumpslow了。
安装ActivePerl的步骤我就很少说了。安装好后会在环境变量PATH中找到perl的bin目录。我重启了电脑让环境变量生效。
验证ActivePerl是否安装成功
C:\Users\sjm>perl -v
This is perl 5, version 20, subversion 2 (v5.20.2) built for MSWin32-x86-multi-thread-64int (with 1 registered patch, see perl -V for more detail) Copyright 1987-2015, Larry Wall
若是打印和我差很少的就安装成功了。
找到mysqldumpslow的目录。也就是mysql的bin目录(linux用户可直接运行mysqldumpslow)
windows下验证mysqldumpslow可用运行
mysqldumpslow经常使用命令说明:
C:\Program Files\MySQL\MySQL Server 5.5\bin>perl mysqldumpslow.pl -help Usage: mysqldumpslow [ OPTS... ] [ LOGS... ] Parse and summarize the MySQL slow query log. Options are --verbose verbose 查看版本 --debug debug 调试 --help write this text to standard output -v verbose 查看版本 -d debug 调试 -s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default 经过下面的方式排序日志↓↓↓ al: average lock time 平均锁定时间排序 ar: average rows sent 平均发送行数排序 at: average query time 平均查询时间排序 c: count 执行次数排序 l: lock time 锁表时间排序 r: rows sent 总结果行数排序 t: query time 总查询时间排序 -r reverse the sort order (largest last instead of first) 正序排序,即从小到大排序 -t NUM just show the top n queries 查看前X条日志 -a don't abstract all numbers to N and strings to 'S' 显示出数字和字符串,默认数字为 N 字符串为 'S' -n NUM abstract numbers with at least n digits within names -g PATTERN grep: only consider stmts that include this string 过滤字符串,后接正则表达式,如'10$' 以10为结尾的条件,大小写不敏感 -h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard), default is '*', i.e. match all -i NAME name of server instance (if using mysql.server startup script) -l don't subtract lock time from total time
windows支持软件安装完成
优化开始… …
查询lm_d_plan全表条数
mysql> use lms
Database changed
mysql> select count(*) from lm_d_plan;
+----------+
| count(*) |
+----------+
| 57106 | +----------+ 1 row in set (1.94 sec)
超过0.1秒 记录到日志中,并查看
命令解析:排序(-s)按执行次数(c)倒序(-a)
C:\Program Files\MySQL\MySQL Server 5.5\bin>perl mysqldumpslow.pl -s c -a c:\mysql\log\mysqlslowquery.log Reading mysql slow query log from c:\mysql\log\mysqlslowquery.log 执行次数 Count: 3 执行时间Time=0.27s (0s) 锁定时间Lock=1.67s (1s) 发送行数Rows=1.0 (1), 执行地址root[root]@localhost 内容:select count(*) from lm_d_plan Count: 1 Time=0.00s (0s) Lock=0.00s (0s) Rows=0.0 (0), 0users@0hosts C:\Program Files\MySQL\MySQL Server 5.5\bin\mysqld, Version: 5.5.22-log (MySQL Community Server (GPL)). started with: TCP Port: 3306, Named Pipe: (null)
pt-query-digest是用于分析mysql慢查询的一个工具,它能够分析binlog、General log、slowlog,也能够经过SHOWPROCESSLIST或者经过tcpdump抓取的MySQL协议数据来进行分析。能够把分析结果输出到文件中,分析过程是先对查询语句的条件进行参数化,而后对参数化之后的查询进行分组统计,统计出各查询的执行时间、次数、占比等,能够借助分析结果找出问题进行优化。
pt-query-digest只有linux版本,因此只能用linux了。同理设置/etc/my.cnf
[mysqld]
slow_query_log = 1 log_queries_not_using_indexes = 1 long_query_time = 0.1 #这个目录必定要有权限,最好就设置到mysql目录而且经过设置用户权限受权目录及文件可读写 slow_query_log_file = /var/lib/mysql/log/slow.log
顺便帖一下受权命令,目的是让log目录及文件对mysql组的mysql用户可读写,已受权就能够无视。
[root@localhost mysql]# cd /var/lib/mysql [root@localhost mysql]# mkdir log [root@localhost mysql]# chown mysql log [root@localhost mysql]# chgrp mysql log [root@localhost mysql]# chmod ug+rwx log [root@localhost mysql]# cd log/ [root@localhost log]# vi slow.log [root@localhost log]# chown mysql slow.log [root@localhost log]# chgrp mysql slow.log [root@localhost log]# chmod 777 slow.log
安装pt-query-digest
地址:https://www.percona.com/doc/percona-toolkit/2.2/pt-query-digest.html#downloading
选择 wget percona.com/get/percona-toolkit.rpm(也能够选择其余类型安装)
安装过程会出现:
[root@localhost ~]# rpm -ivh percona-toolkit-2.2.14-1.noarch.rpm warning: percona-toolkit-2.2.14-1.noarch.rpm: Header V4 DSA/SHA1 Signature, key ID cd2efd2a: NOKEY error: Failed dependencies: perl(DBI) >= 1.13 is needed by percona-toolkit-2.2.14-1.noarch perl(DBD::mysql) >= 1.0 is needed by percona-toolkit-2.2.14-1.noarch perl(Time::HiRes) is needed by percona-toolkit-2.2.14-1.noarch perl(IO::Socket::SSL) is needed by percona-toolkit-2.2.14-1.noarch perl(Term::ReadKey) is needed by percona-toolkit-2.2.14-1.noarch
缺乏依赖库
经过命令yum -y install perl perl-IO-Socket-SSL perl-DBD-MySQL perl-Time-HiRes
安装
个人安装过程比较曲折,安装perl-DBD-MySQL的时候须要安装依赖库mysql-libs-5.1.73-3.el6_5.x86_64可是我安装的是mysql5.6.20,因此报错**file** /usr/share/mysql/ukrainian/errmsg.sys **from install of** mysql-libs-5.1.73-3.el6_5.x86_64 **conflicts with file from package** MySQL-server-advanced-5.6.20-1.el6.x86_64
,就是说安装的这个mysql-libs依赖库版本与我自己的mysql版本冲突,他的版本低,并且确定不能删除本地数据库,因此须要安装MySQL-shared-compat-5.6.20-1.linux_glibc2.5.x86_64.rpm (与本地数据库版本匹配才能够)来解决mysql-libs的问题。
还有经过yum安装的时候最好添加国内的源,我添加是163的,详情以下
来源:http://blog.chinaunix.net/uid-23683795-id-3477603.html 网易(163)yum源是国内最好的yum源之一 ,不管是速度仍是软件版本,都很是的不错,将yum源设置为163yum,能够提高软件包安装和更新的速度,同时避免一些常见软件版本没法找到。具体设置方法以下: 1,进入yum源配置目录 cd /etc/yum.repos.d 2,备份系统自带的yum源 mv CentOS-Base.repo CentOS-Base.repo.bk 下载163网易的yum源: wget http://mirrors.163.com/.help/CentOS6-Base-163.repo 3,更新玩yum源后,执行下边命令更新yum配置,使操做当即生效 yum makecache 4,除了网易以外,国内还有其余不错的yum源,好比中科大和搜狐的,你们能够根据本身需求下载 中科大的yum源: wget http://centos.ustc.edu.cn/CentOS-Base.repo sohu的yum源 wget http://mirrors.sohu.com/help/CentOS-Base-sohu.repo 理论上讲,这些yum源redhat系统以及fedora也是能够用 的,可是没有通过测试,须要的站长能够本身测试一下。
这样pt-query-digest安装告一段落
经过命令pt-query-digest --help
能够查看帮助文档,同时说明安装是没问题的
语法及重要选项
pt-query-digest [OPTIONS] [FILES] [DSN]
–create-review-table 当使用–review参数把分析结果输出到表中时,若是没有表就自动建立。
–create-history-table 当使用–history参数把分析结果输出到表中时,若是没有表就自动建立。
–filter 对输入的慢查询按指定的字符串进行匹配过滤后再进行分析
–limit限制输出结果百分比或数量,默认值是20,即将最慢的20条语句输出,若是是50%则按总响应时间占比从大到小排序,输出到总和达到50%位置截止。
–host mysql服务器地址
–user mysql用户名
–password mysql用户密码
–history 将分析结果保存到表中,分析结果比较详细,下次再使用–history时,若是存在相同的语句,且查询所在的时间区间和历史表中的不一样,则会记录到数据表中,能够经过查询同一CHECKSUM来比较某类型查询的历史变化。
–review 将分析结果保存到表中,这个分析只是对查询条件进行参数化,一个类型的查询一条记录,比较简单。当下次使用–review时,若是存在相同的语句分析,就不会记录到数据表中。
–output 分析结果输出类型,值能够是report(标准分析报告)、slowlog(Mysql slow log)、json、json-anon,通常使用report,以便于阅读。
–since 从什么时间开始分析,值为字符串,能够是指定的某个”yyyy-mm-dd [hh:mm:ss]”格式的时间点,也能够是简单的一个时间值:s(秒)、h(小时)、m(分钟)、d(天),如12h就表示从12小时前开始统计。
–until 截止时间,配合—since能够分析一段时间内的慢查询。
直接分析: pt-query-digest /var/lib/mysql/log/slow.log
第一部分:整体统计结果
# 160ms user time, 10ms system time, 22.46M rss, 199.00M vsz # Current date: Wed Jun 10 18:09:50 2015 # Hostname: localhost.localdomain # Files: /var/lib/mysql/log/slow.log # Overall: 1 total(一共多少条sql), 1 unique(查询条件进行参数化之后,总共有多少个不一样的查询少), 0 QPS, 0x concurrency ______________________ # Time range(日志的时间范围): all events occurred at 2015-06-10 14:31:21 # Attribute total min max avg 95% stddev median # ============ ======= ======= ======= ======= ======= ======= ======= # Exec time(执行时间) 23ms 23ms 23ms 23ms 23ms 0 23ms # Lock time(锁定时间) 253us 253us 253us 253us 253us 0 253us # Rows sent(返回条数) 8 8 8 8 8 0 8 # Rows examine(扫描条数)5.51k 5.51k 5.51k 5.51k 5.51k 0 5.51k # Query size() 50 50 50 50 50 0 50 第二部分:查询分组统计结,这部分对查询进行参数化并分组,而后对各种查询的执行状况进行分析,结果按总执行时长,从大到小排序 # Profile # Rank Query ID Response time Calls R/Call V/M Item # ==== ================== ============= ===== ====== ===== =============== # 1 0x9E3ACC745D5A7770 0.0228 100.0% 1 0.0228 0.00 SELECT lm_d_plan Response 总的响应时间 time 该查询在本次分析中总的时间占比 calls: 执行次数,即本次分析总共有多少条这种类型的查询语句。 R/Call: 平均每次执行的响应时间。 Item : 查询对象 # 第三部分:每一种查询的详细统计结果 # **Query 1**: 0 QPS, 0x concurrency, ID 0x9E3ACC745D5A7770 at byte 0 ________ # This item is included in the report because it matches --limit. # Scores: V/M = 0.00 # Time range: all events occurred at 2015-06-10 14:31:21 # Attribute pct total min max avg 95% stddev median # ============ === ======= ======= ======= ======= ======= ======= ======= # Count 100 1 # Exec time 100 23ms 23ms 23ms 23ms 23ms 0 23ms # Lock time 100 253us 253us 253us 253us 253us 0 253us # Rows sent 100 8 8 8 8 8 0 8 # Rows examine 100 5.51k 5.51k 5.51k 5.51k 5.51k 0 5.51k # Query size 100 50 50 50 50 50 0 50 # String: # Hosts localhost # Users root # Query_time distribution # 1us # 10us # 100us # 1ms # 10ms ################################################################ # 100ms # 1s # 10s+ # Tables # SHOW TABLE STATUS LIKE 'lm_d_plan'\G # SHOW CREATE TABLE `lm_d_plan`\G # EXPLAIN /*!50100 PARTITIONS*/ select itemsName from lm_d_plan group by FK_deptNo\G 查询的详细统计结果,最上面的表格列出了执行次数、最大、最小、平均、95%等各项目的统计。 Databases: 库名 //这个示例中没有 Users: 各个用户执行的次数(占比) Query_time distribution : 查询时间分布, 长短体现区间占比,本例中1s-10s之间查询数量是10s以上的两倍。 Tables: 查询中涉及到的表 Explain: 示例
用法示例
(1)直接分析慢查询文件: pt-query-digest [参数] /var/lib/mysql/log/slow.log > slow.report
(2)分析最近12小时内的查询: pt-query-digest --since=12h /var/lib/mysql/log/slow.log > slow_report2.log
(3)分析指定时间范围内的查询: pt-query-digest /var/lib/mysql/log/slow.log --since '2014-04-17 09:30:00' --until '2014-04-17 10:00:00'> > slow_report3.log
(4)分析指含有select语句的慢查询 pt-query-digest--filter '$event->{fingerprint} =~ m/^select/i' /var/lib/mysql/log/slow.log> slow_report4.log
(5) 针对某个用户的慢查询 pt-query-digest--filter '($event->{user} || "") =~ m/^root/i' /var/lib/mysql/log/slow.log> slow_report5.log
(6) 查询全部全部的全表扫描或full join的慢查询 pt-query-digest--filter '(($event->{Full_scan} || "") eq "yes") ||(($event->{Full_join} || "") eq "yes")' slow.log> slow_report6.log
(7)把查询保存到query_review表 pt-query-digest --user=root –password=abc123 --review h=localhost,D=test,t=query_review--create-review-table /var/lib/mysql/log/slow.log
(8)把查询保存到query_history表 pt-query-digest --user=root –password=abc123 --review h=localhost,D=test,t=query_ history--create-review-table /var/lib/mysql/log/slow.log
pt-query-digest --user=root –password=abc123--review h=localhost,D=test,t=query_history--create-review-table /var/lib/mysql/log/slow.log
(9)经过tcpdump抓取mysql的tcp协议数据,而后再分析 tcpdump -s 65535 -x -nn -q -tttt -i any -c 1000 port 3306 > mysql.tcp.txt
pt-query-digest --type tcpdump mysql.tcp.txt> slow_report9.log
(10)分析binlog mysqlbinlog mysql-bin.000093 > mysql-bin000093.sql
pt-query-digest --type=binlog mysql-bin000093.sql > slow_report10.log
(11)分析general log pt-query-digest --type=genlog localhost.log > slow_report11.log
数据库会先进行计划分析,再进行查询。执行计划从侧面反映了SQL的执行效率,那么经过explain查看并分析执行计划是很是有必要的
mysql> explain select * from lm_d_plan group by fk_deptno; +----+-------------+-----------+-------+---------------+-----------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+-------+---------------+-----------+---------+------+------+-------+ | 1 | SIMPLE | lm_d_plan | index | FK_deptNo | FK_deptNo | 99 | NULL | 5416 | NULL | +----+-------------+-----------+-------+---------------+-----------+---------+------+------+-------+ 1 row in set (0.00 sec)
explain返回的各列含义
select_type
SIMPLE:简单SELECT(不使用UNION或子查询等)
PRIMARY:个人理解是最外层的select
UNION : UNION中的第二个或后面的SELECT语句
DEPENDENT UNION : UNION中的第二个或后面的SELECT语句,取决于外面的查询
UNION RESULT : UNION的结果
SUBQUERY : 子查询中的第一个SELECT
DEPENDENT SUBQUERY : 子查询中的第一个SELECT,取决于外面的查询
DERIVED:派生表的SELECT(FROM子句的子查询)
table: 显示这条sql涉及到的那些表,有时不是真实的表名字,看到的是derivedx(x是个数字,个人理解是第几步执行的结果)
type : 这是重要的列,显示链接是那种类型,从最好到最坏依次是:const(常数查找,主键或惟一索引查找),system:(这是const联接类型的一个特例。表仅有一行知足条件),eq_reg: (范围查找,主键或者索引范围的查找),ref(常见链接查询,一个表是基于某个索引的查找),range(基于索引范围的查找),index(索引的扫描),all(表扫描)
possible_keys : 显示可能用到的索引。若是为空,没有能够用到的索引
key : 实际使用的索引,若是为空则没有使用索引
key_len : 使用索引的长度,在不损失精确性的状况下,长度越短越好
ref: 显示索引的哪一列被使用了。若是可能的话,是一个常数。
rows : mysql认为必须检查的用来返回数据的行数
Extra : 扩展列,须要注意的返回值:
Using filesort 看到这个就须要优化了。mysql须要经过额外的步骤发现如何对返回行进行排序,他须要根据链接类型以及存储排序键值和匹配条件的全部行的行指针进行全部行排序,一般发生order by操做上。
Using temporary 看到这个就须要优化了,mysql须要创建临时表来存储结果,这一般发生在对不一样的列集进行order
by操做上,而不是group by操做。
案例1:咱们须要lm_d_plan的最后建立时间是什么
mysql> select max(createtime) from lm_d_plan; +-----------------+ | max(createtime) | +-----------------+ | 20150528143338 | +-----------------+ 1 row in set (0.11 sec) mysql> explain select max(createtime) from lm_d_plan; +----+-------------+-----------+------+---------------+------+---------+------+-------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-----------+------+---------------+------+---------+------+-------+-------+ | 1 | SIMPLE | lm_d_plan | ALL | NULL | NULL | NULL | NULL | 55562 | NULL | +----+-------------+-----------+------+---------------+------+---------+------+-------+-------+ 1 row in set (0.00 sec)
select_type 这是一个简单的查询
table 查询的是lm_d_plan 表
type ALL说明是全表扫描
rows 须要扫描的行数55562
经过以上信息,显然是一个低效的sql,随着数据量增大,它的IO也随之增大,极可能拖慢整个服务器,说明确定是须要优化的。
策略:一般状况下在 createtime上创建索引
mysql> create index createime on lm_d_plan(createtime); Query OK, 0 rows affected (0.65 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> explain select max(createtime) from lm_d_plan; +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ 1 row in set (0.00 sec) mysql> select max(createtime) from lm_d_plan; +-----------------+ | max(createtime) | +-----------------+ | 20150528143338 | +-----------------+ 1 row in set (0.00 sec)
Extra 返回 Select tables optimized away ,意思就是并不须要查询具体的表,直接经过查询索引就能够完成。由于索引是顺序排列的,经过索引的统计信息就能够得到最大的createime。这也是个覆盖索引,彻底经过索引的信息就能够完成查询。
案例2:咱们须要找出各部门lm_c_dept的提报的需求计划lm_d_plan是多少
mysql> SELECT d.deptName,count(*) from lm_d_plan p INNER JOIN lm_c_dept d ON d.deptNo = p.FK_deptNo GROUP BY p.FK_deptNo; +--------------------------------+----------+ | deptName | count(*) | +--------------------------------+----------+ | xx一部 | 1573 | | xx板块业务部 | 282 | | yy板块业务二部 | 781 | | aa块业务部 | 4753 | | cc块业务部 | 12252 | | dd板块业务部 | 6827 | | xx设备业务部 | 4442 | | cccc物资业务部 | 26196 | +--------------------------------+----------+ 8 rows in set (0.09 sec) mysql> explain SELECT d.deptName,count(*) from lm_d_plan p INNER JOIN lm_c_dept d ON d.deptNo = p.FK_deptNo GROUP BY p.FK_deptNo\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: d(多是表中间有下划线显示有问题lm_c_dept ) type: ALL possible_keys: deptNo key: NULL key_len: NULL ref: NULL rows: 11 Extra: Using temporary; Using filesort select_type 这是一个简单的查询 table 查询的是lm_c_dept 表 type ALL说明是全表扫描,由于没有where条件,表扫描也能够说是正常的。 possible_keys 可能用到的索引是deptNo rows 须要扫描的行数11 Extra 既使用了临时表,也用了文件排序,这是效率极低的。 *************************** 2. row *************************** id: 1 select_type: SIMPLE table: p(多是表中间有下划线显示有问题lm_d_plan ) type: ref possible_keys: FK_deptNo key: FK_deptNo key_len: 99 ref: lms.d.deptNo(这里应该是lms.lm_c_dept.deptNo) rows: 3968 Extra: Using index select_type 这是一个简单的查询 table 查询的是lm_d_plan 表 type ref常见链接查询,一个表是基于索引的查找 possible_keys 可能用到的索引是FK_deptNo key 使用的索引是FK_deptNo key_len 索引的长度是99 ref lms.lm_c_dept.deptNo这列索引被使用了 rows 须要扫描的行数3968 Extra 使用索引 2 rows in set (0.00 sec)
策略:经过以上分析,显然最须要优化的地方是临时表和文件排序了,由于这种低效率查询会直接对服务器产生极大的资源消耗,同时影响整个服务器性能。经过优化sql语句来改善这种状况。
mysql> explain SELECT d.deptName,c.ct FROM lm_c_dept d INNER JOIN(SELECT p.FK_deptNo,count(*) ct from lm_d_plan p GROUP BY p.FK_deptNo) c ON d.deptNo = c.FK_deptNo\G *************************** 1. row *************************** id: 1 select_type: PRIMARY table: d(多是表中间有下划线显示有问题lm_c_dept ) type: ALL possible_keys: deptNo key: NULL key_len: NULL ref: NULL rows: 11 Extra: NULL *************************** 2. row *************************** id: 1 select_type: PRIMARY table: <derived2> type: ref possible_keys: <auto_key0> key: <auto_key0> key_len: 99 ref: lms.d.deptNo rows: 555 Extra: NULL *************************** 3. row *************************** id: 2 select_type: DERIVED table: p(多是表中间有下划线显示有问题lm_d_plan ) type: index possible_keys: FK_deptNo key: FK_deptNo key_len: 99 ref: NULL rows: 55562 Extra: Using index 3 rows in set (0.00 sec)
经过执行计划能够看出Extra已经没有Using filesort 和 Using temporary临时表和使用文件排序已经改善。执行结果也加从0.09 sec 加快到0.02 sec,若是有条件,最好在子查询里增长过滤,而不是外查询。
mysql> select count(distinct cartCode),count(distinct cartlineNum) from lm_d_plan; +--------------------------+-----------------------------+ | count(distinct cartCode) | count(distinct cartlineNum) | +--------------------------+-----------------------------+ | 40782 | 341 | +--------------------------+-----------------------------+ 1 row in set (0.14 sec)
因而可知,若是将cartCode和cartlineNum创建联合索引,cartCode应该放到前面。 create index cartCode_cartlineNum on lm_d_plan(cartCode,cartlineNum);
索引的维护和优化之重复及冗余索引
重复索引是最同一列屡次创建索引,好比在primay key列创建惟一索引,由于primay key已是主键索引了。再创建惟一索引就索引重复了。
冗余索引是指多个索引的前缀列相同,或者联合索引中包含的主键索引。(Innodb会在每一个索引的后面附加主键索引。)
经常使用索引操做
查看索引 show index from tablename;
删除索引,其中,在前面的两条语句中,都删除了table_name中的索引index_name。而在最后一条语句中,只在删除PRIMARY KEY索引中使用,由于一个表只可能有一个PRIMARY KEY索引,所以不须要指定索引名。
drop index index_name on table_name ; alter table table_name drop index index_name ; alter table table_name drop primary key ;
建立索引:
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name [USING index_type] ON tbl_name (index_col_name,...) index_col_name: col_name [(length)] [ASC | DESC]
使用alter的方法建立索引
alter table table_name add index index_name (column_list) ; alter table table_name add unique (column_list) ; alter table table_name add primary key (column_list) ;
对于CHAR和VARCHAR列,只用一列的一部分就可建立索引。建立索引时,使用col_name(length)语法,对前缀编制索引。前缀包括每列值的前length个字符。BLOB和TEXT列也能够编制索引,可是必须给出前缀长度。
此处展现的语句用于建立一个索引,索引使用列名称的前5个字符。 create index itemsCode on lm_d_plan(itemsCode(5));
由于多数名称的前10个字符一般不一样,因此此索引不会比使用列的全名建立的索引速度慢不少。另外,使用列的一部分建立索引可使索引文件大大减少,从而节省了大量的磁盘空间,有可能提升INSERT操做的速度。
前缀最长为255字节。对于MyISAM和InnoDB表,前缀最长为1000字节。注意前缀的限长以字节计,而CREATE INDEX语句中的前缀长度指的是字符的数目。对于使用多字节字符集的列,在指定列的前缀长度时,要考虑这一点。
在MySQL中:
· 只有当您正在使用MyISAM, InnoDB或BDB表类型时,您能够向有NULL值的列中添加索引。
· 只有当您正在使用MyISAM, BDB或InnoDB表类型时,您能够向BLOB或TEXT列中添加索引。
一个index_col_name规约能够以ASC或DESC为结尾。这些关键词未来能够扩展,用于指定递增或递减索引值存储。目前,这些关键词被分析,可是被忽略;索引值均以递增顺序存储。
部分储存引擎容许在建立索引时指定索引类型。index_type指定语句的语法是USING type_name。不一样的储存引擎所支持的type_name值已显示在下表中。若是列有多个索引类型,当没有指定index_type时,第一个类型是默认值。
存储引擎 | 容许的索引类型 |
---|---|
MyISAM | BTREE |
InnoDB | BTREE |
MEMORY/HEAP | HASH, BTREE |
示例:
CREATE TABLE lookup (id INT) ENGINE = MEMORY; CREATE INDEX id_index USING BTREE ON lookup (id);
TYPE type_name能够做为USING type_name的同义词,用于指定索引类型。可是,USING是首选的格式。另外,在索引规约语法中,位于索引类型前面的索引名称不能使用TYPE。这是由于,与USING不一样,TYPE不是保留词,所以会被认为是一个索引名称。
若是您指定的索引类型在给定的储存引擎中不合法,可是有其它的索引类型适合引擎使用,而且不会影响查询功能,则引擎应使用此类型。
FULLTEXT索引只能对CHAR, VARCHAR和TEXT列编制索引,而且只能在MyISAM表中编制。
SPATIAL索引只能对空间列编制索引,而且只能在MyISAM表中编制。
索引的维护和优化之pt-duplicate-key-checker
使用方法:pt-duplicate-key-checker -uroot -p 'xxxx' -h 127.0.0.1
[root@localhost ~]# pt-duplicate-key-checker -uroot -p ‘123456’ -h localhost
# ########################################################################
# lms.lm_d_plan
# ########################################################################
# oldPlanNum is a left-prefix of oldPlanNum_oldPlanlineNum
oldPlanNum索引 已是oldPlanNum_oldPlanlineNum联合索引的左前缀了。也就是说查询oldPlanNum能够直接命中oldPlanNum_oldPlanlineNum联合索引,而毋需再建立oldPlanNum 索引了。
# Key definitions:
关键的定义
# KEY `oldPlanNum` (`oldPlanNum`),
# KEY `oldPlanNum_oldPlanlineNum` (`oldPlanNum`,`oldPlanlineNum`),
# Column types:
列信息
# `oldplannum` varchar(20) default null comment '原始号'
# `oldplanlinenum` int(4) default null comment '原始行号'
# To remove this duplicate index, execute:
建议是remove这个重复的索引,执行如下命令
ALTER TABLE `lms`.`lm_d_plan` DROP INDEX `oldPlanNum`; 一样的错误类型以下: # cartCode is a left-prefix of cartCode_cartlineNum # Key definitions: # KEY `cartCode` (`cartCode`), # KEY `cartCode_cartlineNum` (`cartCode`,`cartlineNum`), # Column types: # `cartcode` varchar(20) default null comment '?????' # `cartlinenum` int(4) default null comment '?????' # To remove this duplicate index, execute: ALTER TABLE `lms`.`lm_d_plan` DROP INDEX `cartCode`; # ######################################################################## # Summary of indexes # ######################################################################## # Size Duplicate Indexes 7000812 # Total Duplicate Indexes 2 # Total Indexes 155
经过pt-dulication-key-checker能够高效的优化数据库的索引。
索引的维护和优化之删除无用索引pt-index-usage
pt-index-usage有空补充,你们有兴趣能够本身研究一下。
from_unixtime(),unix_timestamp()
两个函数进行转换。insert into tablename(timestr) values( unix_timestamp('2015-05-05 20:20:00')); select from_unixtime(timestr) from tablename;
**用bigint存储ip地址,利用inet_aton(),inet_ntoa()
两个函数进行转换。
insert into tablename(ipaddr) values( inet_aton('192.168.1.1')); select inet_ntoa(ipaddr) from tablename;
范式化是指数据库设计遵循的设计规范,目前说的范式化通常是指第三设计范式,也就是要求表中非关键字段对任意候选关键字段不存在传递函数依赖则符合第三范式。
示例:
商品名称 | 价格 | 重量 | 有效期 | 分类 | 分类描述 |
---|---|---|---|---|---|
可乐 | 3.5 | 600ml | 2015-09-09 | 酒水饮料 | 碳酸饮料 |
黄瓜 | 1.0 | 100g | 生鲜食品 | 蔬菜 |
存在如下传递函数依赖管理
商品名称—>分类—>分类描述
很显然若是商品名称是关键字段(任意候选关键字段),那么非关键字段分类描述依赖分类依赖商品名称。也就是说分类描述传递函数依赖关键字段商品名称。
进行标准第三范式化修改后:
Table 1
商品名称 | 价格 | 重量 | 有效期 |
---|---|---|---|
可乐 | 3.5 | 600ml | 2015-09-09 |
黄瓜 | 1.0 | 100g |
Table 2
分类 | 分类描述 |
---|---|
酒水饮料 | 碳酸饮料 |
生鲜食品 | 蔬菜 |
Table 3
商品名称 | 分类 |
---|---|
可乐 | 酒水饮料 |
黄瓜 | 生鲜食品 |
反范式化是为了查询效率的考虑,把本来符合第三范式的表适当的增长冗余,以达到优化查询的目的,反范式化是一种以空间换取时间的策略。
示例:
订单表:订单ID,订单编号,用户ID,下单时间,订单状态 用户表:用户ID,姓名,电话,地址 订单商品表:订单ID,商品ID,订单数量,商品价格 商品表:商品ID,名称,描述
需求描述:须要给客户展示订单的总体概况信息,包括订单,价格,商品信息,构造的SQL以下:
SELECT a.订单编号,b.姓名,b.电话,b.地址,SUM(c.订单数量 * c.商品价格) as 订单价格 FROM 订单表 a LEFT JOIN 用户表 b ON a.用户ID = b.用户ID LEFT JOIN 订单商品表 c ON a.订单ID = c.订单ID
咱们要查询这些信息,至少关联三张表,表结构就是这样的。
若是进行一些反范式化设计,将会大大改善上述查询效率极低的SQL。
订单表:订单ID,订单编号,用户ID,下单时间,订单状态,订单价格,姓名,电话,地址 用户表:用户ID,姓名,电话,地址 订单商品表:订单ID,商品ID,订单数量,商品价格 商品表:商品ID,名称,描述
一样的需求,构造SQL以下:
SELECT a.订单编号,a.姓名,a.电话,a.地址,a.订单价格 FROM 订单表 a
显然,在优化数据库的时候,进行一些适当的反范式化,会在很大程度上提高执行效率。
所谓垂直拆分就是把原来一个不少列的表拆分红多个表,解决表的宽度问题,一般垂直拆分能够按如下原则进行:
1. 把不经常使用的字段单独存放在一张表中
2. 把大字段独立存放在一张表中
3. 把常用的字段存放到一块儿
水平拆分是为了解决单表数据量过大的问题,水平拆分每个表结构都是同样的。
如何将数据平均插入到N张表呢?
经常使用的水平拆分的方法:
1. 对ID进行hash运算,若是要拆分红5个表,则使用mod(ID,5)(取模)取出0-4个值,取模后0插0表,取1插1表
2. 针对不一样的hashID,把数据存储到不一样的表中
拆分后所面临的挑战:
1. 跨分区进行查询
2. 统计及后台报表操做。
建议:先后台表进行拆开,前台考虑效率因此进行水平拆分,后台考虑方便完整的数据统计,单独生成统计及报表所用的表
数据库是基于操做系统之上的,目前大多数mysql都运行在linux上,因此对操做系统的一些配置也会影响到mysql 的性能,下面就列出经常使用的优化配置。
网络方面的配置,要修改/etc/sysctl.conf文件
1. 增长TCP支持的队列数 net.ipv4.tcp_max_syn_backlog=65535
2. 减小断开链接时间,资源回收,因为TCP/IP是有三个状态的,为了加快timewait状态回收,优化如下参数。
net.ipv4.tcp_max_tw_buckets=8000 net.ipv4.tcp_tw_reuse=1 net.ipv4.tcp_tw_recycle=1 net.ipv4.tcp_fin_timeout=10
优化打开文件数,由于mysql的表都是已文件格式存储的,对innodb类型的查询都会打开表文件,因此增长文件打开数在必定程度上提高了mysql查询效率。可使用ulimit -a查看目录的各限制,能够修改/etc/security/limits.conf 文件,增长如下内容增长文件打开数限制
* soft nofile 65535 * hard nofile 65535
修改先后对比:
[root@localhost ~]# ulimit -a ... open files (-n) 1024 ... [root@localhost ~]# ulimit -a ... open files (-n) 65535 ...
除此之外最好在mysql所在服务器关闭iptables selinux等防火墙软件,经过硬件防火墙等方式解决安全问题。
mysql能够经过启动时指定配置参数和使用配置文件进行配置,在大多数状况下配置文件存储在/etc/my.cnf或是/etc/mysql/my.cnf。windows通常在mysql目录中my.ini文件。mysql查找配置文件的顺序能够经过如下命令获取。 $ /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'
注意,若是多个位置存在配置文件,则后面的会覆盖前面的配置。
innodb_buffer_poor_size:
很是重要的参数,用来配置Innodb的缓冲池,若是数据库中只有innodb表,则推荐配置为总内存的75%。
mysql> SELECT ENGINE,ROUND(SUM(data_length + index_length)/1024/1024,1) as 'Total MB' FROM INFORMATION_SCHEMA.TABLES WHERE table_schema not in ("information_schema","performance_schema" ) GROUP BY ENGINE; +--------+----------+ | ENGINE | Total MB | +--------+----------+ | CSV | 0.0 | | InnoDB | 79.3 | | MyISAM | 0.7 | +--------+----------+ 3 rows in set (0.59 sec)
innodb_buffer_poor_size >= Total MB(数据+索引大小),若是达不到要求,设置尽量的大就能够。
innodb_buffer_poor_instances:(mysql5.5+以上才有)
能够控制缓冲池个数,默认状况下只有一个缓冲池,mysql中有些操做须要独占使用缓冲池,那么就可能形成阻塞。若是分红多分能够,通常能够分为4到8份,大小=总缓冲大小/分数。
innodb_log_buffer_size:
innodb log 缓冲大小,因为日志最长每秒种就会刷新,因此不须要配置多大,只要可以存下1秒钟事务数据就能够了。
innodb_flush_log_at_trx_commit:(听说很管用)
关键参数,对innodb磁盘IO影响很大,默认为1,能够设置0,1,2三个值,通常建议为2,但若是数据安全性较高,则设置为1.
抱怨Innodb比MyISAM慢 100倍?那么你大概是忘了调整这个值。默认值1的意思是每一次事务提交或事务外的指令都须要把日志写入(flush)硬盘,这是很费时的。特别是使用电 池供电缓存(Battery backed up cache)时。设成2对于不少运用,特别是从MyISAM表转过来的是能够的,它的意思是不写入硬盘而是写入系统缓存。日志仍然会每秒flush到硬 盘,因此你通常不会丢失超过1-2秒的更新。设成0会更快一点,但安全方面比较差,即便MySQL挂了也可能会丢失事务的数据。而值2只会在整个操做系统 挂了时才可能丢数据。
innodb_read_io_threads 和 innodb_write_io_threads :(mysql5.5+以上可调整)
决定innodb读写IO的进程数量,默认为4。可根据服务器具体状况设置
innodb_file_per_table:建议设为ON。
关键参数,控制innodb每一个表独立使用的表空间,默认为OFF,也就是全部表都会创建在共享表空间中。共享表空间IO成为瓶颈,顺序写入的时候,共享表空间只有一个文件,并发写入的时候效率很低。共享表空间是没法单独收缩的,当删除一个很大的数据后,以后把全部数据导出后导入才能够收缩。
innodb_stats_on_metadata:默认为ON,建议OFF
决定mysql在什么状况下刷innodb表的统计信息。优化器在工做中须要经过统计信息获取索引等系想你,保证优化器使用到正确的索引。可是若是设为ON,在进行show,select等操做时都会进行没必要要的刷新,因此建议在修改数据库后人为进行刷新。
PERCONA CONFIGURATION WIZARD FOR MYSQL
http://tools.percona.com/wizard
介绍略,你们能够本身研究
建议RAID1+0 :就是RAID1和RAID0结合,兼顾性能和数据安