今天来系统地学习一下MySQL,主要有如下知识点:git
InnoDB引擎
事务
索引
锁
MyISAM和InnoDB区别
MyISAM
是MySQL的默认数据库引擎(5.5版以前)。虽然性能极佳,并且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,并且最大的缺陷就是崩溃后没法安全恢复。不过,5.5版本以后,MySQL引入了InnoDB
(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。github
二者的对比:算法
READ COMMITTED
和REPEATABLE READ
两个隔离级别下工做;MVCC可使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统一。事务是逻辑上的一组操做,要么都执行,要么都不执行。sql
事务的特性(ACID)
- 原子性:事务是最小的执行单位,不容许分割。事务的原子性确保动做要么所有完成,要么彻底不起做用;
- 一致性:执行事务先后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
- 隔离性:并发访问数据库时,一个用户的事务不被其余事务所干扰,各并发事务之间数据库是独立的;
- 持久性:一个事务被提交以后。它对数据库中数据的改变是持久的,即便数据库发生故障也不该该对其有任何影响。
并发事务带来的问题
在典型的应用程序中,多个事务并发运行,常常会操做相同的数据来完成各自的任务(多个用户对统一数据进行操做)。并发虽然是必须的,但可能会致使如下的问题。数据库
- 脏读(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的隔离级别。全部的事务依次逐个执行,这样事务之间就彻底不可能产生干扰,也就是说,该级别能够防止脏读、不可重复读以及幻读。
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重复读)
函数
这里须要注意的是:与 SQL 标准不一样的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,所以能够避免幻读的产生,这与其余数据库系统(如 SQL Server)是不一样的。因此说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经能够彻底保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。高并发
Mysql的基本存储结构是页(记录都存在页里边)
各个数据页能够组成一个双向链表
而每一个数据页中的记录又能够组成一个单向链表
每一个数据页都会为存储在它里边儿的记录生成一个页目录,在经过主键查找某条记录的时候能够在页目录中使用二分法快速定位到对应的槽,而后再遍历该槽对应分组中的记录便可快速找到指定的记录
以其余列(非主键)做为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
因此说,若是咱们写 select * from use where username='wugui' 这样没有进行任何优化的sql语句,默认会这样作:
因为不是根据主键查询,只能遍历所在页的单链表了。
很明显,在数据量很大的状况下这样查找会很慢!
原理
索引底层结构就是B+树
,B+树是为磁盘或其它直接存储辅助设备设计的一种平衡二叉树。
那么索引是如何加快检索速度的呢?
总结
- 非叶子结点不保存数据,只用来索引,全部数据都保存在叶子节点。
- 全部的数据都在叶子节点,各叶子结点造成了一个有序的双向链表。为何要有序呢?实际上是为了范围查询。好比说 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 )这两个索引就是冗余索引,可以命中后者的查询确定是可以命中前者的 在大多数状况下,都应该尽可能扩展已有的索引而不是建立新索引。
MyISAM和InnoDB存储引擎使用的锁:
表级锁和行级锁
共享锁和排它锁
InnoDB实现了如下两种类型的行锁。
在默认的状况下, 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已被修改,则会取消事务提交。