Mysql性能调优(九)

前言

  上一篇文章我们介绍了应用的优化、mysql中查询缓存优化、mysql中内存管理优化以及mysql的并发参数调整。接下来我们给大家介绍MySQL中的锁,包括锁的争用情况以及行锁和表锁的相关内容,另外给大家介绍sql中常用的一些技巧,首先给大家介绍锁的相关内容。

一、MySQL锁

1、锁概述

  锁是计算机协调多个进程或线程并发访问的某一种资源机制。在数据库中,除系统的计算资源(包括CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤为重要,也更加复杂。

2、锁分类

  从对数据操作的粒度分为以下几种:

  • ①、表锁: 操作时,会锁定整个表
  • ②、行锁: 操作时,会锁定当前操作行

  从对数据操作的类型分为以下几种:

  • ①、读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
  • ②、写锁(排它锁):当前的操作没有完成前,它会阻断其他写锁和读锁。

3、MySQL锁

  相对其他数据库而言,MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制,下表给出了各个存储引擎对锁的支持情况:

  MySQL这3种锁的特性可大致归纳如下:

  从上述特点可见,很难笼统地说哪种锁更好,只能就具体应用的特点来说哪种锁更合适。仅从锁的角度来说:表级锁更适合于以查询为主,只有少量按索引条件更新数据应用,如Web应用;而行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

4、MyISAM表锁

  MyISAM存储引擎只支持表锁,这也是MySQL开始几个版本中唯一支持的锁类型。

  • 1、如何加表锁
      MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATEDELETEINSERT)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显示加锁。具体的命令如下:
加表锁:lock table table_name read;
加写锁:lock table table_name write;

  我们首先创建一张名为tb_book的表,并且插入一些数据,具体代码如下:

create datebase demo_03 default charset=utf8mb4;
use demo_03;
create table `tb_book`(
	`id` int(11) auto_increment,
	`name` varchar(50) default null,
	`publish_time` date default null,
	`status` char(1) default null,
	primary key(`id`)
)engine=myisam default charset=utf8;
insert into tb_book(id, name, publish_time, status)values(null, 'mysql从入门到精通','2020-01-01','1');
insert into tb_book(id, name, publish_time, status)values(null, 'java从入门到精通','2020-02-02','0');

create table `tb_user`(
	`id` int(11) auto_increment,
	`name` varchar(50) default null,
	primary key(`id`)
)engine=myisam default charset=utf8;
insert into tb_suer(id, name) values(null, 'stefan');
insert into tb_suer(id, name) values(null, 'napoleon');
  • 2、写锁案例
      获得tb_book表的写锁
lock table tb_book write;

  执行查询操作

select * from tb_book;

  执行结果如下:

  查询操作执行成功。
  执行更新操作

update tb_book set name = 'java编程思想(第二版)' where id = 1;

  执行结果如下:

  更新操作执行成功。
  因此,锁模式的相互兼容性如表所示:

  由上表可见:

1)、对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
2)、对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;

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

  • 3、查看锁的争用情况
show open tables;

  执行结果如下:

  • In_user : 表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。
  • Name_locked :表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作。
show status like 'Table%';

  执行结果如下:

  Table_locks_immediate:指的是能够立即获得表级锁的次数,每立即获取锁,值加1
  Table_locks_waited:指的是不能立即获取表级锁而需要等待的次数,每等待一次,该值加1,此值高说明存在着较为严重的表级锁争用情况。

5、InnoDB行锁

  行锁的特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。InnoDB与MyISAM的最大不同有两点:一是支持事务;而是采用了行级锁
  事务是由一组SQL语句组成的逻辑处理单元,事务具有以下4个特性,简称为事务ACID属性。

  并发事务处理带来的问题

  • 1、事务隔离级别
      为了解决上述提高的事务并发提问,数据库提供了一定的事务隔离机制来解决这个问题。数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使用事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。
      数据库的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏写、脏读、不可重复读、幻读这几类问题。

      这里需要注意的是:√代表可能出现,x代表不会出现。MySQL的数据库的默认隔离级别为Repeatable read,查看方式为:
show variables like 'tx_isolation'; 

  执行结果如下:

  我们应该创建一个表锁,具体如下:

