nginx显示文件目录列表并提供文件下载服务

测试环境:

centos 7 64 位

安装并启动nginx

# yum -y install wget vim
# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
# yum clean all
# yum -y install nginx
# iptables -F
# service nginx start

更改nginx配置文件

# cd /etc/nginx/
# cp nginx.conf nginx.conf.bk
# vim nginx.conf   <--找到server字段下的location,更改如下
.......
.......
server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        location / {
charset utf-8;
autoindex on;
autoindex_exact_size on;
autoindex_localtime on;
if ($request_filename ~* ^.*?.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){
            add_header Content-Disposition attachment;
            }
        }
        error_page 404 /404.html;
            location = /40x.html {
        }
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
.......
....... 

新建一些测试文件和文件夹

# cd /usr/share/nginx/html/
# rm -fr *
# touch A{1..9}.txt
# mkdir docs
# mkdir pdf
# chown nginx.nginx /usr/share/nginx/html/ -R
# service nginx reload

浏览器验证访问,如图

未分类

Nginx作HTTP的负载均衡max_fails和fail_timeout如何设置?

很多同学,经常利用Nginx来作HTTP的负载均衡,但是对于Nginx的负载均衡原理却了解得不够,如,今天讲解的max_fails、fail_timeout参数。

一、描述

在Nginx的负载均衡检查模块中,对于负载均衡的节点可以配置如下可选参数参数:

max_fails=1 fail_timeout=10s

这个是Nginx在负载均衡功能中,用于判断后端节点状态,所用到两个参数。

Nginx基于连接探测,如果发现后端异常,在单位周期为fail_timeout设置的时间,中达到max_fails次数,这个周期次数内,如果后端同一个节点不可用,那么接将把节点标记为不可用,并等待下一个周期(同样时常为fail_timeout)再一次去请求,判断是否连接是否成功。

默认:fail_timeout为10s,max_fails为1次。

二、探讨

经过这几天的压力测试和研究,如下:

1、在后端服务能力正常,nginx 可以调节如下阀值,增加其并发能力。

这些参数主要是一次请求,超时时间间隔。课程中有介绍到。

client_header_timeout 30;
client_body_timeout 30;
send_timeout 30;
read_timeout 30;```

这里,表示允许后端节点失败次数,如果调大,Nginx相当于把请求缓冲,如果整体的的后端服务处于可用状态,对于高并发的场景来说,建议适当调大是有效的。

max_fails=10 fail_timeout=60s`

当然,和所有优化阀值一样,数值加大,对应于给Nginx的压力也会相应加大。

2、检测方式及对后端服务性能

Nginx只有当有访问时后,才发起对后端节点探测。如果本次请求中,节点正好出现故障,Nginx将把请求转交给接下来的节点处理。所以不会影响到这次请求的正常进行。

linux 配置nfs挂载共享目录

第一步:安装nfs;

第二步:启动nfs;

service nfs start

第三步:编辑/etc/exports文件;

加入:/xxxdir
xxx.xxx.xxx.xxx(rw,sync,no_root_squash),xxx.xxx.xxx.xxx是要挂载的IP

例如:

/temp      192.168.9.19(rw,sync,no_root_squash)
/共享目录        地址      (权限)

地址可以使用完整IP或网段,也可以地址可以使用主机名,DNS解析的和本地/etc/hosts解析的都行

权限有:  
rw:read-write,可读写;    注意,仅仅这里设置成读写客户端还是不能正常写入,还要正确地设置共享目录的权限,参考问题7  
ro:read-only,只读;  
sync:文件同时写入硬盘和内存;  
async:文件暂存于内存,而不是直接写入内存;  
no_root_squash:NFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,也拥有root权限。显然开启这项是不安全的。  
root_squash:NFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,拥有匿名用户权限,通常他将使用nobody或nfsnobody身份;  
all_squash:不论NFS客户端连接服务端时使用什么用户,对服务端分享的目录来说都是拥有匿名用户权限;  
anonuid:匿名用户的UID值,通常是nobody或nfsnobody,可以在此处自行设定;  
anongid:匿名用户的GID值。 

