MySQL高级-知识总结(索引优化、查询截取分析、MySQL锁机制、主从复制)完结

目录

1前言

2索引优化分析

2.1问题

2.2常见通用的Join查询⭐

2.3手写和机读的不同

2.4索引

2.4.1索引的定义

2.4.2索引的优势和劣势

2.4.3索引分类

2.4.4索引结构

2.4.5哪些情况需要建索引

2.5性能分析

2.5.1MySQL Query Optimizer

2.5.2MySQL常见的瓶颈

2.5.3Explain⭐

2.5.4sql语句优化规则⭐

3查询截取分析

3.1查询优化

3.1.1永远小表驱动大表

3.1.2order by关键字优化⭐

3.2慢查询日志

3.2.1slow_query_log⭐

3.2.2mysqldumpslow⭐

3.3批量数据脚本⭐

3.4Show Profiles⭐

4、MySQL锁机制

 4.1表锁(偏读)---MyISAM

4.1.1加了读锁的情况

4.1.2加了写锁的情况

4.1.3表锁分析

4.2行锁(偏写)---InnoDB

4.2.1行锁特点

4.2.2间隙锁

4.2.3对一行加锁

4.2.4行锁分析

4.2.5优化建议⭐

4.3总结⭐

5、主从复制

5.1复制的基本原理

5.2复制的基本原则

5.3复制的最大问题


1前言

对于现在市场上的数据库操作需求而言,如果你仅仅只会增删改查,就太low了。日前数据量指数级增加,对高并发等问题的解决成了对数据库操作的基本功,所有对于大厂来说,肯定会考数据库相关的(非增删改查)知识。

优化sql数据库分析步骤:
    1、慢查询的开启并捕获
            观察,至少跑一天,看看生产的慢sql情况
            开启慢查询日志,设置阈值,超过阈值的就是慢sql,并将他抓取出来
    2、explain+慢sql分析
    3、show profile查询sql在MySQL服务器里面的执行细节和生命周期情况
    4、sql数据库服务器的参数调优。

2索引优化分析

2.1问题

性能下降sql慢,执行时间太长,等待时间太长

原因:

1、索引失效
2、查询语句写的烂
3、关联查询太多join(设计缺陷或不得已的需求)
4、服务器调优及各个参数设置(缓存、线程数等)

2.2常见通用的Join查询⭐

ps:MySQL并不支持full outer语法(oracl支持)。

怎么办?

对于上图左下角的查询(AB并集):
    可以把左上角和右上角的查询加起来,就是最下角的查询(AB并集):
    select * from tableA A left join tableB B on A.key=B.key
    union
    select * from tableA A right join tableB B on A.key=B.key;
对于上图右下角的查询:
    同理:
    select * from tableA A left join tableB B on A.key=B.key where B.key is null
    union
    select * from tableA A right join tableB B on A.key=B.key where A.key is null;

2.3手写和机读的不同

手写:

上面的语句再MySQL眼中的(机读):

分析:

2.4索引

2.4.1索引的定义

索引是一种数据结构。
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
你可以简单理解为“排好序的快速查找数据结构”

索引的数据结构:
    在数据之外,数据库系统还维护这满足特定查找算法的数据结构,
    这些数据结构以某种方式引用数据。
    这样就可以在这些数据结构上实现高级查找算法,
    这种数据结构,就是索引。

    我们平常所说的索引,如果没有特别指明,
    都是指B树(多路搜索树,并不一定是二叉的)数据结构组织的索引
        其中聚集索引,次要索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引
    统称索引。
        除了B+树,还有哈希索引(hash index等)

关键字:
    1、排序
    2、快速查找
    3、数据结构

索引的作用:
    假设你去图书馆(藏书1000万本)找玄幻小说《斗破苍穹》,
    如果没有这个图书馆索引,就就要一本一本的去找,即全局遍历
    如果这个图书馆有索引,玄幻小时再二楼,天蚕土豆的小说在第二个书架,
    那你会很快找到书的位置,即局部遍历
    这就是索引的作用。

一种可能的索引方式示例:

 

