1、安装mysql
1)安装包下载地址:https://downloads.mysql.com/archives/community/
2)采用rpm安装方式,先下载安装包,放到/opt/mysql路径下:rz命令,如下图:
3)安装之前一定要先删除centos7自带的mysql的分支mariadb
在安装之前需要先检查是否有安装过Mysql:
rpm -qa |grep -i mysql
rpm -qa |grep -i mariadb(centos7默认系统自带的)
因此需要先删除mariadb:
rpm -e mariadb-libs-5.5.64-1.el7.x86_64
如果没有先删除自带的mariadb,则安装mysql的时候会提示有依赖,无法安装,需要在命令后加上--force --nodeps ,这还不是最重要的,最重要的是在服务端安装完成之后,通过
service mysql start 命令启动服务的时候会报错,这个比较严重:Starting MySQL. ERROR! The server quit without updating PID file (/var/lib/mysql/xiaojdCentos.pid). 如下图:
4)安装服务端:
rpm -ivh MySQL-server-5.5.48-1.linux2.6.x86_64.rpm --force --nodeps
以上内容说明服务端安装成功,需要设置root密码
4)安装客户端:
rpm -ivh MySQL-client-5.5.48-1.linux2.6.x86_64.rpm --force --nodeps
5)查看rpm是否安装过mysql:
rpm -qa|grep -i mysql
6)查看mysql是否安装好:
cat /etc/passwd|grep mysql
cat /etc/group|grep mysql
mysqladmin --version
7)启动mysql服务
由于还没有设置密码,所以输入mysql则可以直接连接数据库。
8)修改root用户密码
/usr/bin/mysqladmin -u root password root@123
修改完之后再通过mysql直接连的时候就会报错需要密码了
9)通过密码的方式连接数据库
mysql -u root -p
10)设置mysql开机自启动
chkconfig mysql on
也可以以命令打开窗口的方式查看哪些服务是开机自启动的[前边带型号*的是开机自启动]:
ntsysv
tab键可以在【ok】和【cancel】之间切换
11)修改配置文件位置
cd /usr/share/mysql
cp my-huge.cnf /etc/my.cnf
其中配置文件在5.5版本中叫my-huge.cnf,在5.6中叫my-default.cnf
12)更改my.cnf中的字符集编码
2、数据库操作:
1)修改字符集
连接数据库:mysql -u root -p
显示有哪些库:
show databases; 注意一定要带分号,否则认为命令还没有写完
创建一个新的库实例:
create database db01;
使用新建的库:
use db01;
查看当前库下有哪些表:
show tables;
创建表:
create table user(id int not null,name varchar(20));
查看表结构:
desc user;
向表中插入数据:
insert into user values(1,‘z3‘);
查询数据:
select * from user;
如果插入中文,再查询的时候,就会中文乱码,如下图:
把mysql默认字符集编码改为utf8
先查看字符集编码:
show variables like ‘character%‘; 或者 show variables like ‘char%‘;
通过上图可以看到mysql服务端默认用的字符编码是latin1,需要改为utf8
vim /etc/my.cnf
下图红色内容为需要新增的内容:
配置改完后需要重启数据库:
service mysql restart
重新连接数据库,选择db01库,查询user表,原有的中文仍然乱码,插入新的含有中文的数据,再查询,中文仍然乱码,原因是在改字符集编码之前,已经建立了db01库导致的,正确的办法,应该删除db01库,再重新建库。
删除db01库:
drop database db01;
3、存储引擎
查看存储引擎,默认的存储引擎用的就是InnoDB
show engines;
show variables like ‘%storage_engine%‘;
mysql语句执行顺序如下图:
limit的用法:
从第一条记录开始,共查询3条记录的两种写法:
select name nm,id from user order by id limit 3;
select name nm,id from user order by id limit 0,3;
从第二条记录开始,共查询3条记录:
select name nm,id from user order by id limit 1,3;
所以,当limit有两个参数时,第一个参数为0代表从第1条记录开始,为1则代表从第2条记录开始取出结果。
4、mysql中join的用法
其中join 等价于 inner join
mysql不存在full outer join的写法,等价于A left join B union A right join B
例:select * from user left join user2 on user.id=user2.id
union
select * from user right join user2 on user.id=user2.id;
1)左连接
2)右连接
3)inner join 等价于join
4)只取表A独有的 left join + B为null
5)只取表B独有的,right join + A为null
6)取A和B的合集
A left join B union A right join B
例子:select * from user left join user2 on user.id=user2.id
union
select * from user right join user2 on user.id=user2.id;
7)取A独有的和B独有的合集
A left join B + B.KEY is null union A right join B where A.key is null
例子:select * from user left join user2 on user.id=user2.id where user2.id is null union select * from user right join user2 on user.id=user2.id where user.id is null;
为方便记忆,可概括总结为7种结果集的写法为:
1) left join (左表全部)
2) left join + where 条件 (左表独有)
3) right join (右表全部)
4) right join + where 条件 (右表独有)
5) left join union right join (两表全部)
6) left join+条件 union right join+条件 (左表独有+右表独有)
7) inner join (两表共有,即中间交叉部分)
8)from A,B (笛卡尔积,两表条数相乘)
5、索引
索引的作用:提升查找和排序的效率,分组必排序,所以group by用到索引也会提升分组效率。
有些表都加上一个是否删除的标志位,好处就是做逻辑删除,不会因为数据的物理删除而重新维护索引。(多个字段建有索引,物理删除记录,则多个索引都需要进行维护),另一个原因就是数据存在,方便进行数据分析。
索引的优势:
1) 提高数据的检索效率,降低数据库的IO成本
2) 索引对数据进行排序,降低数据排序成本,降低了CPU的消耗
创建索引的两种方式:
1) create index idx_user_name on user(name);
2) ALTER TABLE user ADD INDEX index_name (name);
删除索引:
drop index index_name on user;
查询表中建了哪些索引:
show index from user;
explain的用法
explain + sql :查看sql语句的执行计划。
执行计划包含的10个字段:id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra。
id:
1) id相同,从上向下执行;
2) id不同,id大的先执行,小的后执行;
3) id相同不同,按id相同分组,id大的组先执行,相同的id从上向下执行。
select_type:
可能存在的值:
1) SIMPLE 普通的查询;
2) PRIMARY 含有复杂子查询时,最外层的查询;
3) SUBQUERY 子查询;
4) DERIVED 子查询形成的临时表;
5) UNION 若第2个select出现在union之后,则被标记为union;
6) UNION RESULT 从union表获取结果的select。
table:显示这次查询是从哪张表查询数据的。
type: system>const>eq_ref>ref>range>index>ALL
system:表中只有一条数据
const: 用到了主键索引,一次就找到了记录
eq_ref:用到了唯一索引,表中只有一条记录与之匹配
ref: 用到了普通索引,查询等于某一个值的数据,可能返回多条数据
range: 用到了普通缩影,查询某一个范围内的数据,可能返回多条数据
index: 全索引扫描,遍历整个索引树,由于索引表比数据表小,所以快于全表扫描。
全表扫描:
备注:一般来说,得保证查询至少达到range级别,最好能达到ref
possible_keys:
显示可能应用在这张表中的索引,一个或多个。 查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
key:
实际使用的索引。如果为NULL,则没有使用索引;
查询中若使用了覆盖索引,则该索引仅出现在key列表中。
key_len:
表示索引中使用的字节数,在不损失精确性的前提下,长度越短越好,该字段可暂时不用记。
ref:
举例:
1) where a.id=5; ref的值为const,即常量,即索引字段查询的值为常量
2) where a.id = b.deptid; ref 的值为b.deptid ,即索引字段查询的值为其他字段的返回值
3) where a.id in(5,6); ref的值为null,即不是常量也不是其他字段的值
ref的值的含义为:索引字段查询的值为常量或者其他字段的返回值,或者为null。
rows:
每张表有多少行被查询。
extra:其他额外的重要信息
1) Using filesort:无法使用建好的索引进行排序。效率极低,比较危险。
2) Using temporary: 使用了临时表保存中间结果,十分十分严重,必须需要优化常见于排序order by 和分组查询group by,或者distinct。解决办法:group by 尽量和索引个数和索引字段顺序保持一致。
3) Using index: 代表使用了覆盖索引(即select后跟的字段都是索引字段),防止了回表扫描。效率是不错的,是好现象。
4) Using where : 有where条件查询
5) Impossible WHERE:where 后跟的条件的为false,不能获取到任何数据,如where 1=2。
mysql与db2不同之处:db2中select 后跟的字段需要是group by中的字段,而mysql没有这个限制,但是结果中会导致同组中只能查询出一条数据,其他数据丢失。
如果想将执行计划竖着显示,可以加上“\G”,例如:
1) 横着显示执行计划
explain select * from tbl_dept order by id;
2) 竖着显示执行计划
explain select * from tbl_dept order by id\G 注意后边没有分号,其他语句是有分号的。
索引优化:
案例1:
--建表
create table if not exists article(
id int(10) unsigned not null primary key auto_increment,
author_id int(10) unsigned not null,
category_id int(10) unsigned not null,
views int(10) unsigned not null,
comments int(10) unsigned not null,
title varbinary(255) not null,
content text not null
);
--插入数据
insert into article(author_id,category_id,views,comments,title,content) values
(1,1,1,1,‘1‘,‘1‘),
(2,2,2,2,‘2‘,‘2‘),
(1,1,3,3,‘3‘,‘3‘);
查询category=1,comments>1,且views值最大的数据。
查询语句为:
explain select * from article where category_id=1 and comments >1 order by views desc limit 1;
1) 没有索引的情况先,查询语句的执行计划为:
可以看到,全表扫描且存在Using filesort,如果表数据量大,这是很耗时的。
2) 尝试按查询条件+order by条件建联合索引
create index idx_article_ccv on article(category_id,comments,views);
再次查看查询语句的执行计划为:
可以看到用到了idx_article_ccv索引,type的值为range,即范围查询,但是extra中仍然存在Using filesort,有了一定的提高,但是仍需进一步优化;
3) 索引优化,删除(category_id,comments,views)顺序的联合索引,新建(category_id,views)顺序的联合索引
drop index idx_article_ccv on article;
reate index idx_article_cv on article(category_id,views);
再次查看查询语句的执行计划:
发现用到了新建的联合索引,type类型变为ref(单值查询),优于range(范围查询)类型,且Extra中也没有了Using filesort,这个索引的效率是比较好的。
总结:范围之后的索引会失效,即建的第一种索引(category_id,comments,views),由于用了comments>1范围查找,所以导致order by时用到的views字段无法使用索引,才出现的Using filesort。
案例2:
准备测试数据:建两张表并像两张表中插入数据
create table if not exists class(
id int(10) unsigned not null auto_increment,
card int(10) unsigned not null,
primary key(id)
);
create table if not exists book(
bookid int(10) unsigned not null auto_increment,
card int(10) unsigned not null,
primary key(bookid)
);
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
insert into book(card) values(floor(1+(rand()*20)));
1) 未建索引的情况下使用左连接和右连接查看执行计划
左连接:
右连接:
结果:左连接先查询左表,右连接先查询右表,均是全表扫描
2) 在左表book上建立索引,再查看左连接和右连接的执行计划
左连接:
右连接:
结果:还是左连接先查左表,右连接先查右表;不管左连接还是由连接,由于右表没有建索引,所以右表一定是全表扫描,重点关注左表的执行计划;左连接左表用到了索引,且type为index,即全索引扫描,右连接左表也用到了索引,且type为ref(优于index),rows为1(优于8),
总结:
1) 左连接先扫描左表,右连接先扫描右表;
2) 左连接将索引建在右表上,右连接将索引建在左表上;
3)如果A表上建有索引,需要A表和B表结合,且查询A表的全部数据,则A left join B 等价于B right join A,无论是数据结果上还是执行计划上都是等价的。
3) 左表和右表都建立索引的情况下:
左连接:
右连接:
总结:在两张表的都建立索引的情况下,仍然是左连接先查询左表,右连接先查询右表;
先被查询的表为type=index即全索引扫描,后被查询的表type=ref即用索引的等值查询。
案例3
3表关联查询
准备:额外建一张phone表,并插入数据
create table if not exists phone(
phoneid int(10) unsigned not null auto_increment,
card int(10) unsigned not null,
primary key(phoneid)
)engine = innodb;
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
insert into phone(card) values(floor(1+(rand()*20)));
1)两种不同的左连接的查询结果:
总结:从结果可以看出
book left join class on book.card = class.card left join phone on book.card = phone.card;和
book left join class on book.card = class.card left join phone on class.card = phone.card;
得到的查询结果是不一样的,主要是导致最后一张phone表被查询的结果不一样。
2)如何建索引
select * from
book left join class on book.card = class.card left join phone on book.card = phone.card;
上边的查询语句,应该如何建索引:
由于book left join class,所以class上应该建立索引;
由于book left join phone,索引phone上应该建立索引;
这样class表和phone表的全表扫描就会变为type=ref的索引指定值扫描了。
当然了book表建立索引,也会提升效率,只不过是由全表扫描变为了全索引扫描,没有class表和book表建索引的作用大。
JOIN语句总结:
1) 永远用小的结果集驱动大的结果集。但是要注意优化是要保证在查询的数据最终是相同的前提下,不能改了语法导致查询到的结果不一致。
2) 优先优化NestedLoop的内层循环;
3) 保证join语句中被驱动表上的join字段加上了索引(即left join的右表);
4) 当无法保证被驱动表的join条件字段加索引且内存资源充足的情况下,不要台吝啬JoinBuffer的设置(即my.cnf中的设置)。
避免索引失效:
如何解决select * from user where name like ‘%aa%’的无法用到索引的问题?
可以考虑不要用select *,如果只是查及少的两三个字段,可以考虑给这两三个字段建上联合索引,然后改为select column1,column2,column3的形式,这样虽然where后的条件用不上索引,但是查询的字段却可以走覆盖索引,不用回表,一样可以提高效率。
如何解决or索引失效的问题:
explain select * from staffs where name=‘z3‘ or name= ‘July‘; 索引失效
改进:explain select * from staffs where name=‘z3‘
-> union
-> select * from staffs where name=‘July‘;
用union解决or导致的索引失效问题。
小表驱动大表:
1)in
select * from A where id in(select id from B);
in先查内层循环,再查外层循环,即先查B再查A。
2)exists
select * from A where exists(select 1 from B where B.id=A.id);
exists先查外层循环,再查内层循环,即先查A,再查B。
1)依据小表驱动大表原则,当A表数据量>B表数据量时,应该B表做为驱动表,即应该先查询B表,选择第一种in的写法。
2)当A表的数据量<B表的数据量时,应该A表做为驱动表,即应该先查询A表,即选择第二种exists的写法。
慢查询日志:
1、生产上开启慢查询日志,设置阙值,比如超过5秒钟就被定义为慢sql,并将其抓取出来;
2、explain + 被抓取出来的慢sql进行分析调优。
3、show profile : 以上两步还不能解决慢的原因,则利用show profile更细致的分析;
4、运维经理或者DBA进行数据库参数的性能调优。
order by:分为双路复用排序和单路复用排序,mysql5之后用的都是单路复用排序。
单路复用排序整体效率高于双路复用,但是当sort_buffer空间不足时,会导致效率明显降低。
1)可以联系运维经理或者DBA看是否可以调大sort_buffer空间,
2)含排序的查询不要用select *,而是用哪些字段就查哪些字段,这样也可以减少无用字段对sort buffer空间的占用。
1)show variables like ‘%slow_query_log%‘;
查看慢查询日志是否开启以及慢查询日志存放位置,默认是关闭的,需要手动开启,除非需要分析,否则不建议开启,因为会影响一定的性能。
缺省文件名为: 主机名-slow.log
2)set global slow_query_log=1;
开启慢查询日志功能,但只对当前库有效,且mysql重启后失效,即又会变为关闭状态。
3) 若需要永久生效,需要在etc/my.cnf配置文件中的[mysqld]下配置两行:
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/centos2-slow.log
4)设置慢查询的阀值
show variables like ‘long_query_time%‘; 查询当前设置的阀值,默认10秒,只有大于10秒的sql会被记录,等于10秒的是不会被记录的。
set global long_query_time=5; 设置慢查询的阀值,当前会话查询仍然是原来的10秒,但实际自己的设置已经生效,只是需要重开一个会话查询才能看到。
5)查看慢查询日志中的慢查询语句并通过explain+sql语句进行分析
cat /var/lib/mysql/centos2-slow.log 具体看上边查询到的日志路径位置
6)show global status like ‘%slow_queries%‘; 查询共有多少条慢查询语句
7)mysqldumpslow mysql提供的慢查询日志分析工具
mysqldumpslow --help 查看用法帮助信息
用法:
建表、建函数、建存储过程、调用存储过程
1) 建表
--部门表
create table dept(
id int unsigned not null primary key auto_increment,
deptno mediumint unsigned not null default 0,
dname varchar(20) not null default "",
ioc varchar(13) not null default ""
)engine=innodb default charset=gbk;
--员工表
create table emp(
id int unsigned primary key auto_increment,
empno mediumint unsigned not null default 0,
ename varchar(20) not null default "",
job varchar(9) not null default "",
mgr mediumint unsigned not null default 0,
hiredate date not null,
sal decimal(7,2) not null,
comm decimal (7,2) not null,
deptno mediumint unsigned not null default 0
)engine=innodb default charset=gbk;
2) 设置参数log_bin_trust_function_creators
show variables like ‘log_bin_trust_function_creators‘; 查询该参数是否开启
set global log_bin_trust_function_creators=1; 开启该参数
3) 建函数
--随机产生字符串的函数
delimiter $$
create function rand_string(n int) returns varchar(255)
begin
declare chars_str varchar(100) default ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ‘;
declare return_str varchar(255) default ‘‘;
declare i int default 0;
while i<n do
set return_str = concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i=i+1;
end while;
return return_str;
end $$
--随机产生部门编号的函数
delimiter $$
create function rand_num() returns int(5)
begin
declare i int default 0;
set i = floor(100 + rand()*10);
return i;
end $$
--查看有哪些函数
select name from mysql.proc where db=‘db01‘ and type=‘function‘;
--查看有哪些存储过程
select name from mysql.proc where db=‘db01‘ and type=‘procedure‘;
--查看函数的内容
show create function rand_string;
--查看存储过程的内容
show create procedure proc_name;
4) 建存储过程
--创建向emp表中插入数据的存储过程
delimiter $$
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
set autocommit=0;
repeat
set i=i+1;
insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno)
values((start)+1,rand_string(6),‘SALESMAN‘,0001,curdate(),2000,400,rand_num());
until i=max_num
end repeat;
commit;
end $$
--创建向dept表中插入数据的存储过程
delimiter $$
create procedure insert_dept(in start int(10),in max_num int(10))
begin
declare i int default 0;
set autocommit=0;
repeat
set i=i+1;
insert into dept(deptno,dname,ioc) values((start+1),rand_string(10),rand_string(8));
until i=max_num
end repeat;
commit;
end $$
5) 调用存储过程
--调用向部门表中插入数据的存储过程,插入10条数据,从101开始
delimiter ;
call insert_dept(100,10);
--调用向员工表中插入数据的存储过程,向员工表中插入50万条数据
delimiter ; 上边如果已经将结束符改为分号了,此处就可以不用再改了。
call insert_emp(100001,500000);
show profile
默认情况下,参数处于关闭状态,并保存最近15次的运行结果。
1) 查看当前的mysql版本是否支持
show variables like ‘profiling‘;
2) 开启功能
set profiling=on;
3) 运行sql
select * from tbl_dept;
select * from tbl_emp e inner join tbl_dept d on e.deptid=d.id;
4) 查看结果
show profiles;
5) 诊断sql
show profile cpu,block io for query query_id; query_id为show profiles中查询到的query_id那一列的值,比如:
show profile cpu,block io for query 13;
可用参数如下:
6) 结论
explain的用处:
1)每次上线之前,可以用explain查看自己编写的sql语句的执行计划,看是否需要进一步优化;
2)生产上出现某功能查询返回结果慢,开启慢查询日志,查看慢查询日志中执行慢的sql,通过explain + 该sql语句分析慢的原因。
全局日志查询:
注意:永远不要在生产环境开启这个功能。
set global general_log=1;
set global log_output=‘table‘;
select * from mysql.general_log;
会把所有的查询语句记录到表中,以查表的形式查看。