第四步:重启nfs服务

service nfs restart

不影响其他挂载目录情况下加载应用exports修改:

exportfs -r
或者
service nfs reload

第五步:在挂载机上(上面例子中192.168.9.19)新建目录,比如/itemp,并更改权限

chmod -R 777 itemp

最后挂载:

mount -t nfs 共享主机IP:/temp /itemp

Linux中利用shell脚本定时检测mysql状态,实现自动重启

问题简介

未分类

最近几个月网站服务器老是半夜挂掉,罪魁祸首就是 mysql 自动停止导致的,那就头痛医头,想办法让Mysql 自动启动起来。要解决这个mysql无规律自动停掉的问题,首先给服务器加个定时执行任务,每分钟去检测一下数据库服务的运行状态,要是down 掉了,就立马启动起来。

处理步骤

第一步,添加定时任务,使用crontab。

crontab -e
*/1 * * * * /目录/文件名.sh

#表示每分钟执行一次 sh 文件

第二步,编写shell执行文件,代码如下:

#!/bin/bash
pgrep -x mysqld &amp;&gt; /dev/null
if [ $? -ne 0 ]
then
echo “At time: `date` :MySQL is stop .”&gt;&gt; /日志路径
/etc/init.d/mysqld start
else
echo “MySQL server is running .”
fi

这里要注意,不能用

service mysqld/mysql start

命令来启动,会导致mysql启动不了,应使用绝对路径,

/etc/init.d/mysqld start

来启动。

另外,也可以不写入日志。删掉>> /日志路径即可。

完成。

就是这么简单。如发现这样操作了,还没有得到期望的结果,可排查crontab是否执行了相应的任务,脚本权限是否够,脚本内部命令或格式是否正确等方面。

如写入

*/1 * * * * echo “test” &gt;&gt; /var/log/test

检测cron是否正确执行。

再看看crontab的执行历史记录:

cd /var/log
tail -100 cron

若权限不够,则加上权限。

chmod 777 ./shell文件.sh

mysql报错注入总结

最近又深刻的研究了一下mysql的报错注入,发现很多值得记录的东西,于是写了这篇博客做一个总结,目的是为了更深刻的理解报错注入

报错注入原因及分类

  • 既然是研究报错注入,那我们先要弄明白为什么我们的注入语句会导致数据库报错,报错的原因我自己总结了一下,有以下几点

重复数据报错,这里的重复主要有两个方面,其中之一是基于主键的唯一性:一个表主键必须是唯一的,如果一个表尝试生成两个相同的主键,就会爆出Duplicate entry ‘1’ for key ‘group_key’的主键重复错误,于是根据这种报错就产生了floor(rand(0)*2)等注入手法,另外一个就是基于列名的唯一性,如果我们在一个表中构造了两个相同的列名,就会产生Duplicate column name的错误,报错方法通常有NAME_CONST,或者利用join和using关键字连接同一个表创建子查询进行报错,这个方法从lctf2017 pcat大佬的writeup中学到的,在我的另一篇文章中会提到

  • 基于数据类型不一致而产生的报错:mysql的一些函数参数要求的是什么数据类型,如果数据类型不符合,自然就会报错,这种报错也是相对容易理解的,根据这种特性产生的报错注入有updatexml,extractvalue等注入手法

  • 基于BIGINT溢出错误的SQL注入,根据超出最大整数溢出产生的错误,这类报错注入是在mysql5.5.5版本后才产生的,5.5.5版本前并不会因为整数溢出而报错,这种注入自己在phpstudy上试了试,mysql版本为5.5.53,虽然报错了但是并没有爆出信息,以后研究出来再补充

  • 其他报错,企业级代码审计这本书上看到的,一些mysql空间函数geometrycollection(),multipoint(),polygon(),multipolygon(),linestring(),multilinestring(),通过这些报错会产生Illegal non geometric的错误,里面同时包含了我们构造查询语句的信息

原理分析

接下来对上面列出的一些报错注入一个个进行分析

基于主键值重复