2.4.2索引的优势和劣势

优势:

1、类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本
2、通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

劣势:

1、实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所有索引列也是要占用空间的。
2、虽然索引大大提高了查询速度,同时却会降低更新表的速度。
    如对表进行insert、update和delete。
    因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新索引列的字段,
    都会调整因为更新所带来的键值变化后的索引信息。
3、索引注释提高效率的一个因素,如果你的MySQL有大大数据量的表,
    就需要花时间研究建立最优秀的索引,或优化查询。

2.4.3索引分类

单值索引:即一个索引只包含单个列,一个表可以有多个单列索引。
唯一索引:索引列的值必须唯一,但允许有空值。
复合索引:一个索引包含多个列
基本语法:
    创建:CREATE [UNIQUE] INDEX indexName ON mytable(colNAme(length));
         ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (colName(length));
    删除:DROP INDEX [indexName] ON mytable;
    查看:SHOW INDEX FROM table_name\G;

                 

2.4.4索引结构

1、BTree索引
2、Hash索引
3、full-text全文索引
4、R-Tree索引

由B+树为例

           

上图为一颗B+树
浅蓝色的块我们称之为磁盘块,可以看到每个磁盘块包括几个数据项(深蓝色)和指针(黄色),
P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。

真实的数据存在于叶子节点(第三层)。
非叶子节点不存储真实的数据,只存储指引搜索的方向。

查找过程

如果要查找数据项29:

    首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,
        在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针。

    通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,
        29在26到30之间,锁定磁盘块3的P2指针

    通过指针加载磁盘块8到内存,发生第三次IO,
        同时内存中做二分查找找到29,结束查询。

    总计三次IO。

2.4.5哪些情况需要建索引

建索引:
1、主键自动建立唯一索引
2、频繁作为查询条件的字段应该创建索引
3、查询中与其他表关联的字段,外键关系建立索引
6、单键/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
7、查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
8、查询中统计或者分组字段

不建索引:
1、表记录太少
2、Where条件里用不到的字段不创建索引
3、一个字段的值不一样的数据太少
    例如:
        有性别1000条数据,而值只有男或者女。2/1000,比例太小,不适合建索引
4、经常增删改的表

2.5性能分析

2.5.1MySQL Query Optimizer

MySQL中有专门负责优化SELECT语句的优化器模块。
主要功能:
    通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他(MySQL的作者)认为最优的执行计划。
    (他认为最优,但不见得实际情况最优,这部分最耗费时间。
      所以很多的大厂,像阿里巴巴,用的MySQL就会修改这个模块的策略)

2.5.2MySQL常见的瓶颈

CPU:CPU饱和的时候一般发生在数据装入内存或从磁盘上读取数据的时候

IO:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候

服务器硬件的性能瓶颈:top,free,iostat和vmstat来查看系统的性能状态

2.5.3Explain⭐

是什么:

使用EXPLAIN关键字可以模拟优化器查询SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。
分析你的查询语句或表结构的性能瓶颈

用法:
explain+sql语句

能干嘛:

1、表的读取顺序(id)
2、数据读取操作的操作类型(select_type)
3、哪些索引可以使用(possible_key)
4、哪些索引被实际使用(key)
5、表之间的引用(ref)
6、每张表有多少行被优化器查询(rows)

查询出来的字段:

如何判断:

1、id---->select查询的序列号包含一组数字,表示查询中执行select字句或操作表的顺序。

三种情况:
1、id相同,执行顺序由上至下
2、id不同,如果是子查询,id的序号会递增,id的值越大优先级越高,越先被执行
3、id相同不同,同时存在
    id值的大先执行,id相同的由上至下顺序执行。

2、select_type

类型:

1、SIMPLE:简单的select查询,查询中不包含子查询或UNION。
2、PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为。
3、SUBQUERY:在SELECT或WHERE列表在包含的子查询。
4、DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)
            MySQL会递归执行这些子查询,把结果放在临时表里。
5、UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;
            若UNION包含在FROM字句的子查询中,外层SELECT被标记为:DERIVED
