高性能MySQL&&MySQL架构

注:本篇是《高性能MySQL》第三版的读书笔记。基于MySQL5.5

 

MySQL的架构与历史

如下为MySQL的逻辑架构图

最上层是正常的连接处理、授权认证、安全等。

中间是核心的服务。查询解析、分析、优化、缓存预计所有内部函数,所有跨越存储引擎的功能都在这一层实现:存储过程、触发器、视图。

第三层包含了存储引擎,存储引擎负责数据的存储和提取。

MySQL服务器通过API与存储引擎通信,来做到屏蔽存储引擎之间的差异。

连接管理与安全

每个客户端打进来都会在服务器中拥有一个线程池的线程,这个连接的查询只会在这个单独的线程中进行。线程在CPU和核心中轮流获取资源,服务器维护了一个连接池(线程池),保证不会为每个连接都创建一个新的线程。

当客户端连接到MySQL服务器时,首先进行认证,并查看是否使用了SSL的方式(用X.509证书认证)、认证过了查询客户端对某个查询是否有相应的权限。 没有权限需要对这个登录的用户授权。grant

mysql  -uroot  -p123456  -h127.0.0.1   

优化与执行

Mysql会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询,决定表的读取顺序,选择索引等。

并发控制

MySQL在两个层面进行并发控制,服务器层和存储引擎层。

想到并发,第一想的肯定是锁,那么锁,首先想到的肯定是类似Java的synchronized  一个一个来。但是这是数据库这么玩就凉了。所以读写锁也就成为一个好的解决办法了。

读写锁分为读锁和写锁。即共享锁和排他锁。共享锁就是多个用户可以访问同一个资源。排他锁是只要这行数据拿到排它锁,其他对这行数据的读写操作都是被阻塞的、这样确保在给定时间,只有一个用户能写入,并且其他用户读也不会出错。

颗粒度是提高并发的一种选择。

表锁:基本的锁,类似上面的读写锁加在表上,读互相不影响,写就把表锁住了。谁也读不了。

行锁:最大程度支持并发,类似上面读写锁加在一行数据。写这行数据会导致其他人读不了这行数据,别的能读。锁开销大。

 

事务

事务就是一组SQL语句,如果数据库引擎能成功都执行就成功,有执行失败的就全部都失败。

关系型数据库事务的ACID

example:

查账号是否有200块 -----   当前账号减200  ---- 儿子账号多200

select   balance   from  checking  where  uid = 1000

update  checking  set  balance = balance - 200 where  uid = 1000

update  savings  set  balance  =  balance  +200  where uid = 1000

1.原子性:一个事务要么都执行,要么都失败。

2.一致性:数据库总是从一个一致性状态转换到另外一个一致性状态。这两个一致性可以理解为确定的、中间出问题就不行了

3.隔离性:多个事务之间,如果第一个事务得到的balance是 500   此时另一个事务把balance改成了400并持久化到数据库了。那么再执行 balance - 200 set 的结果就是300 了。这不合适、应该本次事务失败,在重新获取balance = 400  然后再减。

4.持久性:一旦事务提交,其所做的修改就被持久化到数据库中了。

四种隔离级别

1.读未提交:事务中的修改即使没提交,其他事务也是可以看到的。其他事务可以读取没有被提交的数据,也叫脏读。

2.提交读:事务开始时只能看见已经提交的事务所做的修改。如果在这个事务进行时,其他事务提交他是不知道的。如果事务回滚再次读到的数据就不一样了也叫不可重复读。

3.可重复读:当一个事务在读取某个范围内的记录时,其他事务往这个范围内又插入了数据,之前的事务再读,会产生幻行,也叫做幻读。(MySQL默认隔离级别)行级锁

4.可串行化:强制事务串行执行,会在读取每一行数据的时候加锁。表级锁

事务的隔离级别可以通过 set session transation isolation level read committed; 来改。

死锁:多个事务在同一个资源上互相占用。

A  

transation

update  price_table set price = price -10 where uid = 10

update  price_table set price = price -10 where uid = 11

commit

B

transation

update  price_table set a= a-10 where uid = 11
update  price_table set a= a-10 where uid = 10

commit

当A事务执行第一个update时B事务也在执行第一个update  AB对对应的行数据上了锁,那么AB都在等待对方释放锁才能继续修改第二条语句,此时就是死锁了。

innodb目前将持有最少行级排它锁的事务进行回滚。有些死锁也是由于存储引擎的实现方式导致的。

事务日志

事务日志可以提高事务的效率。事务日志存储操作记录,比如insert一个数据,存储引擎只需要修改数据的内存拷贝,并将操作存到事务日志中,而不是直接将数据持久化到磁盘。跟elasticsearch的translog一个套路。事务日志是顺序写入的,不会导致磁盘的随机IO。即使我们机器宕掉了,再启动也会根据translog恢复数据。

对于Mysql常见的存储引擎,innodb和myisam中,innodb是支持事务显示创建commit和rollback的。而myisam不存在commit和rollback的操作。事务都是自动提交的。 对于一些alter对表的操作,会commit之前SQL并开始使用新的事务操作。

innodb会根据隔离级别在需要的时候自动加锁。事务是属于存储引擎层的。不属于服务器层。不同存储引擎的事务不一样。

 

多版本并发控制(MVCC)

MySQL大多数事务型存储引擎都不是简单的行级锁,基于对于并发场景性能的提升,一般都实现了多版本并发控制。

MVCC可以理解为行级锁的变种,性能更高了。典型的有乐观并发控制和悲观并发控制。

InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建版本号,一个保存了过期系统版本号。每开启一个事务,系统版本号会递增,事务开始时刻的系统版本号会作为事务的版本号,用来和查询的记录对比。

下面是可重复读隔离级别的MVCC操作。

select   满足1.版本早于或等于当前事务版本号的行。2.行的删除版本要么未定义要么大于当前事务版本号。

insert   插入的行用当前系统版本号作为行版本号,删除版本号为空。

delete  删除的行用当前系统版本号作为删除版本号。

update   innodb的实现是插入了一条新记录,用当前版本号作为行版本号,当前版本号也作为之前记录的删除版本号。

可以看到跟elasticsearch又是一个玩法。所以更新是比想像的更麻烦的。

MVCC只在可重复读和读已提交两个隔离级别下工作。

 

Mysql将每个数据库报错为数据目录下的一个子目录。创建表时,MySQL会在数据库子目录下创建一个和表同名的.frm文件保存表的定义。不同存储引擎保存数据和索引的方式是不同的。

可以使用 show table status like 'user'    来查询某个表的相关信息。

 

InnoDB存储引擎

InnoDB是基于聚簇索引建立的,聚簇索引对主键查询有很高的性能,非主键索引中包含主键列,所以主键太大会导致索引文件特别大,因此主键应该尽可能少。有行级锁,5.6版本之前不支持全文索引。之后支持了(不理想)。

MyISAM存储引擎

不支持事务和行级锁。崩溃后无法恢复。加锁的维度是整张表。myisam存储引擎会对表进行压缩。如果发现所有查询属于Locked状态,就很有可能是表锁导致的。

InnoDB有事务,备份,崩溃恢复等特性。使用MySQL基本就选它没错了。

MyISAM的压缩使得数据传输比较快。但是一些缺点导致InnoDB的优先于它。

 

不建议中途修改数据的存储引擎,可能会导致一些意想不到的问题。后文会详细描述存储引擎。

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章