最上层不是Mysql独有的, 好比链接处理,受权认证, 安全 等等
第二层核心服务功能,包括查询解析,分析,优化,缓存以及全部内置函数,存储过程,触发器,视图等都在这层实现
第三层 存储引擎,存储引擎API包含几十个底层函数。 html
优化与执行:每一个链接都会在mysql服务端产生一个线程(内部经过线程池管理线程),好比一个select语句进入,mysql首先会在查询缓存中查找是否缓存了这个select的结果集,若是没有则继续执行 解析、优化、执行的过程;不然会之间从缓存中获取结果集。
MySQL解析查询会建立内部数据结构(解析树),而后对其进行各类优化,包括重写查询,决定表的读取顺序,以及选择合适的索引等。 (用户能够经过特殊的关键字提示优化器,影响它的决策过程。也能够请求优化器解释优化过程的各个因素)
MySQL原理与实践,一共六篇java
mysql数据库支持插件式存储引擎。就是说支持多种存储引擎,甚至用户能够按照本身的须要定制和使用本身的存储引擎。
mysql5.5以前默认存储引擎是MyISAM,5.5后改成InnoDB。
MySQL数据库各个版本的区别、以及不一样引擎的适用环境mysql
执行update时,更新语句涉及到了数据的更改,因此必不可少的须要引入日志模块。
redo log是InnoDB引擎特有的日志模块;binlog是Server层自带的日志模块。sql
在更新数据写入内存的同时,会先更新内存记录redo log,而后顺序写入磁盘,而不是等内存占满以后再持久化,这样当数据库异常宕机以后,咱们能够根据redo log重作日志来恢复数据,保证以前提交的数据不会丢失。 数据库
各类不一样操做有不一样的重作日志格式。数组
binlog是逻辑日志,记录本次修改的原始逻辑,说白了就是记录了修改数据的SQL语句。
经过mysqlbinlog能够解析查看binlog日志。缓存
在今天中午12点的时候,发现上午10点执行了错误的SQL语句,想把数据库状态恢复到上午10点,错误语句执行以前。那么该怎么办呢?
数据恢复步骤以下: 首先你要拿到最近一次的备份库 拿到最近备份库开始到出错时候的全部binlog(binlog日志保留时间能够自定义) 使用binlog重放到错误发生以前。
主要用于事务的回滚。安全
是一种优化查询的数据结构。服务器
经常使用的有哈希表、彻底平衡二叉搜索树、B树、B+树等数据结构。
一个表的数据行数越多,那么对应的索引文件其实也是会很大的,实际上也是须要存储在磁盘中的,而不能所有都放在内存中,因此咱们在考虑选用哪一种数据结构时,咱们能够换一个角度思考,哪一个数据结构更适合从磁盘中读取数据,或者哪一个数据结构可以提升磁盘的IO效率。
MySQL中的索引是B+树实现的,MySQL为什么选择使用B+树?得看看其余数据结构实现的索引的特色:session
好比给name字段创建hash索引的状况下
首先数据库底层会计算name字段各行值对应的hash值做为数组下标,其中可能会有hash冲突,存储对应的那一行数据的地址。
当直接执行select * from users where name = 'tom';
的时候,数据库会计算'tom'的hash值,获得数组下标,而后直接从数据中取出数据并拿到锁对应的那一行数据的地址,进而在数据表文件中查询那一行数据。
可是当执行select * from users where name > 'tom';
时,索引将再也不起做用。 哈希表并非有序的,能够快速的精确查询,可是不支持范围查询。因此只适合作等值查询,区间查询的效率很低!
好像字典里的索引去掉了排序。
二叉搜索树的特色:每一个节点的左儿子小于父节点,父节点又小于右儿子。
因为彻底平衡二叉搜索树是有序的,因此支持范围查找。
二叉树是搜索效率最高的,可是实际上大多数的数据库存储却并不使用二叉树。其缘由是,索引不止存在内存中,还要写到磁盘上。为了让一个查询尽可能少地读磁盘,就必须让查询过程访问尽可能少的数据块。那么,咱们就不该该使用二叉树,而是要使用“N 叉”树。“N”取决于数据块的大小。
一样的数据,B树表示的要比彻底平衡二叉搜索树要 "矮",缘由在于B树中的一个节点能够存储多个元素。
B+树是B树的升级版,只是把非叶子节点冗余一下,这么作的好处是 为了提升范围查找的效率。
一样的元素,B+树的表示要比B树要 "胖",缘由在于全部的叶子结点包含了所有元素的信息,及指向含这些元素记录的指针,且叶子结点自己依关键字的大小自小而大顺序连接。
非叶子节点也会冗余一份全部的中间节点元素,它们是最大(或最小)元素。
提升查询索引时的磁盘IO效率,而且能够提升范围查询的效率,而且B+树里的元素也是有序的,叶子节点造成有序链表,便于范围查询。
参考的这篇博客
对于主健,oracle/sql server/mysql等都会自动创建惟一索引。
主键索引是惟一索引的特殊类型。= 惟一索引+ not null
不容许两行具备相同的索引值。
例外状况是,若是该字段被设置为容许NULL值,则插入该字段的值能够包含多个NULL值。
它们的一些比较:
(1)对于主健/unique constraint , oracle/sql server/mysql等都会自动创建惟一索引; (2)主键不必定只包含一个字段,因此若是你在主键的其中一个字段建惟一索引仍是必要的; (3)主健可做外健,惟一索引不可; (4)主健不可为空,惟一索引可; (5)主健也但是多个字段的组合; (6)主键与惟一索引不一样的是: a.有not null属性; b.每一个表只能有一个。 (7)主键索引比普通索引得查询速度快。由于普通索引树上并无完整得数据,而是先找到主键后,再到主键索引树上去获取所需得数据,这个操做被称为回表。当你没有设主键或者主键忽然被删除时,会自动创建一个主键rowid,保证回表等功能的正常运行。
由关键字KEY或INDEX定义的索引。惟一任务是加快对数据的访问速度。
在多个列上创建索引,这种索引叫作复合索引(组合索引)。https://www.cnblogs.com/zjdxr...
用于在一篇文章中,检索文本信息的。
explain 语句 extra列出现using index
若是执行的语句是 select ID from T where k between 3 and 5
,这时只须要查 ID 的值,而 ID 的值已经在 k 索引树上了,所以能够直接提供查询结果,不须要回表。也就是说,在这个查询里,索引 k 已经“覆盖了”咱们的查询需求,咱们称为覆盖索引。
区分度最高的放在最左边。
涉及到在创建复合索引的时候,如何安排索引内的字段顺序的问题。
若是咱们建立了(username,sex,age)的复合索引,那么其实至关于建立了:
(username,sex,age),(username,sex)、(username,age)、(username)四个索引,这被称为最佳左前缀特性。
所以咱们在建立复合索引时应该将最经常使用做限制条件的列放在最左边,依次递减。
https://www.cnblogs.com/jiqin...
在索引遍历过程当中,对索引中包含的字段先作判断,直接过滤掉不知足条件的记录,减小回表次数。
1.主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。
2.索引的维护的主要代价是因为有序性的维护引发的,若是每次插入一条新记录,都是追加操做,都不涉及到挪动其余记录,也不会触发叶子节点的分裂,这样就能够减小索引的维护代价。
1.在没有建索引的状况下,数据库查找某一条数据,就必须进行全表扫描了,对全部数据进行一次遍历,查找出符合条件的记录。
2.where 子句中对字段进行 null 值判断。
3.where 子句中使用!=或<>操做符
4.where 子句中使用 or 来链接条件
5.in 和 not in
6.使用非打头字母搜索
7.在 where 子句中对字段进行表达式操做
8.在where子句中对字段进行函数操做
9.在 where 子句中的“=”左边进行函数、算术运算或其余表达式运算
10.复合索引状况下,使用到该索引中的第一个字段做为条件时才能保证系统使用该索引,不然该索引将不会被使用,而且应尽量的让字段顺序与索引顺序相一致。
https://blog.csdn.net/qq_3631...
在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是并发性问题。
典型的冲突有:
1.丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从6改成2,用户B把值从2改成6,则用户A丢失了他的更新。
2.脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是6,用户B把值改成2,用户A读到的值仍为6。
为了解决这些并发带来的问题。 咱们须要引入并发控制机制。
mysql采用读写锁来进行并发控制。
两种锁的思想——乐观锁和悲观锁。
指的是对数据被外界(包括本系统当前的其余事务,以及来自外部系统的事务处理)修改持保守态度,所以,在整个数据处理过程当中,将数据处于锁定状态,好比表锁,行锁。独占锁、java中 synchronized就是悲观锁。
依靠数据库提供的锁机制实现。适用于数据争用严重/重试代价大的场景。
缺点:
在多线程竞争下,加锁、释放锁会致使比较多的上下文切换和调度延时,引发性能问题。
一个线程持有锁会致使其它全部须要此锁的线程挂起,对长事务而言,这样的开销每每没法承受。
若是一个优先级高的线程等待一个优先级低的线程释放锁会致使优先级倒置,引发性能风险。
参考悲观锁实践
乐观锁假设认为数据通常状况下不会形成冲突,因此在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,若是发现冲突了,则让返回用户错误的信息,让用户决定如何去作。即冲突检测和数据更新
两步。乐观锁不能解决脏读的问题。ReadCommitted、REPEATABLE READ隔离级别是经过MVCC实现乐观锁的,还有hibernate中@Version注解。
依靠数据版本记录机制实现。
能够经过使用时间戳(timestamp)字段也能够定义version字段(自增加的整数)做为版本标识,当读取数据时,将版本标识字段的值一同读出,在更新提交的时候检查当前数据库中数据的当前版本标识和本身更新前取到的版本标识进行对比,若是一致则予以更新,不然就是版本冲突返回给用户。
适用于数据争用不严重/重试代价不大/须要相应速度快的场景。
缺点:在高并发的状况下,乐观锁并不适合。由于致数据库会有不少的更新失败,处理异常再次执行的状况。
了解下Compare and Swap(CAS)。
CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知此次竞争中失败,并能够再次尝试。CAS是一种非阻塞式的同步方式。CAS 操做包含三个操做数 —— 内存位置(V)、预期原值(A)和新值(B)。若是内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。不然,处理器不作任何操做。不管哪一种状况,它都会在 CAS 指令以前返回该位置的值。(在 CAS 的一些特殊状况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;若是包含该值,则将 B 放到这个位置;不然,不要更改该位置,只告诉我这个位置如今的值便可。”这其实和乐观锁的冲突检查+数据更新的原理是同样的。
参考java对CAS的支持
提升并发性的方式就是让锁定的对象更有选择性,只锁定部分数据,而不是全部的资源,这就是锁粒度要考虑的问题。
MySQL数据库的MyISAM引擎支持表锁。
当你对一张表进行修改时,会锁死整张表,其余的请求须要在修改完成释放锁才能继续。
特色:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的几率最高,并发度最低。
更适合于查询为主的场景。
MySQL最基本的锁策略,开销最小的策略。
MyISAM引擎中表锁分为表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
InnoDB引擎中表锁分为意向共享锁(IS)和意向排他锁(IX)。意向锁是InnoDB自动加的,不须要用户干预。
对于读操做,能够增长读锁,一旦数据表被加上读锁,其余请求能够对该表再次增长读锁,可是不能增长写锁
。(当一个请求在读数据时,其余请求也能够读,可是不能写,本身也不能写,由于一旦另一个线程写了数据,就会致使当前线程读取到的数据不是最新的了。这就是不可重复读现象)
实例
对于写操做,能够增长写锁,一旦数据表被加上写锁,这会阻塞其余用户对该表的全部读写操做
。(当一个请求在写数据时,其余请求不能执行任何操做,由于在当前事务提交以前,其余的请求没法看到本次修改的内容。这有可能产生脏读、不可重复读和幻读)
读锁和写锁都是阻塞锁。读和写是串行的。同时请求的状况下,写进程先得到锁。
只有没有写锁时,其余读取用户才能得到读锁,读锁之间是不相互阻塞的。
MyISAM 在执行查询语句(SELECT)前,会自动给涉及的全部表加读锁,在执行更新操做 (UPDATE、DELETE、INSERT 等)前,会自动给涉及的表加写锁,这个过程并不须要用户干预,所以,用户通常不须要直接用LOCK TABLE命令给MyISAM表显式加锁。
若是用户想要显示的加锁可使用如下命令:锁定表:LOCK TABLES tbl_name {READ | WRITE},[ tbl_name {READ | WRITE},…]
解锁表:UNLOCK TABLES
注意:当使用 LOCK TABLES 时,不只须要一次锁定用到的全部表,并且,同一个表在 SQL 语句中出现多少次,就要经过与 SQL 语句中相同的别名锁定多少次,不然也会出错!
参考资料
表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
相似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
InnoDB存储引擎既支持行级锁,也支持表级锁,但默认状况下是采用行级锁。
当你对一张表的某一行数据修改时,会锁死这一行数据,对表中其余的数据没影响。
行级锁能够最大程度地支持并发处理(最大锁开销),InnoDB和XtraDB,还有其余一些存储引擎中实现了行级锁,行级锁只在存储引擎层实现,而MySQL服务器层没有实现。
特色:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的几率最低,并发度也最高。
适合于大量按索引条件并发更新少许不一样的数据,同时又有并发查询的应用。
开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度通常。
BDB 存储引擎。
也称为读锁,读锁容许多个链接能够同一时刻并发的读取同一资源,互不干扰。但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放全部共享锁。
在查询语句后面增长LOCK IN SHARE MODE
,Mysql会对查询结果中的每行都加共享锁。
也称为写锁,一个写锁会阻塞其余的写锁或读锁,保证同一时刻只有一个链接能够写入数据,同时防止其余用户对这个数据的读写。
在查询语句后面增长FOR UPDATE
,Mysql会对查询结果中的每行都加排他锁。
InnoDB目前处理死锁的方法是,将持有最小行级排他锁的事务进行回滚(相对比较简单的死锁回滚方法)
越复杂的系统,越能检测到死锁的循环依赖,并当即返回一个错误,不然会致使出现很是慢的查询。
参考资料1
参考资料2
要么都作,要么都不作的。为了不出现逻辑处理失败致使的脏数据等问题。
具体有这些问题:
1.脏读
:A事务中读取到了B事务中还没有提交的数据。
2.不可重复读
:同一个事务中两次读取的数据的内容不一致。其余事务对同一条数据update了(提交了),时间上的问题。
3.幻读
:同一个事务中两次count的数量不一致。其余事务对该表insert或delete了(提交了)。
定义了事务中,数据库读写方面的控制范围。
1.Serializable
串行化
最严格、最安全的级别,可读,不可写。像java中的锁,至关于锁表,对于全部的query,即便是查询,也会加上读锁,避免其余事务对数据的修改。操做数据必须等待另外一个事务结束。
能够解决脏读、不可重复读和幻读,但事务串行执行,资源消耗最大,数据库系统的并发处理能力大大下降,因此它不会被用到生产系统中。
2.REPEATABLE READ
可重复读(mysql默认) 保证了在同一个事务中屡次读取一样记录的结果是一致的。由于只能读取在它开始以前已经提交的事务对数据库的修改,在它开始之后,全部其余事务对数据库的修改对它来讲均不可见,避免了“脏读取”和“不可重复读”的状况,但当其余事务往表中新增数据时,有幻读的问题。
不过mysql的innodb引擎采用了多版本并发控制(MVCC)机制能够解决幻读问题。
mysql环境作个实验:
一、session1启动一个事务start transaction;
;
二、执行一条查询语句SELECT * FROM class WHERE teacher_id=1;
,观察结果;
三、session2启动一个事务,执行更新或删除teacher_id=1
的命令,而后执行commit;
,在session1中查看teacher_id=1
行,发现并无被更新或删除。这里变体现了“可重复读”,无论别的事物修改或删除了什么,即使是提交了,在当前事务下,重复读取的结果是同样的。
;
四、session2执行一条插入语句,而后提交,在session1中count一下发现总条数并无加1,不是说可重复读有幻读问题吗?这里是mysql的MVCC机制解决了幻读问题。
3.READ COMMITTED
读取已提交(oracle默认,开发里经常使用)
其余事务对数据库的修改,只要已经提交,其修改的结果就是可见的,与这两个事务开始的前后顺序无关。这种隔离等级避免了脏读,但屡次读取的数据结果可能出现不一致的状况,即解决不可重复读和幻读问题。
能够修改数据库的事务隔离级别set transaction isolation level read committed;
重复上面的实验,看看有没有不可重复读和幻读问题。
4Read Uncommitted
读未提交
查询能够读取到其余事务正在修改的数据,即便其余事务的修改尚未提交。这种隔离等级哪一个问题都不能解决。
1.查看当前会话隔离级别:select @@tx_isolation;
2.查看系统当前隔离级别:select @@global.tx_isolation;
3.设置当前会话隔离级别:set session transaction isolatin level repeatable read;
4.设置系统当前隔离级别:set global transaction isolation level repeatable read;
5.命令行,开始事务时:set autocommit=on;
或者 start transaction;
6.让当前会话的事务自动提交(更新、插入操做后会自动提交):set @@session.autocommit=1;
7.设置手动提交事务:set autocommit = 0;
8.查看当前事务id:SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
表中能够查到正在执行的事务信息。
它使得大部分支持行锁的事务引擎,再也不单纯的使用行锁来进行数据库的并发控制,取而代之的是,把数据库的行锁与行的多个版本结合起来,只须要很小的开销,就能够实现非锁定读,从而大大提升数据库系统的并发性能。
innodb对每一行加上了两个隐含的列,一列存储行被更新的”时间”,另外一列存储行被删除的”时间”。 并非绝对的时间,而是与时间对应的数据库系统的版本号,每当一个事务开始的时候,innodb都会给这个事务分配一个递增的版本号,因此版本号也能够被认为是事务号
。 对于每个”查询”语句,innodb都会把这个查询语句的版本号同这个查询语句对应的行的版本号进行对比,而后结合不一样的事务隔离等级,来决定是否返回该行。
每一行数据均可能存在多个版本,那么这些行组合起来获得的结果集的版本就更是不可胜数,这就是数据库多版本的由来。MVCC就是经过事务发生的不一样的时间点,与数据行的版原本进行对比,从而取回与事务开始的时间点相一致的数据,来实现非阻塞的一致读。
好比对于select语句,只有同时知足了下面两个条件的行,才会被返回: 1.行的被修改版本号小于或者等于该事务号;
2.行的被删除版本号要么没有被定义(说明该行没有被删除过),要么大于事务的版本号(说明该行是被该事务后面启动的事务删除的,repeatable read隔离等级下,后开始的事务对数据的影响不该该被先开始的事务看见,因此该行应该被返回)。
深刻理解
好比INSERT语句,对新插入的行,行的更新版本被修改成该事务的事务号;
对于删除,innodb直接把该行的被删除版本号设置为当前的事务号,至关于标记为删除,而不是实际删除;
在更新行的时候,innodb会把原来的行复制一份到回滚段中,并把当前的事务号做为该行的更新版本。
读取数据的时候,innodb几乎不用得到任何锁, 每一个查询都经过版本检查,只得到本身须要的数据版本,从而大大提升了系统的并发度。
为了实现多版本,innodb必须对每行增长相应的字段来存储版本信息,同时须要维护每一行的版本信息,并且在检索行的时候,须要进行版本的比较,于是会下降查询的效率;
innodb还必须按期清理再也不须要的行版本,及时回收空间,这也增长了一些开销。
另外,数据库执行了大事务状况下,数据库仅是频繁更新,没有插入新数据,也会致使表空间占用愈来愈大,由于innodb会把被修改数据的前映像存放到称为回滚段的公共表空间中,并且对于索引和表中的行的多个版本,若是innodb来不及purge,或者这些行由于要提供一致读而不能被purge,就会占用愈来愈多的空间,甚至有可能短期撑爆你的硬盘。因此应用程序中须要合理控制事务的大小!
优化
另外,因为innodb的mvcc策略的实施,char数据类型相对于varchar类型几乎没有任何优点,反而varchar列可能节省更多的存储空间,建议使用varchar数据类型。???
MVCC因为其实现原理,只支持read committed和repeatable read隔离等级。
MVCC自己不支持read uncommitted等级,因此能够经过设置transaction_isolation = read uncommitted 来禁用MVCC。
可是任何改变innodb默认隔离等级的操做,都会起到innodb_locks_unsafe_for_binlog=off相似的效果,这会致使诸如insert into t select * from t_src 之类的语句再也不给源表t_src加锁,也再也不使用innodb的间隙锁,从而产生幻读,直接致使binlog中记录的sql语句不能正确的串行化,从而主从数据库的数据再也不一致,并且基于binlog的增量备份也再也不有效,因此除非不须要记录binlog,不然别这么作。固然咱们能够这样作来优化从库的性能,由于从库不须要记录binlog。