6、UNION RESULT:从UNION表获取结果的SELECT

3、table----->表示这一行的数据是关于哪个表的

4、type---->显示查询使用了何种类型

从最好到最差(最常用的):
system > const > eq_ref > ref > range > index > all

system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个可以忽略不计。

const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。
       因为只匹配一行数据,所以很快。比如银行卡号(常量)。
       如将主键置于where列表中,MySQL就能将该查询转换为一个常量。

eq_ref:唯一性索引扫描,对于每个索引建,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。

ref:非唯一性索引扫码,返回匹配某个单独值的所有行。
        本质上也是一种索引访问,他返回首页匹配某个单独值的行,然而,
        他可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。

range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引。
        一般检索在你的where语句中出现了between、<、>、ind等的查询
        这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某个点,而结束于另一个点,
        不用扫描全部索引。
        比如:查询昨天的所有订单。

index:遍历索引树以找到匹配的行

all:将遍历全表以找到匹配的行

(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)

5、possible_key--->显示可能应用在这张表中的索引(理论上应该使用哪些索引)

6、key----->实际使用的索引。如为NULL,则没有使用索引

7、key_len------>表示(他希望)索引中使用的字节数

可以通过该列计算查询中使用的索引的长度。
在不损失精度的情况下,长度越短越好。
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,
即key_len是根据表定义计算而得,不是通过表内检索出的。

8、ref---->显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常数被用于查出索引列上的值

9、rows----->根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数

观察rows列:
    建索引之前一共扫描了641行,
    建完索引,只扫描了143行。

10、Extra---->包含不适合在其他列显示但十分重要的额外信息

最好不要出现:
1、Using filesort:说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。
                    MySQL中无法利用索引完成的排序操作称为“文件排序”。

2、Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序是使用临时表。
                    常见于排序order by和分组查询group by。会拖慢查询时间。

最好出现:
1、Using index:表示相应的select操作中使用了覆盖索引(Covering Index),
                避免了访问了表的数据行,效果不错。(key=possible_key)
                如果同时出现using where,表明索引被用来执行索引键值的查找。
                如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
    覆盖索引:就是select的数据列只用从索引中就能够取得,不必读取数据行,
             MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,
             换句话说查询列要被所建的索引覆盖。

其他:
1、Using where:表示使用了where过滤。
2、Using join buffer:使用了连接缓存。
3、impossible where:where子句的值总是false,不能用来获取任何元组。
4、select tables optimized away:在没有groupby子句去情况下,基于索引优化min/max操作或者
                                 对于myisam存储引擎优化count(*)操作,不必等到执行阶段再进行计算
                                 查询执行计划生成的阶段即完成优化。
5、distinct:优化distinct操作,在找到第一个匹配的元组后即停止找相同值的动作。

观察Extra:
    第一个sql语句的Extra里出现了using filesort,说明没有使用表内的索引排序,这是非常不好的现象
    第二个sql语句做了优化。
    在实际的情况中,要尽量避免写第一种sql语句,如果出现要尽快优化。

2.5.4sql语句优化规则⭐

1、全值匹配是最好的
2、最佳左前缀法则:如果索引了多列,要遵循最佳左前缀法则。即从索引的最左前列开始并且不跳过索引中的列
3、不在索引列上做任何操作(会导致索引失效)
4、存储引擎不能使用索引中范围条件右边的列
5、尽量使用覆盖索引,减少select *
6、mysql在使用不等于(!=)的时候会导致索引失效
7、is null,is not null会导致索引失效
8、like以通配符开头(%abc。。。)会导致索引失效
9、字符串不加单引号会导致索引失效
10、用or来连接,会导致索引失效


口诀:

    带头大哥不能死(1、2)
    中间兄弟不能断(2)
    索引列上不计算(3、9)
    like%加右边(8)
    范围之后全失效(2)

例题:

面试题:如果我一定要使用like “%abc”作为查询条件,如何对其优化,使得索引不失效?

select 后面跟需要查询的字段,不用 select * 
针对需要查询的字段,建立多值索引。

例如:select * from user where name like '%ab';

