初识MySQL

今天来系统地学习一下MySQL,主要有如下知识点:git

  • InnoDB引擎
  • 事务
  • 索引

InnoDB引擎

MyISAM和InnoDB区别

MyISAM是MySQL的默认数据库引擎(5.5版以前)。虽然性能极佳,并且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,并且最大的缺陷就是崩溃后没法安全恢复。不过,5.5版本以后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。github

二者的对比:算法

  1. 是否支持行级锁: MyISAM 只有表级锁(table-level locking),而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
  2. 是否支持事务和崩溃后的安全恢复: MyISAM强调的是性能,每次查询具备原子性,其执行速度比InnoDB类型更快,可是不提供事务支持。可是InnoDB提供事务支持事务,外部键等高级数据库功能。 具备事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
  3. 是否支持外键:MyISAM不支持,而InnoDB支持。
  4. 是否支持MVCC:仅 InnoDB 支持。应对高并发事务, MVCC比单纯的加锁更高效;MVCC只在READ COMMITTEDREPEATABLE READ两个隔离级别下工做;MVCC可使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。

事务

什么是事务?

事务是逻辑上的一组操做,要么都执行,要么都不执行。sql

事务的特性(ACID)

2.png

  1. 原子性:事务是最小的执行单位,不容许分割。事务的原子性确保动做要么所有完成,要么彻底不起做用;
  2. 一致性:执行事务先后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
  3. 隔离性:并发访问数据库时,一个用户的事务不被其余事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性:一个事务被提交以后。它对数据库中数据的改变是持久的,即便数据库发生故障也不该该对其有任何影响。

并发事务带来的问题

在典型的应用程序中,多个事务并发运行,常常会操做相同的数据来完成各自的任务(多个用户对统一数据进行操做)。并发虽然是必须的,但可能会致使如下的问题。数据库

  • 脏读(Dirty read):当一个事务正在访问数据而且对数据进行了修改,而这种修改尚未提交到数据库中,这时另一个事务也访问了这个数据,而后使用了这个数据。由于这个数据是尚未提交的数据,那么另一个事务读到的这个数据是“脏数据”,依据“脏数据”所作的操做多是不正确的。
  • 更新丢失(Lost to modify):指在一个事务读取一个数据时,另一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,所以称为更新丢失。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的更新被丢失。
  • 不可重复读(Unrepeatableread):指在一个事务内屡次读同一数据。在这个事务尚未结束时,另外一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,因为第二个事务的修改致使第一个事务两次读取的数据可能不太同样。这就发生了在一个事务内两次读到的数据是不同的状况,所以称为不可重复读。
  • 幻读(Phantom read):幻读与不可重复读相似。它发生在一个事务(T1)读取了几行数据,接着另外一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些本来不存在的记录,就好像发生了幻觉同样,因此称为幻读。

不可重复读和幻读区别:安全

不可重复读的重点是修改,幻读的重点在于新增或者删除。并发

事务隔离级别

SQL 标准定义了四个隔离级别:ide

  • READ-UNCOMMITTED(读未提交):最低的隔离级别,容许读取还没有提交的数据变动,可能会致使脏读、幻读或不可重复读
  • READ-COMMITTED(读已提交):容许读取并发事务已经提交的数据,能够阻止脏读,可是幻读或不可重复读仍有可能发生
  • REPEATABLE-READ(可重复读):对同一字段的屡次读取结果都是一致的,除非数据是被自己事务本身所修改,能够阻止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE(可串行化):最高的隔离级别,彻底服从ACID的隔离级别。全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读

1.png

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重复读)函数

这里须要注意的是:与 SQL 标准不一样的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,所以能够避免幻读的产生,这与其余数据库系统(如 SQL Server)是不一样的。因此说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经能够彻底保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。高并发

索引

基础知识

Mysql的基本存储结构是页(记录都存在页里边)

image

image

各个数据页能够组成一个双向链表
而每一个数据页中的记录又能够组成一个单向链表

每一个数据页都会为存储在它里边儿的记录生成一个页目录,在经过主键查找某条记录的时候能够在页目录中使用二分法快速定位到对应的槽,而后再遍历该槽对应分组中的记录便可快速找到指定的记录

以其余列(非主键)做为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
因此说,若是咱们写 select * from use where username='wugui' 这样没有进行任何优化的sql语句,默认会这样作:

  1. 定位到记录所在的页
  2. 须要遍历双向链表,找到所在的页
  3. 从所在的页内中查找相应的记录

因为不是根据主键查询,只能遍历所在页的单链表了。
很明显,在数据量很大的状况下这样查找会很慢!

原理

索引底层结构就是B+树,B+树是为磁盘或其它直接存储辅助设备设计的一种平衡二叉树。

那么索引是如何加快检索速度的呢?

image

image

