1.InnoDB与MyISAM的区别?
InnoDB | MyISAM | |
---|---|---|
事务 | 支持 | 不支持 |
记录数据总行数 | 不记录 | 记录 |
主键 | 必须有主键 | 可以没有 |
外键 | 支持 | 不支持 |
索引类型 | 聚集索引 | 非聚集索引 |
索引间层级关系 | 主键索引和辅助索引有层级关系 | 主键索引和辅助索引平级 |
锁 | 行锁,表锁 | 仅表锁 |
全文索引 | 5.7之前不支持,5.7以后支持 | 支持 |
区别解释
事务:InnoDB支持事务,每一条sql语句都是一个单独的事务,我们可以将多个sql语句放在begain和commit之间来提高事务的效率。
数据总行数:这点区别是由是否支持事务而产生的,由于InnoDB支持事务,而在不同的事务中数据总行数可能是不一样的,而MyISAM不支持事务,他有一条属性专门用来存放事务,select count(*) from table 的时候直接读取属性值返回,而InnoDB就需要走索引来统计总行数了。
聚集索引:聚集索引是指索引和数据是存放在一起的,InnoDB中B+tree的叶子节点上是存放着数据文件的,而在MyISAM中叶子节点存放的是数据文件的地址。
主键:InnoDB引擎必须有主键,如果没有指定也会生成一个隐藏列row_id用来存放主键,这是因为聚集索引的数据和主键索引时存放在一起的,没有主键数据没有办法存放。
索引层级关系: InnoDB的主键和数据存放在一起,辅助索引的叶子节点上存放着的是主键的信息,所以InnoDB的主键和辅助索引之间是存在着层级关系,查询命中辅助索引的时候需要走两次索引,先根据辅助索引查找到主键索引,然后再通过主键索引查找到数据。MyIsam的索引不存在层级关系,都是平级的,这也是两者时候使用聚集索引导致的。
锁:InnoDB的锁是基于索引实现的,如果没能命中索引只能使用表锁,只有在命中索引的情况下才可以使用行锁。
2.事务的基本要素(ACID)
原子性 Atomicity
事务被视为不可分割的最小单元,事务中的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用日志来实现,日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
原子性(Atomicity)
事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,这个原子性是从最终结果来看的,从最终结果来看这个过程是不可分割的
一致性 Consistency
数据库在事务执行前后都保持一致性状态。
在一致性状态下,所有事务对一个数据的读取结果都是相同的。
首先回顾一下一致性的定义。所谓一致性,指的是数据处于一种有意义的状态,这种状态是语义上的而不是语法上的。最常见的例子是转帐。例如从帐户A转一笔钱到帐户B上,如果帐户A上的钱减少了,而帐户B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。
从这段话的理解来看,所谓一致性,即,从实际的业务逻辑上来说,最终结果是对的、是跟程序员的所期望的结果完全符合的。
隔离性 Isolution
一个事务所做的修改在最终提交以前,对其它事务是不可见的。
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性 Durability
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
3.事务基本要素之间的关系
只有满足一致性,事务的执行结果才是正确的。
在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。
在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
事务满足持久化是为了能应对系统崩溃的情况。
4. 事务中的一些问题(重点)
这些问题主要是基于数据在多个事务中的可见性来说的。也是并发事务产生的问题。
更新丢失(回滚性丢失,覆盖性丢失)
丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。
「
第一类丢失更新 :A,B 事务同时操作同一数据,A先对改数据进行了更改,B再次更改时失败然后回滚,把A更新的数据也回滚了。(事务撤销造成的撤销丢失)
第二类丢失更新:A,B 事务同时操作同一数据,A先对改数据进行了更改,B再次更改并且提交,把A提交的数据给覆盖了。(事务提交造成的覆盖丢失)
」
脏读
一个事务在执行的过程中读取到了其他事务还没有提交的数据。 这个还是比较好理解的。
「
两个事务同时操作同一数据,A事务对该数据进行了修改还没提交的时候,B事务访问了该条事务,并且使用了该数据,此时A事务回滚,那么B事务读到的就是脏数据。
比如事务1,修改了某个数据 事务2,刚好访问了事务1修改后的数据
此时事务1,回滚了操作 事务2,读到还是回滚前的数据
」
读已提交
从字面上我们就可以理解,即一个事务操作过程中可以读取到其他事务已经提交的数据。
事务中的每次读取操作,读取到的都是数据库中其他事务已提交的最新的数据(相当于当前读)
不可重复读
在同一事务中,多次读取同一数据返回的结果有所不同,换句话说,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读” 在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据。
「
这种情况发生 在一个事务内多次读同一数据。A事务查询某条数据,该事务未结束时,B事务也访问同一数据并进行了修改。那么在A事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。
事务1,查询某个数据 事务2,修改了某个数据,提交
事务1,再次查询这个数据
这样事务1两次查询的数据不一样,称为不可重复读
」
可重复读
一个事务操作中对于一个读取操作不管多少次,读取到的结果都是一样的。
幻读
脏读、不可重复读、可重复读、幻读,其中最难理解的是幻读
幻读现象例子:
可重复读模式下,比如有个用户表,手机号码为主键,有两个事物进行如下操作
事务A操作如下: 1、打开事务 2、查询号码为X的记录,不存在 3、插入号码为X的数据,插入报错(为什么会报错,先向下看) 4、查询号码为X的记录,发现还是不存在(由于是可重复读,所以读取记录X还是不存在的)
事物B操作:在事务A第2步操作时插入了一条X的记录,所以会导致A中第3步插入报错(违反了唯一约束)
上面操作对A来说就像发生了幻觉一样,明明查询X(A中第二步、第四步)不存在,但却无法插入成功
幻读可以这么理解:事务中后面的操作(插入号码X)需要上面的读取操作(查询号码X的记录)提供支持,但读取操作却不能支持下面的操作时产生的错误,就像发生了幻觉一样。
看第二种解释:
事务A在操作一堆数据的时候,事务B插入了一条数据,A事务再次(第二次)查询,发现多了一条数据,像是幻觉。与不可重复读类似,不同的是一个是修改删除操作,一个是新增操作。
5.事务的隔离级别
当多个事务同时进行的时候,如何确保当前事务中数据的正确性,比如A、B两个事物同时进行的时候,A是否可以看到B已提交的数据或者B未提交的数据,这个需要依靠事务的隔离级别来保证,不同的隔离级别中所产生的效果是不一样的。
事务隔离级别主要是解决了上面多个事务之间数据可见性及数据正确性的问题。(或者说为了解决并发控制可能产生的异常问题,数据库定义了四种事务的隔离级别)
隔离级别分为4种:
- 读未提交:READ-UNCOMMITTED
- 读已提交:READ-COMMITTED
- 可重复读:REPEATABLE-READ
- 串行:SERIALIZABLE
6.各种隔离级别中会出现的问题
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 |
---|---|---|---|
READ-UNCOMMITTED | 有 | 有 | 有 |
READ-COMMITTED | 无 | 有 | 有 |
REPEATABLE-READ | 无 | 无 | 有 |
SERIALIZABLE | 无 | 无 | 无 |
7.隔离级别小结
-
读未提交( Read Uncommitted )
读未提交是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。 -
读已提交( Read Committed )
在 Read Committed 隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。 -
可重复读( Repeatable Read )
在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。幻读就是没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了。 -
可串行化( Serializable )
Serializable 是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
默认隔离级别:如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用 InnoDB,默认的隔离级别是Repeatable Read。
8.隔离级别如何选择?
- 需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
- 隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。
- 具体选择哪种需要结合具体的业务来选择。
- 读已提交(READ-COMMITTED)通常用的比较多。
9.二叉树,avl树,红黑树,B树,B+树做索引的优缺点
普通二叉树:容易产生歪脖子问题,退化成链表结构。
avl树:树的高度很高,左右子节点高度差超过1就会自旋转,数据量变多的时候自选很耗时,只适合存放少量数据。
红黑树:也是二叉树,部分自选,但是没解决树高过高的问题,数据量大的话一样会增加io次数,降低效率。
B树:非叶子节点也存放数据,每个page上存放的索引数据就少了,索引的利用率低。
B+树:数据只存在于叶子节点的数据页上,非叶子节点上只存放索引,每个数据也的利用率高。
10. 什么情况下会出现索引失效的情况?
- 没有遵循最左前缀原则
- 使用了前 % 的模糊查询(原因:索引的排列是有序的,把%放在最左边违背了索引有序排列的规则,就会造成索引失效)
- 在查询(select)使用 * ,这里需要使用具体的字段名
- 在索引字段上进行了函数运算
- 范围查询右边的列没有使用索引
- 字符串的字段没有加引号
- 用or关键字连接的条件都必须是索引列
- != 和 not in可能会失效,因为反选可能会命中大量数据,而查询的步骤是先根据索引查主键,再根据主键查数据,如果反选命中了大量数据,效率可能比全表扫描的效率要低,毕竟根据索引查主键也是一部分开销,最后命中了大量数据基本还是在全表扫描,不如一开始就全表扫描;如果反选满足了覆盖索引的条件,那么也会走索引,因为省去了根据主键查询数据的步骤
- is null \ is not null 可能会导致索引失效
- 散列度不高的字段添加索引(用散列度不高的字段构建索引树,会导致遍历B+树的每个分支,导致效率降低)