优化:
    create index idx_nameAge on user(name,age);
    select name,age from user where name like '%ab';

一般性建议

1、对于单间索引,尽量选择针对当前query过滤性更好的索引。
2、在选择组合索引的时候,当前Query中过滤器最好的字段在索引字段顺序,位置越靠前越好。
3、在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更好字段的索引。
4、尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的。

3查询截取分析

3.1查询优化

3.1.1永远小表驱动大表

3.1.2order by关键字优化⭐

提高order by的速度

1、order by时select *是个大忌,只query需要的字段非常重要。
    1.1、当Query的字段总和小于max_length_for_sort_data而且排序字段不是TEXT|BLOB类型时,
         会用改进后的算法--单路排序,否则用老算法--多路排序。
    1.2、两种算法的数据都有可能超出sort_buffer的容量,超出之后,会创建temp文件进行合并排序,
         导致多次I/O,但是用单路排序算法的风险更大一些,所以要提高sort_buffer_size

2、尝试提高sort_buffer_size
    不管用那种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,
    因为这个参数是针对每个进程的。

3、尝试提高max_length_for_sort_data
    提高这个参数,会增加用改进算法的概率。但是如果设的太高,
    数据总量超出sort_buffer_size的概率就增大,明显症状是高的磁盘I/O活动和低的处理器使用率。

例题

        

优化策略

1、group by是指是先排序后进行分组,遵循索引建的最佳左前缀。
2、当无法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置。
3、where高于having,能写在where限定的条件就不要去having限定了。

3.2慢查询日志

3.2.1slow_query_log⭐

MySQL的慢查询日志是MySQL提供的一种查询日志记录,它用了记录在MySQL中响应时间查过阈值的语句,具体指运行时间超过long_query_time值的sql,则会被记录到慢查询日志中。

默认情况下,MySQL数据库没有开启慢查询日志,需要手动设置。

set global slow_query_log=1;

默认情况下,long_query_time的值为10秒(大于而非大于等于)。

show variables like 'long_query_time%';

查看是否开启慢查询日志,和慢查询日志的地址。

show variables like '%slow_query_log%';

3.2.2mysqldumpslow⭐

日志分析工具mysqldumpslow。使用此命令可以对慢查询日志进行分析。

得到返回数据集最多的10个SQL。

mysqldumpslow -s r -t 10 /var/lib/mysql/root-slow.log;

 得到访问次数最多的10个SQL。

mysqldumpslow -s c -t 10 /var/lib/mysql/root-slow.log;

 得到按照时间排序的前10条里面含有左连接的查询语句。

mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/root-slow.log;

 另外建议在使用这些命令时结合 | 和more使用,否则有可能出现爆屏情况。

mysqldumpslow -s r -t 10 /var/lib/mysql/root-slow.log | more;

查看mysqldumpslow的帮助信息

s:是表示按照何种方式访问
c:访问次数
l:锁定时间
r:返回记录
t:查询时间
al:平均锁定时间
ar:平均返回记录数
at:平均查询数据
t:即为返回前面多少条的数据
g:后面搭配一个正则匹配模式,大小写不敏感的

3.3批量数据脚本⭐

向数据库自动插入(1000万)随机数据

1、建库和建表

# 新建库
create database dbtest;
use dbtest;
 
#1 建表dept
CREATE TABLE dept(  
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,  
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,   
dname VARCHAR(20) NOT NULL DEFAULT "",  
loc VARCHAR(13) NOT NULL DEFAULT ""  
) ENGINE=INNODB DEFAULT CHARSET=UTF8 ;  
 
#2 建表emp
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=UTF8 ;

2、设置参数log_bin_trust_function_creators

创建函数,假如报错:This function has none of DETERMINISTIC......
# 由于开启过慢查询日志,因为我们开启了 bin-log, 我们就必须为我们的function指定一个参数。
 
show variables like 'log_bin_trust_function_creators';
 
set global log_bin_trust_function_creators=1;
 
# 这样添加了参数以后,如果mysqld重启,上述参数又会消失,永久方法:
 