floor(rand(0)*2):我们在进行报错注入时用的相对较多的方法,网上给出的报错语句大部分是这样的

id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

看到这是不是有点云里雾里的感觉呢,没关系,我也因为这个语句纠结了一段时间,比如为什么要floor(rand(0)*2),为什么要用到information_schema.tables这个表,接下来我们就把它彻底弄明白,先看rand()这个函数,这个函数都知道是产生随机数的,但是当rand(0)被计算多次时它所产生的值是有规律的,我们以information_schema.tables这个表进行示范,因为它里面的数据多,别的表也可以,只要数据量够多,这样可以使rand(0)计算多次,便于观察,为了更便于观察,我们取前30条记录,查询语句

mysql> select rand(0) from information_schema.tables limit 0,30;

+---------------------+
| rand(0)             |
+---------------------+
| 0.15522042769493574 |
|   0.620881741513388 |
|  0.6387474552157777 |
| 0.33109208227236947 |
|  0.7392180764481594 |
|  0.7028141661573334 |
|  0.2964166321758336 |
|  0.3736406931408129 |
|  0.9789535999102086 |
|  0.7738459508622493 |
|  0.9323689853142658 |
|  0.3403071047182261 |
|  0.9044285983819781 |
|   0.501221708488857 |
|  0.7928227780319962 |
|  0.4604487954270549 |
|  0.9237756737729308 |
| 0.23753201331713425 |
|  0.4163330760005238 |
|  0.3690693707848614 |
|  0.5963476566563805 |
|   0.874530201660963 |
|  0.5836080690693185 |
| 0.29444977109734877 |
|  0.7214246790124333 |
|  0.7237741125037652 |
|  0.4545965562151713 |
| 0.10166047429820567 |
| 0.14451273357915947 |
|  0.4175822757348253 |
+---------------------+
30 rows in set (0.05 sec)

通过多次测试观察可以发现规律,每次执行sql语句多次计算rand(0)时,rand(0)产生的值是总是固定的,不管执行多少次语句,多次计算的rand(0)的前30条总是和上面得计算结果一样,那么可以做出结论之后的结果也总是一样,观察上述计算结果,看似杂乱的数值其实都有着一个范围界限,那就是0~0.5,0.5~1,我们将rand(0)*2再观察一下

mysql> select rand(0)*2 from information_schema.columns limit 0,30;

+---------------------+
| rand(0)*2           |
+---------------------+
|  0.3104408553898715 |
|   1.241763483026776 |
|  1.2774949104315554 |
|  0.6621841645447389 |
|  1.4784361528963188 |
|  1.4056283323146668 |
|  0.5928332643516672 |
|  0.7472813862816258 |
|  1.9579071998204172 |
|  1.5476919017244986 |
|  1.8647379706285316 |
|  0.6806142094364522 |
|  1.8088571967639562 |
|   1.002443416977714 |
|  1.5856455560639924 |
|  0.9208975908541098 |
|  1.8475513475458616 |
|  0.4750640266342685 |
|  0.8326661520010477 |
|  0.7381387415697228 |
|   1.192695313312761 |
|   1.749060403321926 |
|   1.167216138138637 |
|  0.5888995421946975 |
|  1.4428493580248667 |
|  1.4475482250075304 |
|  0.9091931124303426 |
| 0.20332094859641134 |
| 0.28902546715831895 |
|  0.8351645514696506 |
+---------------------+
30 rows in set (0.69 sec)

此时界限分隔值变成了1,数值都是零点几的小数和一点几的小数,于是用floor处理一下

mysql> select floor(rand(0)*2) from information_schema.columns limit 0,30;

+------------------+
| floor(rand(0)*2) |
+------------------+
|                0 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                0 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
|                0 |
|                0 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                0 |
|                0 |
|                0 |
|                0 |
+------------------+

于是就有了floor(rand(0)2),同样的,每次执行多次floor(rand(0)2),所得的结果也总是这样固定,我们只看前6个数字,总是0,1,1,0,1,1这样的顺序,后面的数也是如此有着自己的顺序

