-
InooDB和MyISAM的区别:
1、innodb支持事务,而myisam不支持事务。
2、innodb支持外键,而myisam不支持外键。
3、innodb是行锁,而myisam是表锁(每次更新增加删除都会锁住表)。
4、innodb和myisam的索引都是基于b+树,但他们具体实现不一样,innodb的b+树的叶子节 点是存放数据的,myisam的b+树的叶子节点是存放指针的。
5、innodb是聚簇索引,必须要有主键,一定会基于主键查询,但是辅助索引就会查询两 次,myisam是非聚簇索引,索引和数据是分离的,索引里保存的是数据地址的指针,主键索引和辅助索引是分开的。
6、innodb不存储表的行数,所以select count( * )的时候会全表查询,而myisam会存放表的行数,select count(*)的时候会查的很快。
总结:mysql默认使用innodb,如果要用事务和外键就使用innodb,如果这张表只用来查询,可以用myisam。如果更新删除增加频繁就使用innodb。 -
什么是数据库索引?
索引是对数据库表的一列或者多列的值进行排序的一种结构,使用索引可以快速访问数据库表中的信息
索引的优缺点:
优点:
1、大大加快数据检索的速度
2、降低排序的成本
3、将随机I/O变成顺序I/O(B+树的叶子节点是连在一起的)
4、加速表与表之间的连接
缺点:
1、索引需要占用物理空间
2、创建索引和维护索引都需要花费时间,对数据进行增删改的时候都需要维护索引 -
MYSQL索引的类型:
FULLTEXT全文索引:
MyISAM存储引擎和InnoDB存储引擎在MySQL5.6.4以上版本支持全文索引,一般用于查找文本中的关键字。全文索引主要是针对文本模糊查询效率1较低的问题
**HASH哈希索引:**基于哈希表实现,对于每一个数据行,通过哈希算法得到哈希码作为key值,将数据行的地址作为value。哈希索引多用于等值查询,时间复杂度为O(1),不支持排序、范围查询及模糊查询
BTREE索引:Balance的意思,是一种多路平衡树,它的结点是有序的,支持排序、分组、范围查询、模糊查询等,并且性能稳定
RTREE:空间数据索引,多用于地理数据的存储,空间数据索引的优势在于范围查找 -
B树和B+树的区别?
1、B树中中的内部结点和叶子结点均存放键和值,而B+树的内部结点只保存键值,叶子结点存放所有的键和值。这样B+树的一个结点就可以存储更多的索引,从而使树的高低变低,减少了IO次数,使得数据库检索速度更快
2、B+树的所有叶子节点之间是链式环结构,而B树不是。B树适用于随机检索,而B+树适用于随机检索和顺序检索
3、B+树的性能更加稳定,每次检索都是从根结点到叶子节点,而在B树中,要查询的值可能不在叶子结点,在内部结点就找到了 -
什么是聚簇索引,什么是非聚簇索引
聚簇索引和非聚簇索引的最主要区别是数据和索引是否分开存储
1、InnoDB存储引擎中,利用主键创建的索引为主键索引,也是聚簇索引,叶子结点存放的就是实际的数据,对于在主键索引上创建的索引为辅助索引,也是非聚簇索引,叶子结点存放的是主键的引用,检索的时候通过主键到主键索引中找到数据行(回表查询)
2、MyISAM存储引擎中,主键索引和辅助索引都是非聚簇索引,索引的叶子结点存储的数一个指向数据行的地址。 -
非聚簇索引一定会回表查询吗?
如果查询的数据在辅助索引上完成能获取,则不需要回表查询。通过索引覆盖能解决非聚簇索引回表查询的问题。 -
为什么推荐使用自增主键而不是UUID?
1、UUID是字符串,比整型消耗更多的存储空间
2、在B+树查询时需要跟经过的结点值比较大小,整型的比较字符串比较更加快速
3、自增主键不会破坏左子树的结构,而UUID是随机产生的,会发生随机IO,影响插入速度。在数据插入的时候会造成大量的数据移动,造成大量的内存碎片。 -
索引设计的原则?
1、最适合的索引的列是在where后面出现的列或者在连接句子出现的列
2、索引的区分度越高,索引的效果越好。比如以性别作为索引就会效果很差
3、尽量使用短索引,对较长的字符串进行索引时应该制定一个较短的前缀长度,因为较小的索引涉及的磁盘IO较少,索引高速缓冲块能容纳更多的键值
4、尽量利用最左前缀,并且尽量将区分度的索引放在前面
5、不要过度索引
索引优化
6、在查询的时候,索引不能是表达式的一部分,也不能是函数的参数,这样无法使用索引
7、尽量少使用select * 索引覆盖 -
什么是前缀索引?
前缀索引是指对文本或者字符串的前一个字符建立索引,这样索引的长度更短,索引高速缓冲块能容纳更多的键值,查询速度更快。使用场景:前缀索引的区分度比较高情况 -
什么是最左匹配原则?
最左匹配原则:从最左边为起点开始连续匹配,遇到范围查询(<,>,between,like)就会停止匹配,对于字符串的情况,只有前缀匹配可以使用索引。 -
索引在什么情况下会失效?
1、条件中有or,用union或者union all
2、在索引上使用计算会导致索引失效
3、在索引中使用函数会使索引失效
4、在索引的类型上进行数据类型的隐形转换,会导致索引失效。字符串一定要加引号
5、在使用like模糊查询时用%开头会导致索引失效
6、索引上使用!=、<>进行判断会导致索引失效
7、索引字段上使用is null/is not null判断时会导致索引失效 -
什么是数据库事务?
访问并可能更新数据库的数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。
事务的四个特性:
1、原子性:原子性是指事物是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
2、一致性:说明数据库事务不能破坏关系数据的完整性和一致性
3、隔离性:隔离性是指并发的事务是相互隔离的。一个事务内部操作即使用的数据对其他并发事务是隔离的
4、持久性:数据一旦提交,其所修改将永久保存在数据库中。 -
数据库事务的隔离级别:
1、未提交读:一个事务在提交之前,它的修改对其他事务也是可见的
2、提交读:一个事务提交之后,它的修改才能被其他事务看到
3、可重复读:在同一个事务中多次读取到的数据是一致的
4、串行化:事务“串行化顺序执行”,也就是一个一个排队执行 -
脏读、不可重复读、幻读:
1、脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
2、不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。重点在于修改。
3、幻读:是值在一个事务中,相同的条件第一次和第二次读出来的记录数不一致,另外一个事务新增或删除了一条记录,这条记录属于第一个事务读取的数据行内。重点在于新增或删除。
#MVCC多版本并发控制
InnoDB中的MVCC是依赖undo log和ReadView来实现的
-
undo日志主要分为两种:
1、**insert undo log:**由inert操作产生的日志,因为insert操作只对事务本身可见,对其他事务是不可见的,只在事务回滚时需要,所以insert undo log在事务提交后可以直接删除
2、**update undo log:**由delete和update操作中产生的日志,该日志后续会被用于MVCC中,因此不能在事务提交的时候删除。只有在快照读和事务回滚不涉及该日志时,对应的日志才会被purge线程删除 -
修改数据的过程:
每次修改一条记录的时候,会将该行当前的值复制到undo log中,然后修改当前行的值,同时会添加一个事务ID,还会有一个回滚指针指向undo log 中修改之前的行(第一次修改的时候会先复制一个当前行到undo log),事务ID是单调递增的。如下截图显示了两次修改后的undo log 链表中的记录情况。
ReadView -
所谓的ReadView主要包含当前系统中活跃的读写事务,并把它们的事务ID放到一个列表中称之为 m_ids。这样我们在访问某条记录的时候只需要按照如下的步骤去判断记录的某个版本是否可见,可见的版本就是我们读取到的数据,注意:当某个事务被提交了,则它就不属于活跃事务了。
1、如果被访问版本的trx_id 属性值小于m_ids 列表中最小的事务id,表明生成该版本的事务在生成ReadView 前已经提交,所以该版本可以被当前事务访问。
2、如果被访问版本的trx_id 属性值大于m_ids 列表中最大的事务id,表明生成该版本的事务在生成ReadView 后才生成,所以该版本不可以被当前事务访问。
3、如果被访问版本的trx_id 属性值在m_ids 列表中最大的事务id和最小事务id之间,那就需要判断一下trx_id 属性值是不是在m_ids 列表中,如果在,说明创建ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView 时生成该版本的事务已经被提交,该版本可以被访问。
4、如果某个版本的数据对当前事务不可见,则顺着版本链找到下一个版本,直到版本链中的最后一个版本,如果最后一个也不可见则查询不到结果
再谈RC和RR
-
在Mysql中RC和RR一个非常大的区别就是生成ReadView的时机不同
1、read committed每次读取数据前都生成一个readview,这样就保证了每次读之前拿到都是最新的活跃事务ID,所以只要有事务提交了,就不再m_ids中,这样的话对于查询操作的事务而言修改过的那个版本记录就是可见的,所以能读到已经提交的数据。
2、repeatable read:只会在事务第一次读取数据的时候生成一个ReadView,之后的查询都不会重复生成了 -
redo log 存储的页的物理日志,即在这个页上做了什么
1、存储引擎在修改表的数据时,只需要修改内存拷贝,再把修改持久化到redo.log里去,后面再找合适的时机将修改的数据持久化到磁盘。
(因为直接将数据写到MySQL中,需要找到MySQL对应的页,涉及随机IO访问,是非常耗时的过程,采用事务日志采用的是追加的方式,设计的是磁盘的顺序IO)
2、如果MySQL进程异常重启了了,会价差redo log,将未写入到MySQL的数据从redo log恢复到MySQL中 -
redo log是固定大小的一组文件,采用“循环写”的方式,redo log设置为一组4个文件
write pos、check point
1、事务提交时,会先到写到redo log离去,写完一个事务,write pos会往前移动。在redo log中的记录被更新到数据库时,check point会向前移动
2、write pos到check point之前表示可以被擦除 -
两段式提交:
MySQL大体可以分为Server层和存储引擎层,其中binlog在Server层,redo log在存储引擎层
1、binlog是MySQL Server层的日志,主要用于数据误删后进行数据恢复。另外,主从复制也需要依赖binlog(可通过上一次的全量备份数据加上某段时间的binlog将数据恢复到指定的某个时间点的数据)
2、过程:redo log是MySQL InnoDB引擎层日志,为了保证两份日志最终恢复的数据库是一致的采用两段式提交。
(1)第一阶段(prepare阶段):写redo-log并将其标记为prepare状态
(2)第二阶段(commit阶段):写bin-log并将其标记为commit状态
为什么需要两段式提交?
为了保证两个日志的一致性
(1)直接先写redolog后写binlog。假设,写完redo log,系统挂了。那么重启后innnoDB引擎会根据redo log日志来恢复数据库。这时候数据库的数据是正确的,但是binlog丢失了。如果有从库,那么从库的数据就错误了。因为从库的数据是通过binlog同步的
(2)如果先写binlog后写redo日志,那么就会丢失redo log,数据库实际上没有更新。导致不一致。
所以需要两阶段提交来保证数据一致性。如果这时候写完redo log后挂掉,因为redo log和binlog都没有数据,所以会回滚数据。如果binlog和redo log都写入了,但是没有提交,那么重启后会提交事务。这样binlog和数据库都会有数据了