什么是MySql数据库
MySql数据库是开放源代码的关系型数据库。目前,它可以提供的功能有:支持sql语言、子查询、存储过程、触发器、视图、索引、事务、锁、外键约束和影像复制等。同Oracle 和SQL Server等大型数据库系统一样,MySql也是客户/服务器系统并且是单进程多线程架构的数据库。并且MySQL是一个真正的多用户、多线程SQL数据库服务器。MySQL的客户机/服务器结构由一个服务器守护程序mysql和很多不同的客户程序和库组成。由于其源码的开放性及稳定性,且与网站流行编徎语言PHP的完美结合,现在很多站点都利用其当作后端数据库,使其获得了广泛应用。MySql区别于其它数据库系统的一个重要特点是支持插入式存储引擎。
那么什么是存储引擎呢?
存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)。在Oracle 和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的。而MySql数据库提供了多种存储引擎。用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据自己的需要编写自己的存储引擎。
MySql中有哪些存储引擎?
1. MyISAM:这种引擎是MySql最早提供的, 这种引擎又可以分为静态MyISAM、动态MyISAM 和压缩MyISAM三种:
静态MyISAM:如果数据表中的各数据列的长度都是预先固定好的,服务器将自动选择这种表类型。因为数据表中每一条记录所占用的空间都是一样的,所以这种表存取和更新的效率非常高。当数据受损时,恢复工作也比较容易做。
动态MyISAM:如果数据表中出现varchar、xxxtext或xxxBLOB字段时,服务器将自动选择这种表类型。相对于静态MyISAM,这种表存储空间比较小,但由于每条记录的长度不一,所以多次修改数据后,数据表中的数据就可能离散的存储在内存中,进而导致执行效率下降。同时,内存中也可能会出现很多碎片。因此,这种类型的表要经常用optimize table命令或优化工具来进行碎片整理。
压缩MyISAM:以上说到的两种类型的表都可以用myisamchk工具压缩。这种类型的表进一步减小了占用的存储,但是这种表压缩之后不能再被修改。另外,因为是压缩数据,所以这种表在读取的时候要先时行解压缩。
但是,不管是何种MyISAM表,目前它都不支持事务,行级锁和外键约束的功能。
2 MyISAM Merge引擎:这种类型是MyISAM类型的一种变种。合并表是将几个相同的MyISAM表合并为一个虚表。常应用于日志和数据仓库。
3 InnoDB:InnoDB表类型可以看作是对MyISAM的进一步更新产品,它提供了事务、行级锁机制和外键约束的功能。
4 memory(heap):这种类型的数据表只存在于内存中。它使用散列索引,所以数据的存取速度非常快。因为是存在于内存中,所以这种类型常应用于临时表中。
5 archive:这种类型只支持select 和 insert语句,而且不支持索引。常应用于日志记录和聚合分析方面。
当然MySql支持的表类型不止上面几种。 下面介绍如何查看和设置数据表类型。
MySql中关于存储引擎的操作:
1查看数据库可以支持的存储引擎:show engines,默认数据表类型是MyISAM。当然,我们可以通过修改数据库配置文件中的选项,设定默认表类型。
2.通过显示表的创建语句可以查看该表的Engine, Showcreate table tablename;
3 设置或修改表的存储引擎,在创建表的语句上加上engine=engineName即可,如:
- create table user(
- id intnot null auto_increment,
- usernamechar(20) not null,
- sexchar(2),
- primarykey(id)
- ) engine=merge
而修改存储引擎,使用alter命令,很容易想到,因为Engine是在创建表的时候定好的,顾使用alter来修改,可以用命令Altertable tableName engine =engineName
假如,若需要将表user的存储问引擎修改为archive类型,则可使用命令alter table userengine=archive。
聚集索引
聚集索引确定表中数据的物理顺序。聚集索引类似于电话簿,后者按姓氏排列数据。由于聚集索引规定数据在表中的物理存储顺序,因此一个表只能包含一个聚集索引。但该索引可以包含多个列(组合索引),就像电话簿按姓氏和名字进行组织一样。
聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行后,便可以确保包含后续索引值的行在物理相邻。例如,如果应用程序执行 的一个查询经常检索某一日期范围内的记录,则使用聚集索引可以迅速找到包含开始日期的行,然后检索表中所有相邻的行,直到到达结束日期。这样有助于提高此类查询的性能。同样,如果对从表中检索的数据进行排序时经常要用到某一列,则可以将该表在该列上聚集(物理排序),避免每次查询该列时都进行排序,从而节 省成本。 当索引值唯一时,使用聚集索引查找特定的行也很有效率。例如,使用唯一雇员 ID 列 emp_id 查找特定雇员的最快速的方法,是在 emp_id 列上创建聚集索引或 PRIMARY KEY 约束
My SQL常用目录及系统命令
1、数据库默认目录 /var/lib/mysql/,为啥放在这个地方?一般var下面是最大的目录,数据文件也存储在这个地方,如果要想修改数据文件目录,要配置文件进行修改,有几个地方要进行修改 (1)在配置文件中mysql.sock文件产生的位置,mysql.sock文件的产生位置,如socket = /home/data/mysql/mysql.sock (2)修改mysql的启动脚本中的datadir:init.d/myql:datadir=/home/data/mysql需要注意的有要将默认路径下的data文件copy到新目录而不是新建,还有要将Mysql的一份配置文件copy到/etc/下面,对sock位置的修改要在改文件下修改,最后在修改前停止mysql, 修改完进行生效重启Mysql.
2、配置文件 /usr/share/mysql(mysql.server命令及配置文件),如/usr/share/mysql/my-medium.cnf
3、相关命令 /usr/bin(mysqladminmysqldump等命令) 命令在/usr/bin下
4、启动脚本 /etc/rc.d/init.d/mysql(启动脚本文件mysql的目录)
5、自动启动:只要记住关于自动启动的都在/sbin/chkconfig就可以了
1)察看mysql是否在自动启动列表中 [root@test1local]# /sbin/chkconfig –list
2)把MySQL添加到系统启动服务组里面去 [root@test1local]# /sbin/chkconfig –add mysql
3)把MySQL从启动服务组里面删除。[root@test1 local]# /sbin/chkconfig –del mysql
6、停掉与启动Mysql命令:mysqladmin -u root -pshutdown 和 /etc/rc.d/init.d/mysql start
Sql命令中的几个关键字
(1) Group by使用, 比如delete from tablename where id not in (select max(id) from tablenamegroup by col1,col2,...) 《= 删除重复数据的例子, Group By的含义是进行分组值相同的为一组,注意使用Group By 之后 Select 出来的列必须只能来自于group By 或使用聚合函数Sum, AVG, max, min等中的列:返回集字段中,这些字段要么就要包含在Group By语句的后面,作为分组的依据;要么就要被包含在聚合函数中,GroupBy与聚合函数一块使用,表示对分组后的数据进行求和,平均,最大值等。
(2) Group By 和 Having, Where ,Order by语句的执行顺序:以尽可能的减少操纵的数据为原则,显然顺序为:Where, Group By, Having, Order by,首先where将最原始记录中不满足条件的记录筛选,减少分组数据,然后Group By与Having显然是Having是基于Groupby的,顾Having在Groupby之后,而Order By在Groupby和where之前都没用用,只有在Groupby之后才起作用,顾最后。
(3) Having 的使用:Having对Group By进行限定条件,可以使用聚合函数和Groupby中的列。
(4) Left outer join 与 left inner join的区别:外连接可以包含主表的全部行,而内连接只含有匹配那行
(5) Distinct 用法,去掉重复行,类似于Linux命令中的uniq,但uniq命令只能去掉相邻的重复行,所以在用之前要进行sort
(6) SQL Sever中用法: sysobjects和syscolumns表放了数据库中的所有表名和一个表的列名,而在My SQL中使用:show tables
MySql命令
(1) Mysql的所有用户都在user表中,可以通过该表进行,增加用户,修改用户密码,删除用户,和赋予用户权限等,注意修改后要使用FLUSH PRIVILEGES进行确认,如下:
删除匿名用户:delete from User where User=""; 匿名用户的User是空
增加一个用户 INSERT INTO mysql.user (Host,User,Password) VALUES ('%','system', PASSWORD('manager'));
修改密码update User set Password=PASSWORD(newpassword) where User=root;
(1) 用Grant 命令授权,输入的代码如下:
grant select,insert,update on mydb.*to NewUserName@HostNameidentified by"password"; 如果对HostName不限制,则使用%
(2) 对用户的每一项权限进行设置: 如:
mysql>INSERT INTO user VALUES('localhost','system',PASSWORD('manager'), 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y'); 对于3.22.34版本的MySQL,这里共14个"Y",其相应的权限如下(按字段顺序排列):
权限
表列名称
相应解释
使用范围
select
Select_priv
只有在真正从一个表中检索时才需要select权限
表
insert
Insert_priv
允许您把新行插入到一个存在的表中
表
update
Update_priv
允许你用新值更新现存表中行的列
表
delete
Delete_priv
允许你删除满足条件的行
表
create
Create_priv
允许你创建新的数据库和表
数据库、表或索引
drop
Drop_priv
抛弃(删除)现存的数据库和表
数据库或表
reload
Reload_priv
允许您告诉服务器再读入授权表
服务器管理
shutdown
Shutdown_priv
可能被滥用(通过终止服务器拒绝为其他用户服务)
服务器管理
process
Process_priv
允许您察看当前执行的查询的普通文本,包括设定或改变口令查询
服务器管理
file
File_priv
权限可以被滥用在服务器上读取任何可读的文件到数据库表
服务器上文件存取
grant
Grant_priv
允许你把你自己拥有的那些权限授给其他的用户
数据库或表
references
References_priv
允许你打开和关闭记录文件
数据库或表
index
Index_priv
允许你创建或抛弃(删除)索引
表
alter
Alter_priv
允许您改变表格,可以用于通过重新命名表来推翻权限系统
表
对上面的14中权限,可以分为两种全局管理权限和数据表权限
全局管理权限:
FILE: 在MySQL服务器上读写文件。
PROCESS: 显示或杀死属于其它用户的服务线程。
RELOAD: 重载访问控制表,刷新日志等。
SHUTDOWN: 关闭MySQL服务。
数据库/数据表/数据列权限:
ALTER: 修改已存在的数据表(例如增加/删除列)和索引。
CREATE: 建立新的数据库或数据表。
DELETE: 删除表的记录。
DROP: 删除数据表或数据库。
INDEX: 建立或删除索引。
INSERT: 增加表的记录。
SELECT: 显示/搜索表的记录。
UPDATE: 修改表中已存在的记录。
特别的权限:
ALL: 允许做任何事(和root一样)。
USAGE: 只允许登录--其它什么也不允许做。
(2) 查看所有用show命令,查看当前用select函数
查看所有的数据库Show databases
创建和删除数据库create/drop database name
使用或连接数据库use databasename(),
查看当前使用的数据库:select database()
查询当前时间:selectnow()
查询当前用户:select user()
查询数据库版本:select version()
查看所有表:show tables
show processlist;列出每一笔联机的信息。
show variables;列出mysql的系统设定。
show tables from db_name;列出db_name中所有数据表;
show [full] columns from table_name;列出table_name中完整信息,如栏名、类型,包括字符集编码。
show index from table_name; 列出table_name中所有的索引。
show table status;;列出当前数据库中数据表的信息。
show table status from db_name;;列出当前db_name中数据表的信息。
alter table table_name engine innodb|myisam|memory ;更改表类型
explain table_name / describe table_name ; 列出table_name完整信息,如栏名、类型。
show create table table_name 显示当前表的建表语句
alter table table_name add primary key (picid) ; 向表中增加一个主键
alter table table_name add column userid int after picid 修改表结构增加一个新的字段
alter table table_name character set gb2312 改变表的编码
select user(); 显示当前用户。
select password(’root’); 显示当前用户密码
select now(); 显示当前日期
flush privileges 在不重启的情况下刷新用户权限
mysqld –default-character-set=gb2312 ;设置默认字符集为gb2312
(3) 表操作
备注:操作之前使用“use<数据库名>”应连接某个数据库。
匹配字符:可以用通配符_代表任何一个字符,%代表任何字符串;
联合字符或者多个列(将列id与":"和列name和"="连接)
select concat(id,':',name,'=') fromstudents;
limit(选出10到20条)<第一个记录集的编号是0>,SQLServer上为Top
select * from students order by id limit 9,10;
建表:create table <表名> (<字段名 1> <类型 1> [,..<字段名 n><类型 n>]);
插入数据:insert into <表名> [( <字段名 1>[,..<字段名 n >])] values ( 值 1 )[, ( 值 n )]
获取表结构:describe tablename, desc tablename, show columns from tablename
删除表 drop table tablename
删除表中数据 命令:delete from 表名 where 表达式
修改表中数据 命令:update 表名 set 字段=新值,... where 条件
在表中增加字段 命令:alter table 表名 add 字段 类型 其他;
更改表名 命令:rename table 原表名 to 新表名
更新字段内容 命令:update 表名 set 字段名 = 新内容,
删除student_course数据库中的students数据表:rm-f student_course/students.* 直接使
用linux命令来删除,也可以使用drop命令
创建临时表:create temporary table zengchao(name varchar(10));
创建表是先判断表是否存在: create table ifnot exists students(……);
从已经有的表中复制表的结构: create table table2 select * from table1 where 1<>1;
复制表,复制表的时候也会将表结构也复制过去了:create table table2 select * from table1;
对表重新命名:altertable table1 rename as table2;
创建索引,创建索引有两种方式,alter原来的表结构然后add,或者create index on
altertable table1 add index ind_id (id);
createindex ind_id on table1 (id);
createunique index ind_id on table1 (id);//建立唯一性索引
删除索引drop index idx_id on table1;
alter table table1 dropindex ind_id;
可以使用ALTER TABLE语句来更新与属性或表有关的约束。关于修改索引也是用类似的命令
删除约束:ALTER TABLEDROP CONSTRAINT约束名
增加约束ALTER TABLEADD CONSTRAINT约束名约束定义
(4) 数据库导入导出
从数据库导出数据库文件, 使用“mysqldump”命令
- 1)导出数据库: mysqldump -u [用户名] –p [数据库名] -A>[备份文件的保存路径],如
- mysqldump -h localhost -u root -p mydb >e:\MySQL\mydb.sql
- 2)导出数据和数据结构:mysqldump -u [用户名] -p [数据名 表名>[备份文件的保存路径]
- mysqldump -h localhost -u root -p mydb mytable>e:\MySQL\mytable.sql :数据表
- mysqldump -h localhost -u root -p mydb --add-drop-table >e:\MySQL\mydb_stru.sql:数据库结构
- 3)只导出数据不导出数据结构:mysqldump -u [用户名] -p -t [数据库名]>[备份文件的保存路径]
- 4)导出数据库中的Events: mysqldump -u [用户名] -p -E [数据库名]>[备份文件的保存路径]
- 5)导出数据库中的存储过程和函数:mysqldump -u [用户名] -p -R [数据库名]>[备份文件的保存路径]
从外部文件导入数据库中
- 1)使用“source”命令:source [备份文件的保存路径] 这个Source有点类似shell的source命令
- 2)使用“<”符号:mysql -u root –p < [备份文件的保存路径]
- 3)用文本方式将数据装入数据库表中(例如D:/mysql.txt)
- mysql> LOAD DATA LOCAL INFILE "D:/mysql.txt" INTO TABLE MYTABLE;
MySQL命令应用:
(1) mysql之删除重复数据,删除id重复的数据
delete person as a from person as a,
(
select *,min(id) from person group by id having count(1)> 1
) as b
where a.id = b.id
(2) 查找重复的,并且除掉最小的那个
delete tb_person as a from tb_person as a,
(
select *,min(id) from tb_person group by name having count(1) > 1
) as b
where a.name = b.name and a.id > b.id;
MySql explain优化SQL语句
在mysql version 4.1中,explain输出的结果格式改变了,使得它更适合例如 union语句、子查询以及派生表的结构。更令人注意的是,它新增了2个字段: id和 select_type。当你使用早于mysql4.1的版本就看不到这些字段了。
explain结果的每行记录显示了每个表的相关信息,每行记录都包含以下几个字段:
(1) id : 本次 select 的标识符。在查询中每个 select都有一个顺序的数值。
(2) select_type:select 的类型,可能会有以下几种:
- A. simple: 简单的 select (没有使用 union或子查询)
- B. primary: 最外层的 select。
- C. union: 第二层,在select 之后使用了 union。
- D. dependent union: union 语句中的第二个select,依赖于外部子查询
- E. subquery: 子查询中的第一个 select
- F. dependent subquery: 子查询中的第一个 subquery依赖于外部的子查询
- G. derived: 派生表 select(from子句中的子查询)
(3) table:记录查询引用的表。
(4) type:表连接类型。以下列出了各种不同类型的表连接,依次是从最好的到最差的:
- A. system: 表只有一行记录(等于系统表)。这是 const表连接类型的一个特例。
- B. const: 表中最多只有一行匹配的记录,它在查询一开始的时候就会被读取出来。由于只有一行记录,在余下的优化程序里该行记录的字段值可以被当作是一个恒定值。const表查询起来非常快,因为只要读取一次!const用于在和 primary key或unique索引中有固定值比较的情形。下面的几个查询中,tbl_name 就是 c表了:select * from tbl_name where primary_key=1; select * from tbl_name whereprimary_key_part1=1 and primary_key_part2=2;
- C. eq_ref: 从该表中会有一行记录被读取出来以和从前一个表中读取出来的记录做联合。与const类型不同的是,这是最好的连接类型。它用在索引所有部分都用于做连接并且这个索引是一个primary key或 unique类型。eq_ref可以用于在进行"="做比较时检索字段。比较的值可以是固定值或者是表达式,表达式中可以使用表里的字段,它们在读表之前已经准备好 了。以下的两个例子中,mysql使用了eq_ref 连接来处理 ref_table:(1)select * from ref_table,other_table where ref_table.key_column=other_table.column;(2)select * from ref_table,other_tablewhere ref_table.key_column_part1=other_table.column and ref_table.key_column_part2=1;
- D. ref: 该表中所有符合检索值的记录都会被取出来和从上一个表中取出来的记录作联合。ref用于连接程序使用键的最左前缀或者是该键不是primary key或 unique索引(换句话说,就是连接程序无法根据键值只取得一条记录)的情况。当根据键值只查询到少数几条匹配的记录时,这就是一个不错的连接类型。ref还可以用于检索字段使用 =操作符来比较的时候。以下的几个例子中,mysql将使用 ref 来处理ref_table:(1) select * from ref_table wherekey_column=expr; (2) select * from ref_table,other_table where ref_table.key_column=other_table.column; (3) select * fromref_table,other_tablewhereref_table.key_column_part1=other_table.columnandref_table.key_column_part2=1;
- E. ref_or_null: 这种连接类型类似 ref,不同的是mysql会在检索的时候额外的搜索包含null值的记录。这种连接类型的优化是从mysql4.1.1开始的,它经常用于子查询。在以下的例子中,mysql使用ref_or_null 类型来处理 ref_table:select * from ref_table wherekey_column=expr or key_column is null;
- F. unique_subquery: 只是用来完全替换子查询的索引查找函数效率更高了,这种类型用例如一下形式的 in 子查询来替换ref:value in(select primary_key from single_table where some_expr)
- G. Index_subquery: 这种连接类型类似 unique_subquery。它用子查询来代替in,不过它用于在子查询中没有唯一索引的情况下,例如以下形式:value in (select key_column from single_table where some_expr)
- H. range: 只有在给定范围的记录才会被取出来,利用索引来取得一条记录。key字段表示使用了哪个索引。 key_len字段包括了使用的键的最长部分。这种类型时 ref 字段值是 null。range用于将某个字段和一个定植用以下任何操作符比较时 =, <>, >,>=,<, <=, is null, <=>, between, 或 in:select * from tbl_name where key_column = 10; select * fromtbl_namewhere key_column between 10 and 20; select * from tbl_namewhere key_column in(10,20,30); select * from tbl_name wherekey_part1= 10 and key_part2 in(10,20,30);
- I. index: 连接类型跟 all 一样,不同的是它只扫描索引树。它通常会比 all快点,因为索引文件通常比数据文件小。mysql在查询的字段知识单独的索引的一部分的情况下使用这种连接类型。
- J. all: 将对该表做全部扫描以和从前一个表中取得的记录作联合。这时候如果第一个表没有被标识为const的话就不大好了,在其他情况下通常是非常糟糕的。正常地,可以通过增加索引使得能从表中更快的取得记录以避免all。
(5) possible_keys:possible_keys字段是指 mysql在搜索表记录时可能使用哪个索引。注意,这个字段完全独立于explain 显示的表顺序。这就意味着 possible_keys里面所包含的索引可能在实际的使用中没用到。如果这个字段的值是null,就表示没有索引被用到。这种情况下,就可以检查 where子句中哪些字段那些字段适合增加索引以提高查询的性能。就这样,创建一下索引,然后再用explain 检查一下。想看表都有什么索引,可以通过 show index from tbl_name来看。
(6) key:key字段显示了mysql实际上要用的索引。当没有任何索引被用到的时候,这个字段的值就是null。想要让mysql强行使用或者忽略在 possible_keys字段中的索引列表,可以在查询语句中使用关键字force index,use index,或 ignore index。如果是 myisam 和 bdb 类型表,可以使用 analyzetable 来帮助分析使用使用哪个索引更好。如果是 myisam类型表,运行命令 myisamchk --analyze也是一样的效果。
(7) key_len:key_len 字段显示了mysql使用索引的长度。当 key 字段的值为 null时,索引的长度就是 null。注意,key_len的值可以告诉你在联合索引中mysql会真正使用了哪些索引。
(8) ref:ref 字段显示了哪些字段或者常量被用来和 key配合从表中查询记录出来。
(9) rows:rows 字段显示了mysql认为在查询中应该检索的记录数。
(10) extra:本字段显示了查询中mysql的附加信息。以下是这个字段的几个不同值的解释:
- A. distinct:mysql当找到当前记录的匹配联合结果的第一条记录之后,就不再搜索其他记录了。
- B. not exists:mysql在查询时做一个 left join优化时,当它在当前表中找到了和前一条记录符合 left join条件后,就不再搜索更多的记录了。下面是一个这种类型的查询例子:select * from t1left join t2 on t1.id=t2.id where t2.id is null; 假使 t2.id 定义为 not null。这种情况下,mysql将会扫描表 t1并且用 t1.id 的值在 t2 中查找记录。当在 t2中找到一条匹配的记录时,这就意味着 t2.id 肯定不会都是null,就不会再在 t2 中查找相同 id值的其他记录了。也可以这么说,对于 t1 中的每个记录,mysql只需要在t2 中做一次查找,而不管在 t2 中实际有多少匹配的记录。
- C. range checked for each record(index map: #) mysql没找到合适的可用的索引。取代的办法是,对于前一个表的每一个行连接,它会做一个检验以决定该使用哪个索引(如果有的话),并且使用这个索引来从表里取得记录。这个过程不会很快,但总比没有任何索引时做表连接来得快。
- D. using filesort: mysql需要额外的做一遍从而以排好的顺序取得记录。排序程序根据连接的类型遍历所有的记录,并且将所有符合 where条件的记录的要排序的键和指向记录的指针存储起来。这些键已经排完序了,对应的记录也会按照排好的顺序取出来。
- E. using index字段的信息直接从索引树中的信息取得,而不再去扫描实际的记录。这种策略用于查询时的字段是一个独立索引的一部分。
- F. using temporary: mysql需要创建临时表存储结果以完成查询。这种情况通常发生在查询时包含了groupby和 order by 子句,它以不同的方式列出了各个字段。
- G. using where,where子句将用来限制哪些记录匹配了下一个表或者发送给客户端。除非你特别地想要取得或者检查表种的所有记录,否则的话当查询的extra 字段值不是 usingwhere 并且表连接类型是 all 或 index时可能表示有问题。
- H. 如果你想要让查询尽可能的快,那么就应该注意 extra 字段的值为using filesort 和 using temporary 的情况。
使用explain优化SQL实例
通过 explain 的结果中 rows字段的值的乘积大概地知道本次连接表现如何。它可以粗略地告诉我们mysql在查询过程中会查询多少条记录。如果是使用系统变量 max_join_size 来取得查询结果,这个乘积还可以用来确定会执行哪些多表select 语句。下面的例子展示了如何通过 explain提供的信息来较大程度地优化多表联合查询的性能。假设有下面的 select 语句,正打算用 explain 来检测:
- explain select tt.ticketnumber, tt.timein, tt.projectreference,tt.estimatedshipdate, tt.actualshipdate, tt.clientid,tt.servicecodes, tt.repetitiveid, tt.currentprocess,tt.currentdppers tt.recordvolume, tt.dpprinted, et.country,et_1.country, do.custname
- from tt, et, et as et_1, do
- where tt.submittime is null and tt.actualpc = et.employid and tt.assignedpc = et_1.employid and tt.clientid = do.custnmbr;
从from子句可以看出select子句要进行多表查询,要从三个表tt, et 和do三个表中进行联合查询,看起来是四个表
重点看where比较子句:tt.submittime is null and tt.actualpc = et.employid and tt.assignedpc =et_1.employid and tt.clientid = do.custnmbr;
假设要比较的字段定义为表tt中的actualpc,actualpc 和clientid 均为 char(10)类型,et的employid 为char(15) 类型,do 的custnmbr为char(15) 类型,
这三个表的索引为tt有上那个索引:actualpc(值分布不均匀),assignedpc和clientid, et的为主键索引:employid (primary key),do表的也为主键索引custnmbr (primary key)
第一步,在任何优化措施未采取之前,经过 explain分析的结果显示如下:
- table type possible_keys key key_len ref rows extra
- et all primary null null null 74
- do all primary null null null 2135
- et_1 all primary null null null 74
- tt all assignedpc null null null 3872 clientid, actualpc range checked for each record (key map: 35)
分析: 由于字段 type 的对于每个表值都是all,这个结果意味着mysql对所有的表做一个迪卡尔积;这就是说,每条记录的组合。这将需要花很长的时间,因为需要扫描每个表总 记录数乘积的总和。在这情况下,它的积是74 * 2135 * 74 *3872 = 45,268,558,720条记录。如果数据表更大的话,你可以想象一下需要多长的时间。
在这里有个问题是当字段定义一样的时候,mysql就可以在这些字段上更快的是用索引(对isam类型的表来说,除非字段定义完全一样,否则不会使用索 引)。在这个前提下,varchar和 char是一样的除非它们定义的长度不一致。由于 tt.actualpc 定义为char(10),et.employid 定义为 char(15),二者长度不一致。
为了解决这个问题,需要用 alter table 来加大 actualpc的长度从10到15个字符:altertable tt modify actualpc varchar(15); 现在 tt.actualpc 和 et.employid 都是 varchar(15),在执行一次explain:
- table type possible_keys key key_len ref rows extra
- tt all assignedpc, null null null 3872 using clientid, where actualpc
- do all primary null null null 2135 range checked for each record (keymap: 1)
- et_1 all primary null null null 74 range checked for eachrecord (key map: 1) et eq_ref primary primary 15 tt.actualpc 1
这还不够,它还可以做的更好:现在 rows值乘积已经少了74倍。这次查询需要用2秒钟。
第二个改变是消除在比较 tt.assignedpc = et_1.employid 和 tt.clientid= do.custnmbr 中字段的长度不一致问题:
altertable tt modify assignedpc varchar(15), ->modify clientid varchar(15);
- table type possible_keys key key_len ref rows extra
- et all primary null null null 74
- tt ref assignedpc, actualpc 15 et.employid 52 using clientid, where actualpc
- et_1 eq_ref primary primary 15 tt.assignedpc 1
- do eq_ref primary primary 15 tt.clientid 1
这看起来已经是能做的最好的结果了。遗留下来的问题是,mysql默认地认为字段tt.actualpc的值是均匀分布的,然而表tt并非如此。幸好,我们可以很方便的让mysql分析索引的分布:mysql>analyze table tt; 到此为止,表连接已经优化的很完美了,explain 的结果如下:
- table type possible_keys key key_len ref rows extra
- tt all assignedpc null null null 3872 using clientid, where actualpc
- et eq_ref primary primary 15 tt.actualpc 1
- et_1 eq_ref primary primary 15 tt.assignedpc 1
- do eq_ref primary primary 15 tt.clientid 1
请注意,explain 结果中的 rows字段的值也是mysql的连接优化程序大致猜测的,请检查这个值跟真实值是否基本一致。如果不是,可以通过在select 语句中使用 straight_join 来取得更好的性能,同时可以试着在from分句中用不同的次序列出各个表。
调整Mysql数据库性能:
- 改变索引缓冲区长度(key_buffer):一般,该变量控制缓冲区的长度在处理索引表(读/写操作)时使用。MySQL使用手册指出该变量可以不断增加以确保索引表的最佳性能,并推荐使用与系统内存25%的大小作为该变量的值。这是MySQL十分重要的配置变量之一,如果你对优化和提高系统性能有兴趣,可以从改变key_buffer_size变量的值开始。
- 改变表长(read_buffer_size):当一个查询不断地扫描某一个表,MySQL会为它分配一段内存缓冲区。read_buffer_size变量控制这一缓冲区的大小。如果你认为连续扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。
- 设定打开表的数目的最大值(table_cache):该变量控制MySQL在任何时候打开表的最大数目,由此能控制服务器响应输入请求的能力。它跟max_connections变量密切相关,增加 table_cache值可使MySQL打开更多的表,就如增加max_connections值可增加连接数一样。当收到大量不同数据库及表的请求时,可以考虑改变这一值的大小。
- 对缓长查询设定一个时间限制(long_query_time):MySQL带有“慢查询日志”,它会自动地记录所有的在一个特定的时间范围内尚未结束的查询。这个日志对于跟踪那些低效率或者行为不端的查询以及寻找优化对象都非常有用。long_query_time变量控制这一最大时间限定,以秒为单位。
Mysql 优化表命令
AnalyzeTable MySQL 的Optimizer(优化元件)在优化SQL语句时,首先需要收集一些相关信息,其中就包括表的cardinality(可以翻译为“散列程度”),它表示某个索引对应的列包含多少个不同的值——如果cardinality大大少于数据的实际散列程度,那么索引就基本失效了。
我们可以使用SHOW INDEX语句来查看索引的散列程度:SHOWINDEX FROM PLAYERS;
TABLE KEY_NAME COLUMN_NAMECARDINALITY
------- -------- ----------- -----------
PLAYERS PRIMARYPLAYERNO 14
因为此时PLAYER表中不同的PLAYERNO数量远远多于14,索引基本失效。
下面我们通过Analyze Table语句来修复索引:
ANALYZE TABLE PLAYERS;
SHOW INDEX FROM PLAYERS;
结果是:
TABLE KEY_NAME COLUMN_NAMECARDINALITY
------- -------- ----------- -----------
PLAYERS PRIMARYPLAYERNO 1000
此时索引已经修复,查询效率大大提高。
需要注意的是,如果开启了binlog,那么Analyze Table的结果也会写入binlog,我们可以在analyze和table之间添加关键字local取消写入。
Checksum Table数据在传输时,可能会发生变化,也有可能因为其它原因损坏,为了保证数据的一致,我们可以计算checksum(校验值)。使用MyISAM引擎的表会把checksum存储起来,称为live checksum,当数据发生变化时,checksum会相应变化。在执行Checksum Table时,可以在最后指定选项qiuck或是extended;quick表示返回存储的checksum值,而extended会重新计算checksum,如果没有指定选项,则默认使用extended。
Optimize Table经常更新数据的磁盘需要整理碎片,数据库也是这样,Optimize Table语句对MyISAM和InnoDB类型的表都有效。如果表经常更新,就应当定期运行OptimizeTable语句,保证效率。与Analyze Table一样,Optimize Table也可以使用local来取消写入binlog。对于经常修改的表,容易产生碎片,使在查询数据库时必须读取更多的磁盘块,降低查询性能。具有可变长的表都存在磁盘碎片问题,这个问题对blob数据类型更为突出,因为其尺寸变化非常大。可以通过使用optimize table来整理碎片,保证数据库性能不下降,优化那些受碎片影响的数据表。 optimize table可以用于MyISAM和BDB类型的数据表。实际上任何碎片整理方法都是用mysqldump来转存数据表,然后使用转存后的文件并重新建数据表;
Check Table数据库经常可能遇到错误,譬如数据写入磁盘时发生错误,或是索引没有同步更新,或是数据库未关闭MySQL就停止了。遇到这些情况,数据就可能发生错误:Incorrectkey file for table: ' '. Try to repair it. 此时,我们可以使用Check Table语句来检查表及其对应的索引。譬如我们运行CHECKTABLE PLAYERS; 结果是
TABLE OP MSG_TYPE MSG_TEXT
-------------- ----- -------- --------
TENNIS.PLAYERS check status OK
MySQL会保存表最近一次检查的时间,每次运行check table都会存储这些信息:
执行
SELECT TABLE_NAME, CHECK_TIME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'PLAYERS'
AND TABLE_SCHEMA ='TENNIS'; /*TENNIS是数据库名*/
结果是
TABLE_NAME CHECK_TIME
---------- -------------------
PLAYERS 2006-08-2116:44:25
Check Table还可以指定其它选项:
UPGRADE:用来测试在更早版本的MySQL中建立的表是否与当前版本兼容。
QUICK:速度最快的选项,在检查各列的数据时,不会检查链接(link)的正确与否,如果没有遇到什么问题,可以使用这个选项。
FAST:只检查表是否正常关闭,如果在系统掉电之后没有遇到严重问题,可以使用这个选项。
CHANGED:只检查上次检查时间之后更新的数据。
MEDIUM:默认的选项,会检查索引文件和数据文件之间的链接正确性。
EXTENDED:最慢的选项,会进行全面的检查。
Repair Table 用于修复表,只对MyISAM和ARCHIVE类型的表有效。
这条语句同样可以指定选项:
QUICK:最快的选项,只修复索引树。
EXTENDED:最慢的选项,需要逐行重建索引。
USE_FRM:只有当MYI文件丢失时才使用这个选项,全面重建整个索引。
与Analyze Table一样,Repair Table也可以使用local来取消写入binlog。