windows下my.ini[mysqld]加上log_bin_trust_function_creators=1 
 
linux下/etc/my.cnf下my.cnf[mysqld]加上log_bin_trust_function_creators=1

3、创建函数,保证每条数据都不同

#用于随机产生字符串
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
BEGIN    ##方法开始
 DECLARE chars_str VARCHAR(100) DEFAULT   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; 
 ##声明一个 字符窜长度为 100 的变量 chars_str ,默认值 
 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));
##concat 连接函数  ,substring(a,index,length) 从index处开始截取
 SET i = i + 1;
 END WHILE;
 RETURN return_str;
END $$
 
#假如要删除
#drop function rand_string;
#用于随机产生部门编号
DELIMITER $$
CREATE FUNCTION rand_num( ) 
RETURNS INT(5)  
BEGIN   
 DECLARE i INT DEFAULT 0;  
 SET i = FLOOR(100+RAND()*10);  
RETURN i;  
 END $$
 
 
#假如要删除
#drop function rand_num;

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 把autocommit设置成0  ;提高执行效率
 SET autocommit = 0;    
 REPEAT  ##重复
 SET i = i + 1;  
 INSERT INTO emp(empno, ename ,job ,mgr ,hiredate ,sal ,comm ,deptno ) VALUES ((START+i) ,rand_string(6),'SALESMAN',0001,CURDATE(),FLOOR(1+RAND()*20000),FLOOR(1+RAND()*1000),rand_num());  
 UNTIL i = max_num   ##直到  上面也是一个循环
 END REPEAT;  ##满足条件后结束循环
 COMMIT;   ##执行完成后一起提交
 END $$
 
#删除
# DELIMITER ;
# drop PROCEDURE insert_emp;
#执行存储过程,往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,loc ) VALUES (START +i ,rand_string(10),rand_string(8));  
 UNTIL i = max_num  
 END REPEAT;  
 COMMIT;  
 END $$ 
 
#删除
# DELIMITER ;
# drop PROCEDURE insert_dept;

5、调用存储过程

#执行存储过程,往dept表添加10条数据测试一下,deptno从100开始到110
DELIMITER ;
CALL insert_dept(100,10); 

 

#执行存储过程,往emp表添加50万条数据(我的mysql执行了46s,大约1秒插入一万条数据)
CALL insert_emp(100001,500000); 

要想在windows下用navicat连接linux的MySQL,需要关闭Linux的防火墙或者开发端口,还要设置mysql的权限。

navicat连接Linux MySQL教程:
https://blog.csdn.net/qq_42391248/article/details/105483808

3.4Show Profiles⭐

是MySQL提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于sql的调优的测量。

默认关闭状态(MySQL牛逼的功能都是默认关闭的呀)

#查看是否开启profiling
show variables like 'profiling';

打开profiling

set profiling=on;

查看结果

show profiles;

诊断sql

show profile cpu,block io for query 6;
#数字为上一把的query_id

日常开发需要注意的结论(status出现了以下四个结果,说明这个sql有问题,需要优化):

converting HEAP to MyISAM:查询结果太大,内存不够用了往磁盘上搬。

Creating tmp table:创建临时表。
                        1、拷贝数据到临时表。
                        2、用完再删除。

Copying to tmp table on disk:把内存中临时表复制到磁盘,危险!

locked

4、MySQL锁机制

从对数据操作的类型(读/写):

1、读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。

2、写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。

查看表是否加锁

show open tables;
#1表示被锁了,0表示安全

对emp表加读锁,对detp表加写锁

lock table emp read,detp write;

 解开所有锁

unlock tables;

 4.1表锁(偏读)---MyISAM

4.1.1加了读锁的情况

管理员1对一个表加了读锁之后:

1、管理员1只可读此表,不可修改此表(修改会报错);不可读其它表,不可修改其他表

2、其他管理员可读,不可修改(此修改会被堵塞,当管理员1解开锁时,修改才能完成)

4.1.2加了写锁的情况

管理员1对一个表加了写锁之后:

1、管理员1可读写此表,不可读写其他表;