create table test_innodb_lock(
	id int(11),
	name varchar(16),
	sex varchar(1)
)engine=innodb default charset=utf8;
insert into test_innodb_lock values(1, '100', '1');
insert into test_innodb_lock values(3, '3', '1');
insert into test_innodb_lock values(4, '400', '0');
insert into test_innodb_lock values(5, '500', '1');
insert into test_innodb_lock values(6, '600', '0');
insert into test_innodb_lock values(7, '700', '0');
insert into test_innodb_lock values(8, '800', '1');
insert into test_innodb_lock values(9, '900', '1');
insert into test_innodb_lock values(10, '200', '0');
create index idx_test_innodb_lock_id on test_innodb_lock(id);
create index idx_test_innodb_lock_name on test_innodb_lock(name);
  • 2、行锁的基本演示
      首先关闭自动提交功能
set autocommit = 0;

  执行结果如下:

  可以正常的查询出全部数据

select * from test_innodb_lock;

  执行结果如下:

  查询id为3的数据;

select * from test_innodb_lock where id = 3;

  执行结果如下:

  更新id为3的数据;

update test_innodb_lock set name = 'A1' where id = 3;

  执行结果如下:

  通过commit提交事务,解除阻塞,更新正常进行

commit;

  执行结果如下:

  不难看的出,操作的都是同一行的数据,接下来,演示不同行的数据;

update test_innodb_lock set name = 'B1' where id = 3;
update test_innodb_lock set name = 'C1' where id = 5;

  执行结果如下:

  • 3、无索引行锁升级为表锁
      如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样。查看当前表的索引
show index from test_innodb_lock;

  执行结果如下:

  首先关闭事务的自动提交

set autocommit = 0;

  执行结果如下:

  接下来执行更新语句

update test_innodb_lock set sex = '2' where name = 400;
update test_innodb_lock set sex = '2' where id = 9;

  接下来执行更新语句

  最后提交事务

commit;

  接下来执行更新语句

  • 4、间隙锁危害
      当我们用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-key锁)。具体如下:
  • 5、InnoDB行锁争用情况
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:系统启动后到现在总共等待的次数

  当等待的次数很高,而且每次等待的时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。
  总之,InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面带来了性能损耗可能会比表锁会更高一些,但是在整体并发处理能力方面要远远大优于MyISAM的表锁的。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有明显的优势。但是,InnoDB的行级锁同样也有其脆弱的一面,当我们使用不当的时候,可能会让InnoDB的整体性能表现不仅能比MyISAM高,甚至可能会更差。接下来是优化的建议:

  • 尽可能让所有数据检索都能通过索引来完成,避免无索引行锁升级为表锁
  • 合理设计索引,尽量缩小锁的范围
  • 尽可能减少索引条件,及索引范围,避免间隙锁
  • 尽量控制事务大小,减少锁定资源量和时间长度
  • 尽可能使用低级别事务隔离(但是需要业务层面满足需求)。

二、常用SQL技巧

1、SQL执行顺序

  我们在编写语句的过程如下:

select distinct <select list>
from <left_table> <join_type>
join <right_table> on <join_condition>
where <where_condition>
group by <grop_by_list>
having <having_condition>
order by <order_by_condition>
limit <limit_params>;

  执行过程如下:

from <left_table>
on <join_condition>
<join_type> join <right_table>
where <where_condition>
group by <grop_by_list>
having <having_condition>
select distinct <select list>
order by <order_by_condition>
limit <limit_params>;

2、MySQL中的各种函数

  正则表达式是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。

  具体的命令如下:

select * from emp where name regexp '^T';
select * from emp where name regexp '2$';
select * from emp where name regexp '[uvw]';

  接下来是MySQL中数学求和等一系列函数,具体如下:

  字符串函数,具体如下:

  日期函数,具体如下:

  最后是MySQL聚合函数,具体如下:

总结

  上一篇文章我们介绍了应用的优化、mysql中查询缓存优化、mysql中内存管理优化以及mysql的并发参数调整。本文给大家介绍MySQL中的锁,包括锁的争用情况以及行锁和表锁的相关内容,另外给大家介绍sql中常用的一些技巧,包括sql语句执行的顺序以及sql中的各大函数。因此,mysql是很重要的一个技能,几乎计算机中的每个岗位都需要一个mysq技能,因此,需要我们特别的掌握。生命不息,奋斗不止,我们每天努力,好好学习,不断提高自己的能力,相信自己一定会学有所获。加油!!!

猜你喜欢

转载自blog.csdn.net/Oliverfly1/article/details/111715323