接下来我们构造一个sql语句,它可以报出Duplicate的错误

mysql> select count(*) from information_schema.tables group by floor(rand(0)*2);

ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'

分析这条语句,当进行count(),group by聚合函数分组计算时,mysql会创建一个虚拟表,虚拟表由主键列和count()列两列组成,同时floor(rand(0)2)这个值会被计算多次,这一点很重要,计算多次是指在取数据表数据使用group by时,进行一次floor(rand(0)2),如果虚拟表中不存在此数据时,那么在往虚拟表插入数据时,floor(rand(0)2)将会再被计算一次,接下来分析,取数据表第一条记录时第一次使用group by,计算floor(rand(0)2)的值为0,查询虚拟表发现0这个主键不存在,于是再次计算floor(rand(0)2)结果为1,将1作为主键插入虚拟表,这时主键1的count()值为1,接下来取数据表第二条记录时第二次使用group by,计算floor(rand(0)2),结果为1,然后查询虚拟表,发现1的键值存在,于是count()的值加1,取数据表第三条记录时第三次使用group by,计算floor(rand(0)2)值为0,查询虚拟表,发现0的键值不存在,于是再一次计算floor(rand(0)2),结果为1,当尝试将1插入虚拟表中时,发现主键1已经存在,所以报出主键重复的错误,整个过程中查询了information_schema.tables这个表3条记录发生报错,这也是报错为什么需要数据表的记录多到至少为3条的原因,也是为什么选择information_schema.tables表的原因,因为这个表中的记录一定大于三条,由此可知我们其实还以选择information_schema.columns,information_schema.schemata等表

下面构造语句

mysql> select count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x3a,user());
ERROR 1062 (23000): Duplicate entry '1:root@localhost' for key 'group_key'

是不是看着很眼熟,没错,这就是我们在开头给出的那个复杂的语句,只不过开头的那个加了个子查询,其实and后的括号里直接写这个语句也能达到一样的效果

mysql> select * from user where id=1 and (select count(*) from information_schema.tables group by concat(floor(rand(0)*2),0x3a,user()));
ERROR 1062 (23000): Duplicate entry '1:root@localhost' for key 'group_key'

把user()换成其他查询语句,就可以注入出别的数据

基于数据类型的不一致

updatexml(1,XPATH,1)函数的第二个参数要求为XPATH格式,如果我们把它改为字符串格式,那么就会爆出XPATH syntax error的错误,于是构造sql语句

mysql> select * from user where id=1 and updatexml(1,(concat(1,user())),1);
ERROR 1105 (HY000): XPATH syntax error: 'root@localhost'

利用concat函数返回字符串产生报错,同样的函数还有extractvalue(1,XPATH)

mysql> select * from user where id=1 and extractvalue(1,(concat(1,user())));
ERROR 1105 (HY000): XPATH syntax error: 'root@localhost'

Mysql表的数据达到千万级别解决思路

在大神的指导下,我总结如下:

1、建立合适的索引

所谓的合适的索引包括很多内容,比如一开始对于where条件里面的sql语句要建立索引,这样才能查询速度变快,至于建立什么样子的索引根据业务自己建立,这这要说下注意点。比如sql里面语句有like,那like的字段就要建立全文索引,sql语句里面‘%’所在的位置因位置不同而起的作用也不同,这个可以问问度娘。当时由于我的where条件比较多,就没有使用联合索引,网上说这个索引在where条件里面效果非常好。

2、优化sql语句

其实操作单表sql语句并不复杂,但这里面所涉及到的知识点也是很多,sql能根据id的最好能依靠id,因为id一般为主键索引,他的查询速度很快。这里可以用mysql里面的explain来查看sql到底性能怎么样,具体可以看这个http://www.fang99.cc/index.php/Home/Article/detail/id/3380.html。

3、分页

其实分页当时我也做了,这个也要有。因为数据大,不可能展示所有的数据给用户看,没这个必要。另外在拼接分页的sql语句能走id最好。

4、数据库容量