2、其他管理员不可读写此表(读写操作会被堵塞,当管理员1解开锁时,读写操作才能完成)

总而言之,竞赛读锁会阻塞写,但不会阻塞读。而写锁则会把读和写都阻塞。

4.1.3表锁分析

show status like 'table%';

 

Table_locks_immediate:产生表级锁定的次数,表锁可以立即获取锁的查询次数,没立即获取锁值加1;

Table_locks_waited:出现表锁锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在着较严重的表级锁争用情况。

此外,Myisam的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。

4.2行锁(偏写)---InnoDB

4.2.1行锁特点

行锁偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发送锁冲突的概率最低,并发度也最高。

InnoDB与MyISAM的最大不同有两个特点:一是支持事务;二是采用了行级锁。

索引失效会导致行锁变表锁---会导致其他的事务阻塞而使得系统变慢。

4.2.2间隙锁

当我们用范围条件而不是相等条件检索数据,并请求共享或排它锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值再条件范围但并不存在的记录,叫做“间隙(GAP)”;

InnoDB也会对这个“间隙”加锁,这种锁机制竞赛所谓的间隙锁(Next-Key锁)。

危害:

因为Query执行过程中通过范围查找的话,它会锁定整个范围内所有的索引键值,即使整个键值并不存在。

间隙锁有一个比较致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成再锁定的时候无法插入键值范围内的任何数据。再某些场景下这可能会对性能造成很大的危害。

4.2.3对一行加锁

begin;
select * from tablename where a=8 for update;
#锁定a=8这一行,其它人的对这一行的操作会被阻塞,知道锁定行的会话提交。
commit;

4.2.4行锁分析

show status like 'innodb_row_lock%';

 

从上到下(标红了是比较重要的三项):

1、当前正在等待锁定的数量;

2、从系统启动到现在锁定总时间长度(等待总时长);

3、每次等待所花平均时间(等待平均时长);

4、从系统启动到现在等待最长的一次所花的时间;

5、系统启动后到现在总共等待的次数(等待总次数);

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后分析结果着手指定优化计划。

4.2.5优化建议⭐

1、尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁。

2、合理设计索引,尽量缩小锁的范围。

3、尽可能较少检索条件,避免间隙锁。

4、尽量控制事务大小,减少锁定资源量和时间长度。

5、尽可能低级别事务隔离。

4.3总结⭐

InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要高一些,但是再整体并发处理能力方面要远远优于MyISAM的表级锁定的。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势了。

但是InnoDB的行级锁定同样也有其脆弱的一面(间隙锁),当我们使用不当的时候,可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能会更差。 

5、主从复制

5.1复制的基本原理

主从复制,是用来建立一个和主数据库完全一样的数据库环境,称为从数据库(slave)。

主数据库一般是准实时的业务数据库。您看,像在mysql数据库中,支持单项、异步赋值。在赋值过程中,一个服务器充当主服务器,而另外一台服务器充当从服务器。

此时主服务器会将更新信息写入到一个特定的二进制文件中。并会维护文件的一个索引用来跟踪日志循环。这个日志可以记录并发送到从服务器的更新中去。当一台从服务器连接到主服务器时,从服务器会通知主服务器从服务器的日志文件中读取最后一次成功更新的位置。然后从服务器会接收从哪个时刻起发生的任何更新,然后锁住并等到主服务器通知新的更新。

5.2复制的基本原则

每个slave只有一个master。

每个slave只能有一个唯一的服务器ID。

每个master可以有多个slave。

5.3复制的最大问题

延时。

比如一主一从的配置:

既然是两台机器,必然会出现网络,线程。。等原因,造成从机不能及时的同步主机信息。这种问题是不可避免的。部分公司应用主从的时候,往往将主机用来写,从机用来读,这样就很大可能出现读不到最新数据。

解决方案:将已经写入主机(master)的数据,放一份缓存放入redis,去从机(slave)读之前先去redis读就好了,就可以解决主从的延时问题了。

完结!

发布了247 篇原创文章 · 获赞 53 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_42391248/article/details/105487109
今日推荐