0 内容
1.mysql底层原理:逻辑分层、数据库引擎
2.sql优化:sql优化和索引的关系
3.索引
4.sql性能问题,优化方法
5.优化案例
6.避免索引失效的一些原则
7.一些其他优化方法
8.sql排查----慢查询日志
9.分析海量数据
10.锁机制:解决因资源共享 而造成的并发问题
11.主从复制:将主数据库同步到从数据库
零 mysql版本
1.主流的是5.x版本。
5.4-5.x用的最多。特点:整和了三方公司的新存储引擎。(其中5.5最多。)
2.安装mysql
rpm -ivh rpm软件名(自己网上下载的rpm源)
如果安装过程中与某个软件冲突,就将其卸载掉
yum -y remove xxx
启动mysql应用:service mysql start
关闭: service mysql stop
重启: service mysql restart
当出现mysql.sock不存在的报错时,可能原因时mysql服务没有启动,此时设置开机自动开启mysql服务:
开机自启:chkconfig mysql on
取消开机自启:chkconfig mysql off
给root用户设置密码
/usr/bin/mysqladmin -u root password 123
数据库存放目录查看:
ps -ef|grep mysql 可以看到数据存放目录和pid文件目录
mysql的核心目录:
/var/lib/mysql mysql的安装目录
/usr/share/mysql mysql的配置文件
/usr/bin 命令目录(mysqladmin,mysqldump等命令)
/etc/init.d/mysql mysql启停脚本
mysql配置文件:
常见
my-huge.cnf 高端服务器(1-2G内存)的配置在这里面配
my-large.cnf 中等
my-medium.cnf 一般
my-small.cnf 较小
但是,以上配置文件mysql不能默认识别。默认只能识别/etc/my.cnf。因此要将上面的配置复制一份为my.cnf。
cp /usr/share/mysql/my-huge.cnf /etc/my.cnf
注意:mysql5.5的默认配置文件为my.cnf, 5.6为mysql-default.cnf
mysql的字符编码:
show variables like ‘%char%’; 查看编码
可以发现部分编码为latin的,要统一设置为utf-8
设置编码:
vim /etc/my.cnf
在[mysql]标签下加入:
default-character-set = utf8
然后在[client]标签下加入default-character-set = utf8
在[mysqld]标签下加入:
character_set_server = utf8
character_set_client = utf8
collation_server = utf8_general_ci
注意:修改编码只对之后创建的数据库生效。因此建议在mysql创建后就改变编码。
mysql清屏:
ctrl+L 或者system clear ----->在linux中
一.mysql分层、存储引擎
1.mysql逻辑分层
连接层--------->服务层--------->引擎层--------->存储层
客户端查询select命令,交给客户端的第一层连接层,连接层作用是提供与客户端连接的服务,只负责连接,不处理sql语句。然后到第二层服务层。服务层作用有两个:(1)提供各种用户使用的接口(select...);(2)提供sql优化器(优化器好处是对sql语句进行优化,提高性能,弊端是开发时写的和执行是不一致的,造成混乱)。其次到第三层引擎层,引擎层作用是提供各种存储数据的方式(Innodb、Myisam等)。最后到第四层存储层,存储层作用是存储数据。
2.存储引擎
Innodb和Myisam主要区别:Innodb是事务优先(防止并发操作问题),适合高并发操作,用的行锁;Myisam是性能优先,用的表锁。
查询数据库引擎: 支持哪些引擎
show engines
show variables like ‘%storage_engine%’ 查看当前使用引擎
指定数据库对象的引擎:
create table td(
id int(4) auto_increment,
name varchar(5),
dept varchar(5),
primary key(id)
)ENGINE=MyISAM AUTO_INCREMENT=3(指定自增的步长)DEFAULT CHARSET=utf8 ; #注意是在;内部
二、sql优化
1.sql优化
(1)为什么要sql优化:
性能低、执行时间太长、等待时间太长、sql语句欠佳(连接查询)、索引失效、服务器参数设置不当(缓冲、线程等)
(2)sql语句
编写过程:
select distinct ... from ...join ... on ... where ... group by ... having ... order by ...limit ...
解析过程:(解析过程和编写过程不一致)
from--->on---->join---->where--->group by---->having---->select distinct---->order by---->limit
https://www.cnblogs.com/annsshadow/p/5037667.html
2.sql优化与索引关系
sql优化,主要就是在优化索引。
索引:相当于书的目录。加速查询效率。索引是帮助mysql高效获取数据的数据结构(树:B树(mysql默认)、hash树...)。
索引弊端:
本身很大,可以存在硬盘里面,占据一定空间
不是所有情况都适用:少量数据;频繁更新的字段;很少使用的字段
索引确实会提高查询的效率,但降低增删改的操作。(要改两次,还要改索引)
索引优势:
提高查询效率。(降低IO使用率)
降低CPU使用率(比如....order by 有索引就自动给排好序了。)
三、索引
1.B树
3层B树可以存放上百万数据。B树一般指B+树,真实数据存放在叶节点中,根节点和支节点的数据是关键字,只是为了分界指针,指引下一次查找方向的。
B+树中查询任意数据次数都是n次。n是B树的高度。
2.索引分类:
主键索引:不能重复,并且不能为null
唯一索引:不能重复。一般用唯一标识符id。 和主键的区别是可以为null
单值索引:单列,一个表可以有多个单值索引
复合索引:多个列构成的索引(相当于二级目录) (name,age) 先根据name找,name重复了再根据age找。(前面列找到唯一了,后面就可以不用找了)
3.索引命令
创建索引:
方式一
create 索引类型 索引名 on 表(字段)
单值:create index dept_index on tb(dept);
唯一:create unique index name_index on tb(name);
复合:create index dept_name_index on tb(dept, name);
方式二:
alter table 表名 索引类型 索引名(字段)
单值:alter table db add index dept_index(dept);
唯一:alter table db add unique index name_index(name);
复合:alter table db add index dept_name_index(dept, name);
主键:alter table db add constraint tid_pk primary key(tid);
注意事项:如果一个字段是主键primary key,该字段默认是 主键索引。
删除索引:
drop index 索引名 on 表名;
drop index name_index on tb;
查询索引
show index from 表名;
四、sql性能
1.explain
分析sql的执行计划:explain。可以模拟sql优化器执行sql语句,从而让开发人员知道自己编写的sql状况。
注:mysql查询优化器会干扰我们的优化
2.explain字段
id: 编号
select_type :查询类型
table :表
type :类型
possible_keys :预测用到的索引
key :实际使用的索引
key_len :实际使用索引的长度
ref :表和表之间的引用关系
rows: 通过索引查到的数据量
Extra:额外的信息
(1)id
*id值相同,按照table表,从上往下执行(上面的表是数据量小的表)。
多表查询
表的执行顺序因数据量的个数改变而改变的原因:
笛卡儿积。
a b c 表分别是2 3 4 笛卡尔积为 2*3=6*4 =24
当顺序变为3 4 2时,笛卡儿积为 3*4=12*2 =24
虽然最终结果相同,但中间生成的表大小不一样。第一个为6,第二个为12.因此第一个耗的内存少。(程序喜欢中间过程越小越好)
因此数据量小的先算,数据量大的后算,这样中间生成的表小。
总结来说,就是数据量小的表优先查询。
*id值不同,id值越大越优先被查询。
子查询
本质:在嵌套子查询时,先查内层,再查外层
*id值有相同,又有不同:id值越大越优先被查询,id值相同时从上往下执行。
(2)select_type
primary :包含子查询sql中的主查询(最外层) sql语句的核心语法。
subquery:包含子查询sql中的子查询(非最外层)
simple:简单查询(不包含子查询、union查询)
derived:衍生查询(在查询的时候用到了临时表)
情况1:在from子查询中只有一张表 ,from里这张表是derived衍生查询。
explain select cr.cname from (select * from course where tid in (1,2)) cr;
因为cr是临时产生的。
此时primary后面的table写的
情况2:在from子查询中,如果有table1 union table2 ,则table1就是衍生表derived,table2是union表
union:见上例。
union result:告知开发人员,哪些表之间存在union查询。
(3)type索引类型
system > const > eq_ref >ref > range > index >all
>表示性能。越往左边,性能越高。
其中system和const只是理想情况,实际达不到。实际能达到 ref > range 。不优化,就是all。
要对type优化的前提:有索引。
system(忽略):只有一条数据的系统表 或 衍生表只有一条数据的主查询
const: 仅仅能查到一条数据的sql,必须用于主键索引或者唯一索引。(类型 与 索引类型有关)
eq_ref: 唯一性索引:对于每个索引键的查询,返回匹配唯一行的数据(有且仅有一个)。常见于主键索引和唯一索引。也很难满足,要求查询结果和数据条数是一致的。表的数据的个数和连接查询的数据个数一致(查出来的数据和实际数据个数是一样的),有可能满足eq_ref,否则不能满足(肯定有0的记录)
ref:非唯一性索引,对于每个索引键的查询,返回匹配的所有行(0、1或多个)。
range:检索指定范围的行, where后面是个范围查询。(between、>、<、in注:in有时会失效,从而转为无索引all)
(在查询前先确定是索引,否则谈不上优化,都是all)
index:查询全部索引中的数据。只将索引树全查了一遍。(索引字段的数据)
比如:explain select 索引 from table
all:查询全部表中的数据。将整个表的数据查了一遍。(所有字段的数据)
比如:explain select 非索引 from table #不是索引,全扫描
type类型总结:system/const:结果只有一条数据;eq_ref:结果多条,但每条数据是唯一的 ;ref:结果多条,但每条数据是0或者多条。
(4)possible_keys 可能用到的索引
是一种预测。如果是null,说明没用索引
(5)key 实际索引。如果是null,说明没用索引
(6)key_len : 索引长度 用于判断复合索引是否被完全使用。(a,b,c)可能只用到a和b,c没用。(key_len(字节)和索引的实际长度(字符))
在utf8中一个字符占3个字节。(比如索引的字段(非空的)是char(20),是20个字符,但实际占据60个字节,因此在key_len中查出来的是60)
如果索引字段可以为null,在mysql会使用一个字节用于标识。比如索引的字段(可以为空的)是char(20),是20个字符,但实际占据60+1个字节,因此在key_len中查出来的是61。
如果是varchar(20),varchar是可变字符(char是固定长度字符),那么explain的key_len为字节63=20*3 + 1(可以为空的标识) + 2(可变长度的标识)
utf8 :1字符3字节
gbk:1字符2字节
latin:1字符1字节
(7)ref
注意与type中的ref区分。作用:指明当前表所参照的字段。
select .... where a.c = b.x(其中b.x可以是常量,,const) b.x就是参照的字段
rows
被索引优化查询的 数据个数(实际通过索引而查询到的数据个数)
(8)extra
常见:
using filesort: 标识性能消耗比较大。需要‘额外’一次排序(查询)。排序 的前提是先先查询。经常出现在order by
对于单索引,如果排序和查找是同一个字段,则不会出现using filesort;反之,如果排序和查找不是同一个字段,会出现using filesort。
explain select * from test01 where a1=’’ order by a2 #a1和a2是单索引。(要根据a2排序,而前面查的不是a2,因此需要一次额外的a2的排序操作)
对于复合索引,不能跨列(最佳左前缀)。否则出现using filesort。
explain select * from test01 where a1=’’ order by a3; (a1, a2, a3)
a1 ,a3中间跨列了;a2,a3不符合最佳左前缀------》就会出现using filesort。
a1, a2不会出现using filesort。(没有跨列,也符合最佳左前缀)
建议:对于单索引,where哪些字段,就order by哪些字段。对于复合索引:where 和order by按照复合索引的顺序使用,不要跨列或者无序使用
using temporary:用到了临时表,需要额外再多使用一张表
性能损耗比较大,一般出现在group by分组。查根据一个字段,而分组却根据另外一个字段,就会出现这个问题。查到的表不适用,需要再查一张临时表。
避免:查询哪些列,就根据那些列分组。
要研究using temporary,先要知道解析过程:
from--->on---->join---->where--->group by---->having---->select distinct---->order by---->limit
using index: 性能提升了;索引覆盖。原因在于:不读取原文件,只从索引文件中获取数据,不需要回表查询(只查索引表)。
只要使用到的列 全部都在索引中,就是索引覆盖。 using index
如果用到索引覆盖(using index)时会对possible_keys和keys造成影响:如果没有where,则索引只出现在key中;如果有where,则索引出现在possible_key和key中。
using where:需要回表查询
既要从索引表查,又要回原表查,就会出现using where
比如age是索引列
但查询语句是select age, name from ... where age=... 此语句必须回原表查name,因此会显示using where
impossible where :where子句永远为false
五、优化例子
1.单表优化
create table book( bid int(4) primary key, name varchar(20) not null, authorid int(4) not null, publicId int(4) not null, typeId int(4) not null );
insert into book values(1,'tj', 1, 1, 2); insert into book values(2,'tc', 2, 1, 2); insert into book values(3,'wx', 3,2, 1); insert into book values(4, 'math', 4, 2, 3); commit
查询authorId=1且typeId为2或3 的bid select bid from book where typeId in(2,3) and authorId=1 ; explain select bid from book where typeId in(2,3) and authorId=1 ;
explain select bid from book where typeId in(2,3) and authorId=1 order by typeId desc;
上面语句必须要优化,type为all,又没有索引,又要文件内排序。
优化:加索引。 alter table book add index idx_bta(bid, typeId, authorId);
比刚才有提升,但并不是很好。
上面加索引的顺序和个数不对。 按照执行顺序,先where然后select。因此要调整索引的顺序 索引一旦进行升级优化,要将之前废弃的索引删掉,防止干扰。 drop index 索引名 on 表名; drop index idx_bta on book;
alter table book add index idx_tab(typeId, authorId,bid); 虽然可以回表查询bid,但是将bid放到索引中,可以使用using index
再次优化(现在type是index,要再往上): explain select bid from book where typeId in(2,3) and authorId=1 order by typeId desc; in有时会导致索引失效,导致后面的索引也会失效。所以把等值的放前面(查询语句改变),范围查询的放后面 查询语句变为: explain select bid from book where authorId=1 and typeId in(2,3) order by typeId desc; drop index idx_tab on book; alter table book add index idx_atb( authorId,typeId,bid); 处理索引
提高了两个级别。 注:上面的using where(回表)和using index(不回表)同时出现,即又要回表又要不会表,是不矛盾的。authorId在索引中,查表时索引里有,就不需要回表。而typeId虽然在索引中,但In 的范围查询使索引失效,因此typeId需要回原表。 小结: 1.索引不能跨列使用(最佳左前缀),保持索引的定义和使用顺序一致性。 (跨列:原来顺序是a、b、c,那么使用a、c就是跨列。)
|
2.双表优化
建表插数据 create table teacher2( tid int(4) primary key, cid int(4) not null ); insert into teacher2 values(1,2); insert into teacher2 values(2,1); insert into teacher2 values(3,3);
create table course2( cid int(4), cname varchar(20) ); insert into course2 values(1, 'java'); insert into course2 values(2, 'python'); insert into course2 values(3,'kotlin'); commit;
左连接: select * from teacher2 t left outer join course2 c on t.cid=c.cid where c.cname='java';
优化加索引,索引往哪加呢(哪张表的字段加)? -小表驱动大表。where 小表.x =大表.x 小表:10条数据 大表:300条数据 select .... where 小表.x10=大表.x300; 不管小表在前还是大表在前,最终结果是一样的,但是小表在左,就会使外层循环小。 编程语言优化原则(外层放循环次数小的,内层放循环次数大的,性能越好)
当编写on t.cid=c.cid类似语句时,将min(t, c)数据量小的表放左边。(假设t的数据量小)
-索引建立在经常使用的字段。 本题左边表(小表)的使用量大,因为每一条数据都要执行300次,因此给小表的x字段加索引。因此t.cid使用更频繁,给它加索引。 一般情况,对于左外连接,给左表加索引;右外连接,给右表加索引。(因为左外连接的前提就是以左表为基础,左表的所有数据要全部匹配,也就是小表,即使用频繁;同理右表) 不加索引的情况: explain select * from teacher2 t left outer join course2 c on t.cid=c.cid where c.cname='java';
加索引: alter table teacher2 add index index_teacher_cid(cid);
还有,where后面 等值的加个索引 alter table course2 add index index_course2_cname(cname);
using join buffer:extra中的一个选项,作用是:引擎使用了连接缓存。说明sql写的太差了。
|
3.三表优化
原则: 小表驱动大表(小表放左边,大表放右边) 索引建立在经常查询的字段(将where后面的字段和常用的几个字段加索引) |
六、避免索引失效的原则
1.复合索引,不要跨列或无序使用(最佳左前缀) where a... and b... order by c 2.复合索引,尽量使用全索引匹配(索引建了,全用上) 3.不要在索引上进行任何操作(计算、函数、类型转换等),否则索引失效 select ... where A.x=.. 假设A.x是索引 不要select ... where A.x*3=... 对于复合索引,如果左边索引失效,那么右边索引全部失效。(独立的索引没有关系)
sql优化,是一种概率层面 的优化(服务层的优化器会动sql语句。至于是否实际使用了我们的优化,需要通过explain去推测。 比如<时改了个数字,索引结果完全不同。(一个是ALL,一个是用了一个索引) 索引优化是一个发部份使用的结论,但由于sql优化器的原因,结论不是100%正确。 一般情况下,范围查询(>, < in),之后的索引失效。 补救:尽量使用覆盖索引using index (a, b, c) 比如:select a,b,c from xxx where a=... and b=...;
如果必须使用%开头进行模糊查询,可以使用索引覆盖(查询的列全部在索引里,不需要回表查询)挽救一部分。
where tname = 123 tname类型是字符串,而查询时给的是数字,mysql在内部会转换,因此失效。
|
七.其他优化案例和总结
1.优化例子
建表和索引 create table test03( a1 int(4) not null, a2 int(4) not null,a3 int(4) not null, a4 int(4) not null ); alter table test03 add index idx_a1_a2_a3_a4(a1,a2,a3,a4);
|
explain执行计划: explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a3=3 and a4=4; 推荐写法(索引使用顺序(where后面的顺序)和复合索引的顺序一致)
explain select a1,a2,a3,a4 from test03 where a4=1 and a3=2 and a2=3 and a1=4; 和上面结果一致。虽然编写顺序和索引顺序不一致,但sql优化器调整了顺序,二者是等价的。不推荐。
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a4=4 order by a3;
有using index 代表一部分不需要回表操作。用到了a1和a2两个索引。 有using where代表有回表操作。a4跨列使用,造成该索引失效,需要回表查询。 key_len为8也说明只用上了2个索引。
explain select a1,a2,a3,a4 from test03 where a1=1 and a4=4 order by a3;
以上sql出现using filesort(文件内排序,多了一次额外的查找/排序)。原因是:不要跨列使用(where和order by拼起来不要跨列使用)。 解释上面拼起来: where a1=1 and a2=2 and a4=4 order by a3; 此时a4失效,则是a1, a2 ,a3,满足索引顺序 where a1=1 and a4=4 order by a3; 此时a4失效了,则是a1,a3,但跨列了,因此出现using filesort(看order by之后的索引和之前的索引是否跨列) 比如: explain select a1,a2,a3,a4 from test03 where a1=1 and a4=4 order by a2; 就不会出现using filesort
总结: 1.如果(a,b,c,d)复合索引 和使用的顺序(where后面)全部一致且不跨列使用,则复合索引全部使用。如果部分一致,则使用部分索引。 比如where a= and b= and c= and d= 使用全部索引 where a= and b= and d= 部分一致,使用(a,b) where a= and b= and d= and c = 部分一致,使用(a,b,c) 2.where和order by拼起来不要跨列,否则有文件内排序
|
2.一些其他优化方法
exist和in
select ... from ... where exist (子查询);
select ... from .... where 字段 in (子查询)
如果主查询的数据集大(相对子),使用in
如果子查询的数据集大,使用exist
可以互相转换,只是遵循上面原则更高效
exist语法:将主查询的结果,放到子查询结果中进行条件校验(看子查询是否有这个数据)。如果符合校验,则保留数据;
select tname from teacher where exists (select * from teacher);
-----》等价于select tname from teacher
in语法:
select ... from .... where 字段 in (1,3,5)
select * from A where id in (select id from B); //(1,3,5)
order by优化
using filesort 有两种算法:双路排序、单路排序(根据IO次数)
mysql4.1之前默认使用双路排序;双路:扫描两次磁盘(第一次扫描排序字段,从磁盘读取排序字段后,进行排序(在buffer缓冲区进行的),第二次扫描其他字段)----->IO消耗性能
mysql4.1之后默认使用单路排序;单路:扫描一次磁盘(全部字段),在buffer中排序。但单路排序有一定隐患(不一定真的是一次IO:数据量太多,则无法将所有的字段的数据一次性读取完毕,因此会进行“分片读取”。buffer可能放不下)
注意:单路排序比双路排序占用更多的buffer。
单路排序在使用时,如果数据大,可以考虑调大buffer的大小。
调节buffer大小:
set max_length_for_sort_data = 1024 单位:字节
如果set max_length_for_sort_data值太低,会自动从单路切到双路。
太低:需要排的列的总大小超过set max_length_for_sort_data定义的字节数。
提高order by查询的效率:
a.选择使用单路或者双路;调整buffer容量的大小
b.避免select *
c.复合索引不要跨列使用,避免using filesort
d.尽量保证全部的排序字段 排序的一致性(都是升序 或者降序)
八.sql排查:慢查询日志
就是mysql提供的一种日志记录,用于记录mysql中响应时间超过阈值的sql语句。(long_query_time,默认10s)。10s钟还没查出来,就说明急需优化。
慢查询日志默认关闭的,建议:开发调优时打开,而在最终部署时关闭。
检查是否开启 慢查询日志:
show variables like '%slow_query_log%';
开启慢查询日志:
临时开启:set global slow_query_log = 1; 在内存里开启
永久开启:配置文件my.cnf中追加:
在mysqld后追加:
slow_query_log=1
slow_query_log_file=目录/localhost-slow.log
修改慢查询阈值:
show variables like ‘%long_query_time%’; 查看
临时修改阈值:
set global long_query_time =5; ---设置完毕后,重新登陆才生效。
永久设置阈值:
配置文件my.cnf中追加:
在mysqld后追加:
long_query_time =5
查询超过阈值的sql语句:
show global status like ‘%slow_queries%’ ;
慢查询的日志被记录到日志里面,可以通过查看日志 找到具体的mysql。
或者:
通过工具mysqldumpslow查看 mysqldumpslow --help 选项方便查找
s:排序方式
r:逆序
l:锁定时间
g:正则匹配模式
比如:
获取返回记录最多的3个sql
mysqldumpslow -s r -t 3 日志文件绝对路径 #在终端中写的
获取访问次数最多的3个
mysqldumpslow -s c -t 3 日志文件绝对路径 按照时间排序,并且前10条包含left join的查询语句sql
mysqldumpslow -s t -t 10 -g ‘left join’ 日志文件绝对路径
语法:mysqldumpslow 各种参数 日志文件绝对路径
九、海量数据
1.模拟海量数据
存储过程/存储函数。
create database testdata; use testdata; create table dept( dno int(5) primary key default 0, dname varchar(20) not null default ' ', loc varchar(30) default ' ' ) engine=innodb default charset=utf8;
create table emp( eid int(5) primary key, ename varchar(20) not null default ' ', job varchar(20) not null default ' ', deptno int(5) not null default 0 )engine=innodb default charset=utf8;
通过存储函数插入海量数据 创建存储函数: 产生随机数和随机字符串。(并且可以指定位数) randstring(5) 产生5位的随机字符串,模拟员工名称。 创建存储函数 产生某一位数的随机字符串 delimiter $ create function randstring(n int) returns varchar(255) begin declare all_str varchar(100) default ' qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'; declare return_str varchar(255) default ' '; declare i int default 0; while i do set return_str = concat(return_str, substring(all_str, floor(rand()*52)+1,1)); set i = i + 1; end while; return return_str; end $ 解释: randstring(n int) 是函数名(参数 类型) returns varchar(255) 返回值的类型和大小 begin end是函数体,里面写逻辑 declare 声明变量 all_str varchar(100) 变量名 类型(大小) 默认是52个大小写字母。后面随机字符从这里面取。 concat(参数1,参数2) 拼接字符串函数 函数赋值变量,前面加set. 参数1:代表给谁拼接 参数2:用什么拼接 substring(all_str, floor(rand()*52)+1,1) 截取字符串。第一个参数是截取的字符串,第二个参数是从哪个位置截取(从1计数的),第三个参数是截取几个字符串。注意: rand() 产生一个[0,1)的随机数 floor(rand()*52)+1 取下整 代表[1,52] return return_str; 返回值 mysql中程序遇到;就结束了,因此要改变结束符。 delimiter $ 指定mysql的结尾符号为 $。那么中间可以用;表示存储函数的分行,而mysql也把这个存储函数当为一个整体。 如果开启了慢查询日志,那么创建存储过程/存储函数会报错。 解决方法: 临时: show variables like '%log_bin_trust_function_creators%'; set global log_bin_trust_function_creators=1; 永久解决: my.cnf的[mysqld]下加入 log_bin_trust_function_creators=1 创建某一位数的随机数字的存储函数 delimiter $ create function ran_num() returns int(5) begin declare i int default 0; set i = floor(rand()*100); return i; end $ 通过存储过程插入海量数据-----》利用之前的存储函数 emp表 delimiter $ create procedure insert_emp(in eid_start int(10), in data_times int(10)) begin declare i int default 0; set autocommit =0; repeat insert into emp values(eid_start+i, randstring(5), ' other', ran_num()); set i = i + 1; until i = data_times end repeat; commit; end$ 解释: insert_emp(in eid_start int(10), in data_times int(10)) 过程名(in 参数名1 类型1(大小), in 参数名2 类型2(大小)) 第一个参数表示的是从哪个序号开始,第二个参数表示插多少次 set autocommit =0; 将自动提交关闭,这样不用每插入一条就提交一次;插入所有的再提交,这样速度更快 until 是repeat循环的退出条件,注意没有;结尾 dept表 create procedure insert_dept(in dno_start int(10), in data_times int(10)) begin declare i int default 0; set autocommit =0; repeat insert into dept values(dno_start+i, randstring(6), randstring(8)); set i = i + 1; until i = data_times end repeat; commit; end$ 真正开始插入数据:调用存储过程call delimiter ; call insert_emp(1000, 80000); call insert_dept(10, 30) ; 查看数据量 select count(1) from emp; |
2.分析海量数据profile
方法一:profiles
show profiles; 默认是关闭的,为空
show variables like '%profiling%' ;
set profiling=on; #打开
show profiles;
set profiling=on; 打开之后,才会记录之后的sql语句。
方法二:精确分析sql诊断
但上面不怎么精确。只能看到总共消费的时间,不能看到各个硬件消费的时间。
精确分析sql诊断:(把profiles精确下)
show profile all for query 上一步查询的Query_ID
show profile cpu, block io for query 2;
方法三:全局查询日志
会记录开启之后的全部sql语句(很费性能)(这些全局记录操作 仅仅在调优、开发过程中打开即可,在最终部署实施时一定要关闭)
show variables like '%general_log%';
记录到mysql自带的general_log表中
set global general_log=1; 开启全局日志
set global log_output = 'table '; 将sql记录到表里去
或者记录到文件中
set global log_output ='file';
set global general_log=1;
set global general_log_file = '路径+文件'
开启后,会记录sql:mysql.general_log表中
select * from mysql.general_log;
十、锁机制
1.锁的分类
解决因资源共享, 而造成的并发问题。
分类:
操作类型:读锁、写锁
操作范围:表锁、行锁、页锁
读锁:也称为共享锁,对同一个数据,多个读操作可以同时进行,互不干扰。
写锁,也称为互斥锁,如果当前写操作没有完毕,则无法进行其他的读操作、写操作。
表锁:一次性对一张表整体加索。如Myisam默认用表锁,特点是开销小,加索快,无死锁。但缺点是锁的范围大,容易发生锁冲突,并发度低。
行锁:一次性对一条数据加索。如Innodb使用行锁,特点是开销大,加锁慢,容易出现死锁。锁的范围较小,不容易发生冲突,并发度高。(很小概率发生高并发问题:脏读、不可重复读、幻读)
2.表锁
(1)表锁操作
加锁方法:
lock table 表名1 read/write 给表加读/写锁
lock table 表名1 read/write, 表名2 read/write; 给多张表加读/写锁
查看加锁的表:
show open tables;
释放锁:unlock tables;
(2)总结:
(加读锁)
如果某一会话对A表加了读锁,则该会话可以对A表进行读操作,不能进行写操作,且当前会话不能对其他表进行读写操作;对于其他会话,可以对加锁以外的其他表做任何操作(读写),对加锁的表A,只能进行读操作,写操作需要等待,直到释放锁。
(加写锁)
如果某一会话对A表加了写锁,当前会话可以对当前表做读写操作,但是不能操作(读写)其他表。其他会话对加写锁的表A 做任何操作(读写)都会等待,直到释放锁。
(3)mysql表级锁的锁模式:
myisam在执行查询语句前,会自动给涉及的所有表加读锁,在增删改前,会自动给涉及的表加写锁。因此对myisam的表操作会出现:
- 对myisam的表进行读操作(加读锁),不会阻塞其他进程对同一表的读操作,但会阻塞同一表的写操作。只有当读锁释放后,才会执行其他进程的写操作
- 对myisam表的写操作(加写锁),会阻塞其他进程对同一表的读操作和写操作,只有当锁释放时,才会执行其他进程 的操作。
(4)分析表锁定
查看哪些表加锁:show open tables; 1代表被加锁
分析表锁定的严重性:show status like 'table% '; 空的?
table_locks_immediate :即可能获取到的锁数
table_locks_waited : 需要等待的表锁数(如果该值越大,存在越大的锁竞争)
一般建议用二者 的比值做个权衡table_locks_immediate/table_locks_waited > 5000,采用innodb引擎,否则使用myisam引擎。(立刻能够获得资源相对比较多的时候,用行锁innodb,否则用表锁myisam)
2.行锁(innodb):
mysql默认自动commit,为研究行锁,暂时关闭自动commit
关闭自动提交:set autocommit=0;或者利用事务begin
以后每写一sql语句,都要手动commit
总结:
如果某会话对某条数据a进行增删改操作(关闭自动commit的情况下),则其他会话必须等待那条会话事务结束后(commit、rollback),才能对数据a进行操作。
表锁通过unlock tables;来解锁,也可以通过事务来解锁,行锁通过事务解锁。
行锁对不同数据在不同会话中间互不影响。(行锁一次锁一行数据,如果操作不同数据,互不干扰)
行锁的注意事项:
- 如果没有索引,则行锁会转为表锁。(这个没有索引是从实际操作中是否有索引来说的)
- 行锁的一种特殊情况:间隙锁:值在范围内,却不存在。
间隙特点:mysql会自动给间隙加锁。
行锁:如果有where,则实际加锁的范围就是where后面的范围,不是实际的值。
行锁:innodb默认采用
优点:并发能力强,效率高
缺点:比表锁性能损耗大(每次只锁一行,粒度小)
建议:高并发用innodb,否则用myisam
行锁分析:
show status like '%innodb_row_lock%';
Innodb_row_lock_current_waits:当前正在等待的数量
Innodb_row_lock_time :等待总时长。从系统启动到现在一共等待的时间
Innodb_row_lock_time_avg
Innodb_row_lock_time_max
Innodb_row_lock_waits 等待次数。从系统启动到现在一共等待的次数
如果仅仅是查询数据,能否加锁?
for update对select语句加锁。
select * from student where id=2 for update; (关闭自动提交才能出效果)
十一、主从复制(集群的一种方式)
优点:负载均衡;失败迁移
主数据库进行增删改查,再将数据同步到从数据库。读写分离,将读操作放到从数据库,将写操作放到主数据库。
windows部署主数据库,linux部署从数据库。
windows安装mysql(把之前安装的卸载掉):先用自带卸载工具卸载mysql;然后手动删除mysql(隐藏文件)缓存文件(没找到??);再删除注册表regedit中mysql相关的。
授权远程访问:在a计算机访问b计算机的数据库,就需要在b计算机的mysql里授权: grant all privileges on *.* ‘root’ @’%’ identified by ‘123’ with grant option;
flush privileges;
如果仍保错,可能是防火墙的原因:关闭b 的防火墙:services iptables stop
怎么实现同步(主从复制)?
原理:同步的核心:二进制日志。所有对数据的增删改查操作都会在二进制日志文件中记录。那么这个日志文件怎么从主数据库到从数据库?从数据库一开启就有一个io线程,去读写主数据里的二进制文件,然后将写的数据放到从数据库的relay log里去。通过relay log就可以拿到二进制日志里新增或者改变的所有数据。最终sql线程将该数据放到自己从数据库里。
原理步骤:
(1)master将改变的数据 记录在本地的二进制文件中;该过程称之为二进制日志事件
(2)slave将master的二进制文件拷贝到自己的relay log(中继日志文件)里。
(3)中继日志事件:将数据读取到自己的数据库中
mysql要实现主从复制 是异步的, 串行化的,有延迟的。
master:slave = 1:n