网上查资料说mysql的存储数据能达到千万级别,但是我在实际运用中并没有做到大神们的那个样子。后来看了下别人的同类产品,他们直接对数据库的容量定量了,比如只存储一百万数据,超过则提醒用户,并且每更新一条数据就覆盖最后一条数据。

以上仅个人看法,如有不对的地方,希望大神门指导下!

利用MySQL全备份,如何只恢复一个库或者一个表?

未分类

在实际工作中,一个MySQL实例中可能有多个database。而我们备份时,通常采用完全备份,将所有database都备份到一个文件中。

但是,偶尔会遇到只恢复一个database或者一个表的情况。怎么解决呢?

一、利用全备恢复一个库(database)的数据

案例:朋友在群里问, MySQL全库备份。如何只恢复一个库?

1、采用–one-database 选项。不建议使用该方式,经常出现问题。

# mysql -uroot -pxx -D db1 -o 

2、从全备份文件中将需要的库的建表语句和INSERT数据拿出来,然后再导入

# sed -n '/^-- Current Database: `db1`/,/^-- Current Database: `/p' all.dmp > db1.sql 

# mysql -uroot -pxx -D db1 

二、利用全备恢复一张表(table)的数据

生产中遇到开发同事更新表时未加上where条件,造称 order_status 列所有数据都被更新为0.

通常,mysqldump对全库进行备份。恢复时如果恢复全库,太耗时间又没有必要。所以,我们只恢复想要的表即可。

mysqldump备份中,既有表结构,又有INSERT INTO语句包含数据。所以获得者两部分内容即可。

1、获得表结构

# sed -e'/./{H;$!d;}' -e 'x;/CREATE TABLE `ecs_ugo_order_info`/!d;q' mysqldump_2017-05-23.sql 

2、获得INSERT INTO 语句,用于数据的恢复

# grep -i 'INSERT INTO `ecs_ugo_order_info`' mysqldump_2017-05-23.sql >data.sql & 

3、根据得到的表结构创建表,并导入数据

mysql -uroot -pxxx xx 

4、拼接update语句

mysql> select concat('update xx.ecs_ugo_order_info set order_status=',order_status,' where order_id=',order_id,';') from ecs_ugo_order_info into outfile '/tmp/ecs_ugo_order_info_rercovery.sql';  

结果如下:

update xx.ecs_ugo_order_info set order_status=6 where order_id=3254778; 

update xx.ecs_ugo_order_info set order_status=6 where order_id=3254824; 

update xx.ecs_ugo_order_info set order_status=6 where order_id=3254870; 

5、在生产库中将order_status恢复成正常值

# mysql -uroot -pxxx xx < /tmp/ecs_ugo_order_info_rercovery.sql

如何配置MySQL数据库超时设置

最近备战京东双11,在配置MySQL的超时配置发现有很多地方可以设置。这么多超时的配置有什么影响,以及配置会有什么影响呢?今天的文章就让我来大家来分析一下。

1. JDBC超时设置

connectTimeout:表示等待和MySQL数据库建立socket链接的超时时间,默认值0,表示不设置超时,单位毫秒,建议30000

socketTimeout:表示客户端和MySQL数据库建立socket后,读写socket时的等待的超时时间,linux系统默认的socketTimeout为30分钟,可以不设置

2. 连接池超时设置

maxWait:表示从数据库连接池取链接,连接池没有可用连接时的等待时间,默认值0,表示无限等待,单位毫秒,建议60000

未分类

3. MyBatis查询超时

defaultStatementTimeout:表示在MyBatis配置文件中默认查询超时间,单位秒,不设置则无线等待

未分类

如果一些sql需要执行超过defaultStatementTimeout可以通过Mapper文件单独的sql的timeout进行配置

未分类

4. 事务超时

事务超时用于控制事务执行的超时,执行时间是事务内所有代码执行总和,单位为秒。

未分类

总结

高级别的timeout依赖于低级别的timeout,只有当低级别的timeout无误时,高级别的timeout才能确保正常。例如,当socket timeout出现问题时,高级别的statement timeout和transaction timeout都将失效。

