文章目录
- MySQL
- 存储引擎
- MyISAM和InnoDB区别
- 索引
- 事务的实现原理
- 并发事务带来哪些问题?
- 不可重复读和幻读区别
- 事务的隔离级别
- MyISAM和InnoDB存储引擎使用的锁
- 表级锁和行级锁的对比
- InnoDB存储引擎的锁的算法
- MySQL中的varchar和char有什么区别
- B 树索引、哈希索引、B树的区别
- drop、delete、truncate的却别
- MySQL执行查询的过程
- 哪些情况需要创建索引
- 哪些情况不需要创建索引
- MySQL中有哪几种锁
- 行级锁
- 表级锁
- 页级锁
- 最左匹配原则
MySQL
存储引擎
MyISAM和InnoDB区别
MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(事务性数据库引擎),MySQL 5.5版本后默认的存储引擎为InnoDB。大多数时候我们使用的都是 InnoDB 存储引擎,但是在某些情况下使用 MyISAM 也是合适的比如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。
两者对比:
- InnoDB支持事务,MyISAM不支持。
- InnoDB支持外键,MyISAM不支持。
- InnoDB是聚簇索引,数据文件和索引是捆绑在一起的,必须有主键,通过主键索引效率高。MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针,主键索引和辅助索引是独立的。
- Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高。
- Innodb不保存表的具体行数1,MyISAM用一个变量保存了整个表的行数。
- InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁,MyISAM采用表级锁。 索引
MySQL索引使用的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
MySQL的BTree索引使用的是B树中的B Tree,但对于主要的两种存储引擎的实现方式是不同的。
- MyISAM:B Tree叶节点的data域存放的是数据记录的地址。在检索索引的时候,首先按照B Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址读取响应的数据记录,这就是非聚簇索引。
- InnoDB:其数据文件本身就是一个索引文件,相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是B Tree组织的一个索引接口1,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表主键,因此InnoDB表数据文件本身就是主索引。这就是聚簇索引(聚集索引)。
事务的实现原理
事务是基于重做日志文件和回滚日志实现的。
每提交一个事务必须将该事务的所有日志写入重做日志文件进行持久化,数据库1可以通过重做日志文件来保证事务的原子性和持久性。
每当有修改事务时,还会产生undo log,如果需要回滚,则根据1undo log的反向语句进行逻辑操作,比如insert一条记录就delete一条记录。undo log主要实现数据库的一致性。
并发事务带来哪些问题?
在典型的应用程序中,多个事务并发执行1,经常会操作相同的数据来完成各自的任务,会导致几个1问题:
- 脏读(Dirty Read):当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,那么当另一个事务1也访问到了这个数据,然后使用了这个数据,因为这个数据是还没有提交的数据,那么另一个事务读到的这个数据是脏数据。依据脏数据所做的操作可能是不正确的。
- 丢失修改(Lost to modify):指在一个事务中读取一个数据时,另一个事务也访问了该数据,那么在第一个事务中修改了这个数据以后,第二个事务也修改了这个数据,这样第一个事务内修改的结果就被丢失,因此称为丢失修改。例如事务1读取某表中A=20,事务2也读取A=20,事务1修改A为1,事务二修改A为100,最终结果是A=100,事务1修改的数据丢失。
- 不可重复读(Unrepeatableread):指一个事务多次读取1同一个数据,在这个事务还没有结束时,另一个事务也访问了该数据,那么在第一个事务中两次读取数据之间,由于第二个事务的修改导致了第一个事务两次读取的数据的值可能不太一样,这样就发生了一个事务内两次读到的1数据时不一样的情况。
- 幻读(Phantom read):它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据时,在随后的查询中,第一个事务会发现多了一些原本不存在的记录。
不可重复读和幻读区别
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。
事务的隔离级别
SQL 标准定义了四个隔离级别:
- READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据,可能会导致脏读、幻读、不可重复读。
- READ-COMMITTED)(读取已提交):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍然可能发生。
- REPEATABLE-READ(可重复读):对同一字段多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但是幻读仍然可能发生。是MySQL默认的隔离级别
- SERIALIZABLE(可串行化):最高的隔离级别,完成服从ACID的隔离级别,所有事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说可以防止脏读、幻读和不可重复读。
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
MyISAM和InnoDB存储引擎使用的锁
- MyISAM采用表级锁(table-level locking)。
- InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁。
表级锁和行级锁的对比
- 表级锁:MySQL中锁定粒度最大的一种锁,对当前操作的整张表加锁,实现简单,消耗资源也少,加锁快,不会出现死锁,其锁定粒度最大,触发锁冲突的1概率最高,并发度最低。
- 行级锁:MySQL锁定粒度最小的一种锁,只针对当前操作进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,并发度最高,但是加锁的开销也最大,加锁慢,会出现死锁。
InnoDB存储引擎的锁的算法
- Record lock:单个行记录上的锁
- Gap lock:间隙锁,锁定一个范围,不包括记录本身
- Next-key lock:record gap 锁定一个范围,包含记录本身
MySQL中的varchar和char有什么区别
char是一个定长字段,假如申请了char(10)的空间,那么无论实际存储多少内容,该字段都占用10个字符,而varchar是变长的,也就是说申请的只是最大长度,占用的空间为实际字符 1,最后一位字符存储了使用多少空间。
在检索效率上来讲,char > varchar,因此在使用中,如果确定某个字段的值的长度,可以使用char,否则应该尽量使用varchar.例如存储用户MD5加密后的密码,则应该使用char。
B 树索引、哈希索引、B树的区别
- B树:叶子节点和非叶子节点都存储数据,数据结构为有序数组 平衡多叉树。
- B 树:只有在叶子节点存储数据,数据结构为有序数组链表 平衡多叉树。B 树索引的关键字索引效率比较平均,不像B树那样波动幅度大。在有大量重复键值的情况下,哈希索引效率也是极低的,因为存在哈希碰撞问题。B 树只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作(或者说效率太低)。
- 哈希索引:Hash索引仅仅能满足=和<=>等值查询,不能使用范围查询。哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B 树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快,但是Hash索引在任何时候都不能避免表扫描。
drop、delete、truncate的却别
速度上drop>truncate>delete
- drop:直接删掉关于表的一切(数据、结构、约束),不会记录日志,为DDL操作。
- truncate:删除表中所有数据(再插入时自增长id从1开始),该操作也不会记录日志。所以比较快,为DDL操作。只能删除table。
- delete语句执行的过程时每次从表中删除一行,需要记录日志,比较慢。
MySQL执行查询的过程
- 客户端通过TCP连接发送请求到MySQL连接器,连接器会对该请求进行权限验证和连接资源分配。
- 查缓存,当判断缓存是否命中时,MySQL不会进行解析查询语句,而是直接使用SQL语句和客户端发送来1过来的其他原始信息,所以任何字符上的不同,例如空格、注释都会导致缓存的不命中。
- 语法分析,这一步判断SQL是否写错了,检查表和数据列是否存在,解析看别名是否存在歧义。
- 优化,查看是否使用索引以及生成执行计划。
- 交给执行器,将数据保存到结果集,同时会逐步将数据缓存到查询缓存中,最终将结果集返回给客户端。
哪些情况需要创建索引
- 主键自动建立唯一索引。
- 频繁作为查询条件的字段。
- 查询中与其他表关联的字段,外键关系建立索引。
- 单键/组合索引的选择问题,高并发下倾向创建组合索引。
- 查询中排序的字段,排序字段通过索引访问大幅度提高排序速度。
哪些情况不需要创建索引
- 表记录太少。
- 经常增删改的表。
- 数据重复且分布均匀的表字段,只应该为最经常查询和最经常排序的数据建立索引(如果某个数据类包含太多的重复数据,建立索引没有太大的意义)。
- 频繁更新的字段不适合创建索引(会加重IO负担)。
- where条件里用不到的字段不创建索引。
MySQL中有哪几种锁
按照对数据锁定的划分,可以分为行级锁、表级锁、页级锁、间隙锁。
行级锁
行级锁是MySQL中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,但是加锁的开销也最大。行级锁分为共享锁和排他锁。
InnoDB有三种行级(都是排他锁)锁算法:
- Record Lock(记录锁):即元数据锁,单个行记录上锁,也就是我们日常认为的行锁。
- Gap Lock(间隙锁):间隙锁是锁定一个范围,但是不包括记录本身,它的锁粒度比记录锁的锁整行更大一些,他是锁住了某个范围内的多个行,包括根本不存在的数据。间隙锁的目的是为了防止同一事务的两次当前读出现幻读的情况,同时也是为了让其他事务无法在间隙中新增数据。
- Next-Key Lock(临键锁):它是记录锁和间隙锁的结合,锁定一个范围,并且锁定记录本身,对于行的查询都是采用该方法,主要是为了解决幻读的问题,Next-Key Lock是InnoDB默认的锁。
表级锁
表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分的MySQL引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定,表级锁分为表共享读锁(共享锁)、表独占锁(排他锁)。
页级锁
页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁,表级锁速度快,但是冲突多,行级锁冲突少,但是速度慢,页级锁是折中的策略,一次锁定相邻的一组记录。
按照锁的共享策略划分分为:共享锁(读锁)、排他锁(写锁)、意向共享锁、意向排他锁。
- 读锁(共享锁、Shared Locks、S锁):针对同一份数据,多个读操作可以同时进行而不会互相1影响。
- 写锁(排他锁、Exclusive Locks、X锁):当前写操作没有完成之前,它会1阻断其他写锁和读锁。
- 意向共享锁(IS锁、Intention Shared Lock):当事务准备在1某条记录上加S锁时,需要先在表级别加一个IS锁。
- 意向排他锁(IX锁、Intention Exclusive Lock):当事务准备在某条记录上加X锁时,需要先在表级别加一个IX锁。
从加锁策略上分分为乐观锁和悲观锁。
- 乐观锁:认为同一数据的并发操作时不会修改的,通过程序实现,一般使用版本号或者时间戳。
- 悲观锁:和乐观锁相反,悲观锁悲观的认为不加锁的并发操作一定会出现问题,哪怕没有修改,也会认为被修改了。因此对于同一个数据的并发操作,悲观锁采取加锁的形式,悲观锁又分为表级锁和行级锁。
最左匹配原则
最左匹配原则指的是在MySQL建立联合索引时会遵循最左匹前缀匹配原则,即最左优先,在检索数据时会从联合索引的最左边开始匹配。索引的底层是一颗B 树,那么联合索引的底层也应该是一颗B 树,只不过联合索引1的B 树节点中存储的是逗号分隔的多个值。
举个例子,我们有一张student表,我们根据学院编号 班级
建立了一个联合索引index_major_class
。这个索引由学院编号(major)和班级(class)这两个字段组成。他是先根据major(学院编号)进行排序,再根据class(班级)进行排序,如果索引后面还有字段,继续以此类推。
我们查询的where条件如果只传入了班级,是走不到联合索引的,但是如果只传了学院编号,是有可能走到联合索引的(可能的原因是因为MySQL的执行计划和查询的实际实行过程并不是那么吻合,如果说你的数据量很小,那么完全可以全量遍历查询,这样速度更快,就不需要走索引了)。