总结

  • 非叶子结点不保存数据,只用来索引,全部数据都保存在叶子节点。
  • 全部的数据都在叶子节点,各叶子结点造成了一个有序的双向链表。为何要有序呢?实际上是为了范围查询。好比说 select * from Table where id > 1 and id < 100; 当找到1后,只需顺着节点和指针顺序遍历就能够一次性访问到全部数据节点,极大提到了区间查询效率。
  • 通常状况下,数据库的B+树的高度通常在2~4层,这就是说找到某一键值的行记录最多须要2到4次磁盘IO,至关于0.02到0.04s。

哈希索引

除了B+树以外,还有一种常见的是哈希索引

哈希索引就是采用必定的哈希算法,把键值换算成新的哈希值,检索时不须要相似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法便可马上定位到相应的位置,速度很是快

哈希索引有好几个局限(根据他本质的原理可得):

  • 哈希索引也没办法利用索引完成排序
  • 不支持最左匹配原则
  • 在有大量重复键值状况下,哈希索引的效率也是极低的---->哈希碰撞问题。
  • 不支持范围查询

汇集和非汇集索引

简单归纳

  • 汇集索引就是以主键建立的索引
  • 非汇集索引就是以非主键建立的索引

区别

  • 汇集索引在叶子节点存储的是表中的数据
  • 非汇集索引在叶子节点存储的是主键和索引列
  • 使用非汇集索引查询出数据时,拿到叶子上的主键再去查到想要查找的数据

非汇集索引在创建的时候也未必是单列的,能够多个列来建立索引。

覆盖索引

在建立多列索引中也涉及到了一种特殊的索引-->覆盖索引

咱们前面知道了,若是不是汇集索引,叶子节点存储的是主键+列值
最终仍是要“回表”,也就是要经过主键再查找一次。这样就会比较慢
覆盖索引就是把要查询出的列和索引是对应的,不作回表操做!

好比说:
如今我建立了索引 (username,age),在查询数据的时候: select username,age from user where username = 'wugui' and age = 20。

很明显地知道,咱们上边的查询是走索引的,而且,要查询出的列在叶子节点都存在!因此,就不用回表了~

因此,能使用覆盖索引就尽可能使用吧~

最左前缀原则

MySQL中的索引能够以必定顺序引用多列,这种索引叫做联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,若是查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就能够被用到。以下:

select * from user where name=xx and city=xx ; //能够命中索引
select * from user where name=xx ; // 能够命中索引
select * from user where city=xx ; // 没法命中索引

这里须要注意的是,查询的时候若是两个条件都用上了,可是顺序不一样,如city= xx and name =xx,那么如今的查询引擎会自动优化为匹配联合索引的顺序,这样是可以命中索引的。

注意避免冗余索引

冗余索引指的是索引的功能相同,可以命中 就确定能命中 ,那么 就是冗余索引如(name,city )(name )这两个索引就是冗余索引,可以命中后者的查询确定是可以命中前者的 在大多数状况下,都应该尽可能扩展已有的索引而不是建立新索引。

image

MyISAM和InnoDB存储引擎使用的锁:

  • MyISAM采用表级锁(table-level locking)。
  • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

表级锁和行级锁

  • 表级锁:MySQL中锁定粒度最大的一种锁,对当前操做的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的几率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
  • 行级锁:MySQL中锁定粒度最小的一种锁,只针对当前操做的行进行加锁。 行级锁能大大减小数据库操做的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

共享锁和排它锁

InnoDB实现了如下两种类型的行锁。

  • 共享锁(S锁):容许一个事务去读一行,阻止其余事务得到相同数据的排他锁。S锁是共享的,多个客户能够同时读取同一个数据,但不容许其余客户修改。
  • 排他锁(X锁):容许得到排他锁的事务更新数据,阻止其余事务取得相同数据集的共享读锁和排他写锁。X锁是排他的,写锁会阻塞其余的写锁和读锁。

在默认的状况下, select是不加任何行锁的~事务能够经过如下语句显示给记录集加共享锁或排他锁。

共享锁(S): SELECT * FROM table_name WHERE...LOCK IN SHARE MODE

排他锁(X): SELECT * FROM table_name WHERE...FOR UPDATE

乐观锁和悲观锁

  • 乐观锁是一种思想,具体实现是,表中有一个版本字段,第一次读的时候,获取到这个字段。处理完业务逻辑开始更新的时候,须要再次查看该字段的值是否和第一次的同样。若是同样更新,反之拒绝。之因此叫乐观,由于这个模式没有从数据库加锁,等到更新的时候再判断是否能够更新。
  • 悲观锁是数据库层面加锁,都会阻塞去等待锁。

咱们使用悲观锁的话其实很简单(手动加行锁就好了):

select * from xxxx for update

在select 语句后边加了 for update 至关于加了排它锁(写锁),加了写锁之后,其余的事务就不能对它修改了!须要等待当前事务修改完以后才能够修改.

乐观锁的话咱们能够在数据库方面添加一个version字段,当有事务修改了数据时将version加一,当其它事务想要提交时发现version已被修改,则会取消事务提交。

参考

MySQL教程
数据库两个神器索引和锁

相关文章
相关标签/搜索