1. Transaction Timeout

Spring提供的transaction timeout配置非常简单,它会记录每个事务的开始时间和消耗时间,当特定的事件发生时就会对消耗时间做校验,当超出timeout值时将抛出异常。

假设某个事务中包含5个statement,每个statement的执行时间是200ms,其他业务逻辑的执行时间是100ms,那么transaction timeout至少应该设置为1,100ms(200 * 5 + 100)。

2. Statement Timeout

statement timeout用来限制statement的执行时长,timeout的值通过调用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API进行设置。不过现在开发者已经很少直接在代码中设置,而多是通过框架来进行设置。

在iBatis中,statement timeout的默认值可以通过sql-map-config.xml中的defaultStatementTimeout 属性进行设置。同时,你还可以设置sqlmap中select,insert,update标签的timeout属性,从而对不同sql语句的超时时间进行独立的配置。

3. Socket timeout

JDBC的socket timeout在数据库被突然停掉或是发生网络错误(由于设备故障等原因)时十分重要。由于TCP/IP的结构原因,socket没有办法探测到网络错误,因此应用也无法主动发现数据库连接断开。如果没有设置socket timeout的话,应用在数据库返回结果前会无期限地等下去,这种连接被称为dead connection。

为了避免dead connections,socket必须要有超时配置。socket timeout可以通过JDBC设置,socket timeout能够避免应用在发生网络错误时产生无休止等待的情况,缩短服务失效的时间。

不推荐使用socket timeout来限制statement的执行时长,因此socket timeout的值必须要高于statement timeout,否则,socket timeout将会先生效,这样statement timeout就变得毫无意义,也无法生效。

mysql 查看索引、添加索引、删除索引命令

查看索引

mysql> show index from tblname;
mysql> show keys from tblname;


mysql> show index from center_bank_rate;
+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table            | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| center_bank_rate |          0 | PRIMARY  |            1 | id          | A         |           8 |     NULL | NULL   |      | BTREE      |         |               |
+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

· Table
表的名称。

· Non_unique
如果索引不能包括重复词,则为0。如果可以,则为1。

· Key_name
索引的名称。

· Seq_in_index
索引中的列序列号,从1开始。

· Column_name
列名称。

· Collation
列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。

· Cardinality
索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机 会就越大。

· Sub_part
如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。

· Packed
指示关键字如何被压缩。如果没有被压缩,则为NULL。

· Null
如果列含有NULL,则含有YES。如果没有,则该列含有NO。

· Index_type
用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。

· Comment

添加索引

ALTER TABLE Persons
ADD CONSTRAINT uc_PersonID UNIQUE (Id_P,LastName)

删除索引

mysql> alter table center_bank_rate drop index UK_r60biiucgoujcw6htlywu02bg;

Query OK, 8 rows affected (0.11 sec)
Records: 8  Duplicates: 0  Warnings: 0

读懂MySQL执行计划

前言

在之前的面试过程中,问到执行计划,有很多童鞋不知道是什么?甚至将执行计划与执行时间认为是同一个概念。今天我们就一起来了解一下执行计划到底是什么?有什么用途?

执行计划是什么?

执行计划,简单的来说,是SQL在数据库中执行时的表现情况,通常用于SQL性能分析,优化等场景。在MySQL使用 explain 关键字来查看SQL的执行计划。如下所示:

//1. 查询t_base_user
select * from t_base_user where name="andyqian";

//2. 查看上述语句的执行计划
explain select * from t_base_user where name="andyqian";

执行查看上述2语句后,我们可以得出以下执行计划结果

id | select_type|table|type|possible_kes|key|key_len|ref|rows|Extra
—|—|—|—|—|—|—|—|—|—
1|SIMPLE|t_base_user|ALL| | | |1|Using where

上面执行计划是什么意思呢?有什么参考价值呢?

上面这个执行计划给到的信息是: 这个结果通过一个简单的语句全表扫描,共扫描1行,使用where条件在t_base_user表中筛选出的。发现该语句并没有走索引,为什么是这样的呢?别急,我们紧接着看下一节。

