Mysql客户端和服务器之间的通讯协议是“半双工”的,意味着在任何一个时刻,要么是由服务器向客户端发送数据,要么是有客户端向服务器发送数据,这两个动做不能同时发生。当咱们发送一条SQL命令引擎时,MySql执行过程以下图所示mysql
查询缓存
在解析一个查询语句以前,若是查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据,若是命中缓存直接从缓存中拿到结果并返回给客户端。这种状况下,查询不会被解析,不用生成执行计划,不会被执行。sql
语法解析和预处理器
MySQL经过关键字将SQL语句进行解析,并生成一棵对应的“解析树”。MySQL解析器将使用MySQL语法规则验证和解析查询。数据库
查询优化器
语法书被校验合法后由优化器转成查询计划,一条语句能够有不少种执行方式,最后返回相同的结果。优化器的做用就是找到这其中最好的执行计划。缓存
查询执行引擎
在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询执行引擎则根据这个执行计划来完成整个查询。最常使用的也是比较最多的引擎是MyISAM引擎和InnoDB引擎。mysql5.5开始的默认存储引擎已经变动为innodb了。性能优化
下面简单的罗列几点差异:MyISAM类型不支持事务处理等高级处理,强调的是性能,InnoDB支持事物;MyISAM支持表级锁,InnoDB支持行级锁;MyISAM不支持外键,InnoDB支持外键(虽然不建议使用外键约束)。
查询当前默认的引擎能够经过SHOW VARIABLES LIKE '%storage_engine%'语句来查看。服务器
InnoDB是支持事务的,那么到底什么是事务呢?
数据库事务(Database Transaction) ,是指做为单个逻辑工做单元执行的一系列操做,要么彻底地执行,要么彻底地不执行。并发
事物的基本特性(ACID)
原子性(atomicity):
事务中的操做要么都发生,要么都不发生。
一致性 (consistent):
在事务开始和完成时,数据必须保持一致状态(读一致、写一致)。
隔离性 (isolation):
隔离性指并发的事务是相互隔离的。一个事物操做不能对另外一个事物产生影响。
持久性(durable):
事务完成以后,它对于数据的修改是永久性的,即便出现系统故障也可以保持。性能
事务的隔离级别
若是不考虑事物的隔离级别会出现如下三种异常状况测试
幻读和提交读都是读取了另外一条已经提交的事物(这一天与不提交读有区别),所不一样的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据总体。优化
Mysql数据库为咱们定义了四种隔离级别
Repeatable read是mysql默认的隔离级别
InnoDB的锁分类及加锁方法
1、锁分类
InnoDB的锁定模式能够分为四类
按照锁定级别划分为
1.表锁
开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的几率最高,并发度最低;适合于以查询为主,只有少许按索引条件更新数据的应用。
例如MyISAM引擎在执行查询语句的时候会自动给涉及的全部表加读锁,在执行更新操做(UPDATE、DELETE、INSERT)前,自动给涉及的表加写锁。
2.行锁
开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高;适合有大量按索引条件并发更新状况。
InnoDB的行级锁定一样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程当中为了让行级锁定和表级锁定共存,InnoDB也一样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。
当一个事务须要给本身须要的某个资源加锁的时候,若是遇到一个共享锁正锁定着本身须要的资源的时候,本身能够再加一个共享锁,不过不能加排他锁。可是,若是遇到本身须要锁定的资源已经被一个排他锁占有以后,则只能等待该锁定释放资源以后本身才能获取锁定资源并添加本身的锁定。
3.页锁
开销和加锁时间界于表锁和行锁之间,咱们不多场景下会使用。
锁定级别的选择是由存储引擎来自行选择的。
4.间隙锁
当咱们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫作“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。
例如:select * from emp where empid > 100 for update;
是一个范围条件的检索,InnoDB不只会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁。
二. 加锁方法
意向锁是InnoDB引擎自动加的,不须要用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务能够经过如下语句显示给记录集加共享锁或排他锁。
共享锁:SELECT … LOCK IN SHARE MODE
排他锁:SELECT … FOR UPDATE
InnoDB行锁是经过给索引上的索引项加锁来实现的,只有经过索引条件检索数据,InnoDB才使用行级锁,不然,InnoDB将使用表锁。如下几种状况须要避免:
三.死锁
Mysql MyISAM老是一次得到所需的所有锁,要么所有知足,要么等待,所以不会出现死锁。但在InnoDB中,除单个SQL组成的事务外,锁是逐步得到的,当两个事务都须要得到对方持有的排他锁才能继续完成事务,这种循环锁等待就是典型的死锁。
一般来讲,死锁都是应用设计的问题,经过调整业务流程、数据库对象设计、事务大小,以及访问数据库的SQL语句,绝大部分死锁均可以免。
须要注意:
数据类型优化
更短的列一般更好,例如varchar(5)和varhchar(200)存储hello的空间开销是同样的,事实证实更长的列会消耗更多的内存,由于MySQL一般会分配固定大小的内存块来保存内部值。
使用缓存表和汇总表
以网站为例,假设须要计算以前24小时内发送的消息数,在一个繁忙的网站不可能维护一个实时精确的计算器,能够每一小时统计一次,而后把24个小时访问数叠加。
建立高性能的索引
1.使用独立的列
若是不是独立的列,则MySQL就不会使用索引
例如:
Select actor_id from actor where actor_id + 1 = 5;没法使用actor_id列的索引
2.联合索引
不少人对多列索引的理解不够,常见的错误是为每一个列建立独立的索引,或者按错误的顺序建立多列索引。当出现服务器对多个索引作相交操做时(一般是多个AND条件),一般意味着须要一个包含全部相关列的多列索引,而不是多个单独的单列索引。
当服务器须要多个索引作联合操做时(一般有多个OR条件),一般须要耗费大量的CPU和内存资源。一般有两种作法:
①使用union all
Select film_id,actor_id from film_actor where actor_id
Union all
Select film_id,actor_id from film_actor where film_id =1 and actor_id <>1;
②分别对film_id 和 actor_id建立索引。
3.选择合适的索引列顺序
Select * from payment where staff_id = 2 AND customer_id = 584
应该建立一个(staff_id,customer_id)索引仍是应该颠倒一下顺序?哪一个字段选择性更高,选择哪一个字段做为索引列的第一列。customer_id对应筛选掉的数据更多,则索引customer_id应该放在第一位。
4.冗余和重复索引
索引并非越多越好,过多的索引会对系统产生负担,一直未使用的索引应该把它删掉,而不是留在咱们的系统中。
例若有一张参保人表里面有100000行数据,每一个state_id值大概有20000条记录。在state_id列有一个索引对下面查询有用,查询名为Q1:
Select count(*) from table where state_id = 5;
一个简单的测试查询的执行速度每秒115次(QPS)。还有一个查询
Select state_id,city,address from userinfo where state_id = 5;
对于这个查询,测试结果QPS小于10。提高性能最简单的办法就是扩展索引为(state_id,city,address),索引扩展后Q2变快了,可是Q1变慢了,若是咱们想让两个查询都变得更快,就须要两个索引。多个索引的缺点是索引的成本更高。向表中插入100万数据,InnoDB引擎在一个索引状况下须要80秒,两个索引的状况下须要136秒。能够看出表中的索引越多插入速度越慢。
索引和锁
咱们在写查询语句的时候,要利用索引锁住最小范围内的行。
例如:
Select actor_id from sakila.actor where actor_id < 5
And actor_id <> 1 for update
这条语句仅仅返回 2-4,但实际上获取了1-4之间的行的排他锁,InnoDB 会锁住第1行,这是由于MySQL为该查询选择的执行计划是索引范围扫描。
就像这个例子显示的,即便使用了索引,InnoDB也可能锁住一些不须要的数据,若是不使用索引查找和锁定行的话问题可能会更糟,mysql会作全表扫描并锁住全部的行,而无论是否是须要。避免使用多个范围条件:
例如:select actor_id from actor where actor_id > 45
若是能改为select actor_id from actor where actor_id in (46,99)
这两种范围条件查询,MySQL没法再使用范围列后面的索引了,可是对于“多个等值条件查询”则没有这个限制。若是多个范围查询则索引就无能为力了。
查询性能优化
执行计划
执行计划能够查看数据软件是如何扫描表,如何使用索引的,所以明白MySql语句的执行计划,对咱们优化mysql的性能很是有用。
查看执行计划可使用以下语句:
EXPLAIN SELECT * FROM inventory WHERE item_id = 16102176G; 这样的命令时,会给咱们反馈以下信息:
Key: 列指出优化器选择使用的索引
Rows:用于分析结果集中的行数估计值,最好估值是1,通常来讲这种状况发生在当寻找的行在表中能够经过主键或者惟一键找到的时候。
Possible_keys:指出优化器为查询选定的索引
Key_len: 列定义了用于SQL语句的链接条件的键的长度。
Table:这个值多是个是代表、表的别名或者一个为查询产生临时表的标识符
Select_type:
提供了表示table列引用的使用方式的类型。最多见的值包括simple、primary,derived和union
extra
extra列提供了有关不一样种类的MySQL 优化器路径的一系列
额外信息,下面给 出经常使用值的列表