读懂执行计划

通过上面,我们知道了什么是执行计划,也看到了执行计划到底是什么东西,现在我们来具体了解一下,MySQL执行计划中,每个属性代表的是什么意思?

id  select_type table   type    possible_kes    key        key_len      ref    rows    Extra

我们一一来介绍,并说明每个属性有哪些可选值,以及每个可选值的意思。

  • id
    表示查询中select操作表的顺序,按顺序从大到依次执行

  • select_type :
    该表示选择的类型,可选值有: SIMPLE(简单的),

  • type :

该属性表示访问类型,有很多种访问类型。
最常见的其中包括以下几种: ALL(全表扫描), index(索引扫描),range(范围扫描),ref (非唯一索引扫描),eq_ref(唯一索引扫描,),(const)常数引用, 访问速度依次由慢到快。

其中 : range(范围)常见与 between and …, 大于 and 小于这种情况。
提示 : 慢SQL是否走索引,走了什么索引,也就可以通过该属性查看了。

  • table :
    表示该语句查询的表

  • possible_keys :
    顾名思义,该属性给出了,该查询语句,可能走的索引,(如某些字段上索引的名字)这里提供的只是参考,而不是实际走的索引,也就导致会有possible_Keys不为null,key为空的现象。

  • key :
    显示MySQL实际使用的索引,其中就包括主键索引(PRIMARY),或者自建索引的名字。

  • key_len :
    表示索引所使用的字节数,

  • ref :
    连接匹配条件,如果走主键索引的话,该值为: const, 全表扫描的话,为null值

  • rows :
    扫描行数,也就是说,需要扫描多少行,采能获取目标行数,一般情况下会大于返回行数。通常情况下,rows越小,效率越高, 也就有大部分SQL优化,都是在减少这个值的大小。

注意: 理想情况下扫描的行数与实际返回行数理论上是一致的,但这种情况及其少,如关联查询,扫描的行数就会比返回行数大大增加)

  • Extra
    这个属性非常重要,该属性中包括执行SQL时的真实情况信息,如上面所属,使用到的是”using where”,表示使用where筛选得到的值,常用的有:
    “Using temporary”: 使用临时表 “using filesort”: 使用文件排序

看到这里,我们应该已经发现,在第一步中,我们的这条SQL

select * from t_base_user where name="andyqian";

是没有走索引的,而且还是全表扫描,在数据量少的情况下,问题还不会特别突出,如果数据量比较大,这可是个会造成生产事故的慢查询哦,现在我们改造一下,将name字段添加上索引,

# 添加索引
alter table t_base_user add index idx_name(name);

看看它的执行计划是怎样的。

id | select_type|table|type|possible_kes|key|key_len|ref|rows|Extra
—|—|—|—|—|—|—|—|—|—
1|SIMPLE|t_base_user|ref|idx_name|idx_name|93|cons|1|Using where

你看,现在已经走idx_name索引了,其type从All(全表扫描)到ref(非唯一索引了),别看就只有这一点点小区别,在大数据量的时候,可是会起大作用的哦。

数据结

本文中演示的数据结构如下:

# 创建表  
create table t_base_user(
oid bigint(20) not null primary key auto_increment,
name varchar(30) null comment "name",
email varchar(30) null comment "email",
age int null comment "age",
telephone varchar(30) null comment "telephone",
status tinyint(4) null comment "0  无效 1 有效",
created_at datetime null comment "",
updated_at datetime null comment ""
)

## 新增记录:
insert into t_base_user(name,email,age,telephone,created_at,updated_at)values("andyqian","[email protected]",20,"15608411",now(),now());
)

最后

一个好的数据库表设计,从一开始就应该考虑添加索引,而不是到最后发现慢SQL了,影响业务了,才来补救。其实我在工作经历当中,由于新建表,或新加字段后,忘记添加索引也造成了多次生产事故,记忆犹新!!!
其实新建索引也是有一定的原则的,建什么索引,建在哪些字段上,这里面还有不少知识呢,下一篇文章写,尽请期待吧!