MySQL索引相关问题

一、索引有哪些优缺点?

索引的优点

  • 可以大大加快数据的检索速度,这也是创建索引的最主要的原因。

  • 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

  • 通过索引,我们可以对数据进行范围遍历。

索引的缺点

  • 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;

  • 空间方面:索引需要占物理空间。

二、MySQL有哪几种索引类型?

从存储结构上来划分有:

  • B+树索引
  • Hash索引

从应用层次来划分分为:

  • 普通索引:普通索引是最基本的索引,它没有任何限制,值可以为空;仅加速查询。一个表可以有多个普通索引。
  • 唯一索引:唯一索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。简单来说:唯一索引是加速查询 + 列值唯一(可以有null)。
  • 主键索引:主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。简单来说:主键索引是加速查询 + 列值唯一(不可以有null)+ 表中只有一个
  • 组合索引:组合索引指在多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
  • 全文索引:全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。
  • 覆盖索引:一个索引包含(覆盖)所有需要查询字段的值。

从存储角度上来划分分为:

  • 聚集索引
  • 非聚集索引

三、Hash索引和B+树索引的区别?

        首先hash索引的机制在于hash函数的设计,因此在数据量非常大的情况下,难免会出现hash冲突的情况。其次,当预设的空间的使用完以后,就要进行扩容,而进行扩容时就要对已经存储的所有数据重新计算一遍hash码,这一过程的效率十分低下。再有就是,hash索引无法提供范围遍历的功能,也无法进行模糊查询,例如 like %xxx的操作,也不支持多列联合查询

        利用Hash需要把数据全部加载到内存中,如果数据量大,是一件很消耗内存的事,而采用B+树,是基于按照节点分段加载,由此减少内存消耗

        和业务场景有段,对于唯一查找(查找一个值),Hash确实更快,但数据库中经常查询多条数据,这时候由于B+数据的有序性,与叶子节点又有链表相连,他的查询效率会比Hash快的多。

四、为什么索引结构默认使用B+Tree,而不是B树,二叉树,红黑树?

        如果采用二叉树作为索引,并且把id作为索引且id自增,那么二叉查找树就变成了一个单支树,相当于链表查询。即BST可能长歪而变得不平衡了。

        AVL树在进行数据的插入和删除时,要进行自平衡操作,即多次旋转,当数据量将大的情况下,这些旋转操作的效率将会十分低下。

        红黑树做到了大致平衡,因此自平衡操作不会导致效率低下,但是对于数据在磁盘等辅助存储设备中的情况,红黑树还是并不擅长,因为红黑树还是有点高。因为当数据在磁盘中,磁盘IO会成为最大的性能瓶颈,设计的目标应该是尽量减少IO次数;而树的高度越高,增删改查所需要的IO次数也越多,会严重影响性能。

        对于B树来说:

(1)B+树的磁盘读写代价更低

  B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了;

(2)B+树查询效率更加稳定

  由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当;

(3)B+树便于范围查询(最重要的原因,范围查找是数据库的常态)

  B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低;不懂可以看看这篇解读-》范围查找。B树的范围查找用的是中序遍历,而B+树用的是在链表上遍历。

五、什么是回表查询

        首先我们要清楚回表查询是针对于聚集索引来讲的,当我们查询语句所要求的字段没有全部命中,那么就要进行回表查询。即使用在辅索引中查找到的主索引ID,再去主索引里面查找。

六、什么是联合索引?它有什么要注意的事项吗?

        MySQL可以使用多个字段同时建立一个索引,叫做联合索引。在联合索引中,如果想要命中索引,需要按照建立索引时的字段顺序挨个使用,否则无法命中索引。这就叫做最左匹配原则。

        最左匹配原则:MYSQL | 最左匹配原则的原理 - 云+社区 - 腾讯云 (tencent.com)

最左匹配原则 - Mr-blue - 博客园 (cnblogs.com)

六、什么是前缀索引

        因为可能我们索引可能是一个字符串字段,且这个字段非常长,这既占内存空间,也不利于维护。所以我们就想,如果只把很长字段的前面的公共部分作为一个索引,这样即省时又省力,这就是前缀索引。

什么情况下使用前缀索引:

  • 字符串列(varchar,char,text等),需要进行全字段匹配或者前匹配。也就是=‘xxx’ 或者 like ‘xxx%'
  • 字符串本身可能比较长,而且前几个字符就开始不相同。比如我们对中国人的姓名使用前缀索引就没啥意义,因为中国人名字都很短,另外对收件地址使用前缀索引也不是很实用,因为一方面收件地址一般都是以XX省开头,也就是说前几个字符都是差不多的,而且收件地址进行检索一般都是like ’%xxx%’,不会用到前匹配。相反对外国人的姓名可以使用前缀索引,因为其字符较长,而且前几个字符的选择性比较高。同样电子邮件也是一个可以使用前缀索引的字段。
  • 前一半字符的索引选择性就已经接近于全字段的索引选择性。如果整个字段的长度为20,索引选择性为0.9,而我们对前10个字符建立前缀索引其选择性也只有0.5,那么我们需要继续加大前缀字符的长度,但是这个时候前缀索引的优势已经不明显,没有太大的建前缀索引的必要了。

注意,使用前缀索引时,不能使用ORDER BY 或 GROUP BY。

七、为什么建议使用自增长主键作为索引?

        结合B+Tree的特点,自增主键是连续的,在插入过程中尽量减少页分裂,即使要进行页分裂,也只会分裂很少一部分。并且能减少数据的移动,每次插入都是插入到最后。总之就是减少分裂和移动的频率,降低随机I/O。

        使用自增 id 做主键索引新插入数据只要放在该页的最尾端就可以,直接「按照顺序插入」,不用刻意维护。新插入的行一定会在原有的最大数据行下一行,mysql定位和寻址很快,不会为计算新行的位置而做出额外的消耗。

        页分裂容易维护,当插入数据的当前页快满时,会发生页分裂的现象,如果主键索引不为自增 id,那么数据就可能从页的中间插入,页的数据会频繁的变动,「导致页分裂维护成本较高」

八、什么情况下不走索引(索引失效)?

1、使用!= 或者 <> 导致索引失效

2、类型不一致导致的索引失效

3、函数导致的索引失效

4、运算符导致的索引失效

5、OR引起的索引失效,OR导致索引是在特定情况下的,并不是所有的OR都是使索引失效,如果OR连接的是同一个字段,那么索引不会失效,反之索引失效。

6、模糊搜索导致的索引失效,当%放在匹配字段前是不走索引的,放在后面才会走索引。

7、NOT IN、NOT EXISTS导致索引失效

8、索引字段是字符串,但查询时不加单引号,会导致索引失效而转向全表扫描

九、MyISAM 与 InnoDB 的区别是什么?

  • InnoDB支持事务,MyISAM不支持,但是每次查询都是原子的
  • InnoDB 支持外键,而 MyISAM 不支持
  • InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和索引绑在一起的,必须要有主键。MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  • Innodb 有 redolog 日志文件,MyISAM 没有。
  • InnoDB 支持表、行锁,而 MyISAM 只支持表级锁。
  • InnoDB 必须有唯一索引(主键),如果没有指定的话 InnoDB 会自己生成一个隐藏列Row_id来充当默认主键,MyISAM 可以没有。
  • InnoDB 支持ACID事务,支持四种隔离级别,MyISAM 二者都不支持。

十、使用 Innodb 的情况下,一条更新语句是怎么执行的?

用以下语句来举例,c 字段无索引,id 为主键索引

update T 
set c=c+1 
where id=2;

1.执行器先找引擎取 id=2 这一行。id 是主键,引擎直接用树搜索找到这一行

  •  如果 id=2 这一行所在的数据页本来就「在内存中」,就「直接返回」给执行器
  • 「不在内存」中,需要先从磁盘「读入内存」,然后再「返回」

2.执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口「写入这行新数据」

3.引擎将这行新数据更新到内存中,同时将这个更新操作「记录到 redo log 里面」,此时 redo log 处于 「prepare」 状态。然后告知执行器执行完成了,随时可以提交事务

4.执行器「生成这个操作的 binlog」,并把 binlog 「写入磁盘」

5.执行器调用引擎的「提交事务」接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,「更新完成」        

        我们注意到,当更新完redo log之后,并没有立即将redo log提交,而是先去更新binlog,等binlog写入磁盘后,才去将redo log提交——即我们将事务分为了两个间段提交。、

        而且我们还发现,这里的更新操作最后只更新到了内存中,并没有更新到磁盘中,这又是为什么呢?

十一、Innodb 事务为什么要两阶段提交?

  • 先写 redolog 后写binlog。假设在 redolog 写完,binlog 还没有写完的时候,MySQL 进程异常重启,这时候 binlog 里面就没有记录这个语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 「binlog 丢失」,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

  • 先写 binlog 后写 redolog。如果在 binlog 写完之后 crash,由于 redolog 还没写,崩溃恢复以后这个事务无效,所以这一行c的值是0。但是 binlog 里面已经记录了“把c从0改成1”这个日志。所以,在之后用 binlog 来恢复的时候就「多了一个事务出来」,恢复出来的这一行 c 的值就是 1,与原库的值不同。

        可以看到,「如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致」

十二、什么是索引下推

       不使用索引条件下推优化时存储引擎通过索引检索到数据,然后返回给MySQL服务器,服务器然后判断数据是否符合条件。

        当使用索引条件下推优化时,如果存在某些被索引的列的判断条件时,MySQL服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器。索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储引擎接收数据的次数。

工作过程:

不使用索引条件下推优化时的查询过程:获取下一行,首先读取索引信息,然后根据索引将整行数据读取出来。然后通过where条件判断当前数据是否符合条件,符合返回数据。

使用索引条件下推优化时的查询过程:获取下一行的索引信息。检查索引中存储的列信息是否符合索引条件,如果符合将整行数据读取出来,如果不符合跳过读取下一行。用剩余的判断条件,判断此行数据是否符合要求,符合要求返回数据。

索引下推优化技术其实就是充分利用了索引中的数据,尽量在查询出整行数据之前过滤掉无效的数据。由于需要存储引擎将索引中的数据与条件进行判断,所以这个技术是基于存储引擎的。

十三、什么是覆盖索引?

覆盖索引指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取,可以减少回表的次数。

十四、普通索引和唯一索引该怎么选择?

从查询的角度来考虑:

  • 当普通索引为条件时查询到数据会一直扫描,直到扫完整张表。
  • 当唯一索引为查询条件时,查到该数据会直接返回,不会继续扫表。

从更新的角度来考虑:

  • 普通索引会直接将操作更新到 change buffer 中,然后结束。
  • 唯一索引需要判断数据是否冲突。

所以唯一索引更加适合查询的场景,普通索引更适合插入的场景。

        为了说明普通索引和唯一索引对更新语句性能的影响这个问题,我需要先跟你介绍一下 change buffer。当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。

        需要说明的是,虽然名字叫作 change buffer,实际上它是可以持久化的数据。也就是说,change buffer 在内存中有拷贝,也会被写入到磁盘上。

        将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会定期 merge。在数据库正常关闭的过程中,也会执行 merge 操作。

        显然,如果能够将更新操作先记录在 change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用 buffer pool 的,所以这种方式还能够避免占用内存,提高内存利用率。

那么,什么条件下可以使用 change buffer 呢?

        对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。比如,要插入 (4,400) 这个记录,就要先判断现在表中是否已经存在 k=4 的记录,而这必须要将数据页读入内存才能判断。如果都已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer 了。

因此,唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用。

        第二种情况是,这个记录要更新的目标页不在内存中。这时,InnoDB 的处理流程如下:

        对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束;对于普通索引来说,则是将更新记录在 change buffer,语句执行就结束了。将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。change buffer 因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。

        通过上面的分析,你已经清楚了使用 change buffer 对更新过程的加速作用,也清楚了 change buffer 只限于用在普通索引的场景下,而不适用于唯一索引。那么,现在有一个问题就是:普通索引的所有场景,使用 change buffer 都可以起到加速作用吗?

        因为 merge 的时候是真正进行数据更新的时刻,而 change buffer 的主要目的就是将记录的变更动作缓存下来,所以在一个数据页做 merge 之前,change buffer 记录的变更越多(也就是这个页面上要更新的次数越多),收益就越大。

        因此,对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时 change buffer 的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。

        反过来,假设一个业务的更新模式是写入之后马上会做查询,那么即使满足了条件,将更新先记录在 change buffer,但之后由于马上要访问这个数据页,会立即触发 merge 过程。这样随机访问 IO 的次数不会减少,反而增加了 change buffer 的维护代价。所以,对于这种业务模式来说,change buffer 反而起到了副作用。

十五、binlog 是做什么的?

binlog 是归档日志,属于 Server 层的日志,是一个二进制格式的文件,用于「记录用户对数据库更新的SQL语句信息」。用于备份,主从同步复制,如果采用一主多从架构,主备切换,那就必须用到binlog进行主从同步。

主要作用

  • 主从复制

  • 数据恢复

十六、undolog 是做什么的?

        undolog 是 InnoDB 存储引擎的日志,用于保证数据的原子性,一般是逻辑日志,根据每行记录进行记录。「保存了事务发生之前的数据的一个版本,也就是说记录的是数据是修改之前的数据,可以用于回滚」,同时可以提供多版本并发控制下的读(MVCC)。

主要作用

  • 事务回滚

  • 实现多版本控制(MVCC)

十七、redolog 是做什么的?

redolog 是 「InnoDB 存储引擎所特有的一种日志」通常是物理日志,用于记录的是数据页的物理修改,记录的是数据修改之后的值,不管事务是否提交都会记录下来。

可以做「数据恢复并且提供 crash-safe 能力」

当有增删改相关的操作时,会先记录到 Innodb 中,并修改缓存页中的数据,「等到 mysql 闲下来的时候才会真正的将 redolog 中的数据写入到磁盘当中」

十八、redo log和binlog区别

  • redo log是属于innoDB层面,binlog属于MySQL Server层面的,这样在数据库用别的存储引擎时可以达到一致性的要求。
  • redo log是物理日志,记录该数据页更新的内容;binlog是逻辑日志,记录的是这个更新语句的原始逻辑
  • redo log是循环写,日志空间大小固定;binlog是追加写,是指一份写到一定大小的时候会更换下一个文件,不会覆盖。
  • binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。
  • 崩溃恢复的时候,会按顺序扫描redo log,若redolog既有prepare又有commit,直接提交。如果碰到只有prepare、而没有commit的redo log,就拿着XID去binlog找对应的事务。

十九、relaylog 是做什么的?

relaylog 是中继日志,「在主从同步的时候使用到」,它是一个中介临时的日志文件,用于存储从master节点同步过来的binlog日志内容。

图片

master 主节点的 binlog 传到 slave 从节点后,被写入 relay log 里,从节点的 slave sql 线程从 relaylog 里读取日志然后应用到 slave 从节点本地。从服务器 I/O 线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后 SQL 线程会读取 relay-log 日志的内容并应用到从服务器,从而「使从服务器和主服务器的数据保持一致」

二十、一条 Sql 语句查询偶尔慢会是什么原因?

图片

  • 「1. 数据库在刷新脏页」

    • 比如 「redolog 写满了」「内存不够用了」释放内存如果是脏页也需要刷,mysql 「正常在空闲状态刷脏页」

  • 「2. 没有拿到锁或者说是遇到锁」

二十一、什么是刷脏页

首先,我们要了解InnoDB 的 redo log 是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,那么总共就可以记录4GB的操作。「从头开始写,写到末尾就又回到开头循环写」

图片

        当内存数据页和磁盘数据页上的内容不一致时,我们称这个内存页为脏页;内存数据写入磁盘后,内存页上的数据和磁盘页上的数据就一致了,我们称这个内存页为干净页

        redo  log 是循环写的,当redo log 写满了,即 write pos 追上了  check point 时,此时没有空间记录 redo log,就需要将 check point 向前推进,推进的这部分日志对应的脏页就需要刷入磁盘。此时所有的更新全部阻塞,此时写性能跌为0,必须等待刷一部分脏页后才能继续更新。

        当系统内存不足时,就需要将一部分数据页淘汰掉,如果淘汰的是脏页,就需要先将脏页刷入磁盘。当淘汰的脏页过多时,会导致查询的响应时间变长;

 二十二、删除表数据后表的大小却没有变动,这是为什么?

        在使用 delete 删除数据时,其实对应的数据行并不是真正的删除,是「逻辑删除」,InnoDB 仅仅是将其「标记成可复用的状态」,所以表空间不会变小

 二十三、什么是buffer pool

        大家都知道mysql数据其实是放在磁盘里面的,如果每次查询都直接从磁盘里面查询,这样势必会很影响性能,所以一定是先把数据从磁盘中取出,然后放在内存中,下次查询直接从内存中来取。但是一台机器中往往不是只有mysql一个进程在运行的,很多个进程都需要使用内存,所以mysql中会有一个专门的区域来处理这些数据,这个专门为mysql准备的区域,就叫buffer pool。

 但是我们在对数据库执行增删改操作的时候,不可能直接更新磁盘上的数据的,因为如果你对磁盘进行随机读写操作,那速度是相当的慢,随便一个大磁盘文件的随机读写操作,可能都要几百毫秒。如果要是那么搞的话,可能你的数据库每秒也就只能处理几百个请求了! 在对数据库执行增删改操作的时候,实际上主要都是针对内存里的Buffer Pool中的数据进行的,也就是实际上主要是对数据库的内存里的数据结构进行了增删改。

        如果我们要修改的数据此时不在buffer poll中,那么系统则会去磁盘中查找,磁盘中如果找到了对应的数据,则会把该页的数据直接copy一份到buffer pool,然后再做处理。

二十四、Sql 调优

1.「表结构优化」

  • 1.1拆分字段

  • 1.2字段类型的选择

  • 1.3字段类型大小的限制

  • 1.4合理的增加冗余字段

  • 1.5新建字段一定要有默认值

2.「索引方面」

  • 2.1索引字段的选择

  • 2.2利用好mysql支持的索引下推,覆盖索引等功能

  • 2.3唯一索引和普通索引的选择

3.「查询语句方面」

  • 3.1避免索引失效

  • 3.2合理的书写where条件字段顺序

  • 3.3小表驱动大表

  • 3.4可以使用force index()防止优化器选错索引

4.「分库分表」

二十四、MyISAM和InnoDB使用场景

        大多数时候我们使用的都是 InnoDB 存储引擎,在某些读密集的情况下,使用 MyISAM 也是合适的。不过,前提是你的项目不介意 MyISAM 不支持事务、崩溃恢复等缺点(可是~我们一般都会介意啊!)。

  • MyISAM适合读多更新少的:MyISAM索引跟数据分开放,因此有读取更快的说法。

  • InnoDB适合插入更新频繁的:索引与数据一起放,建立索引更复杂,使用行锁,更新频繁效率更高

  • 需要事务,高并发场景用Innodb:Innodb支持事务,采用行锁

  • MyISAM查询比InnoDB快,更新InnoDB快

场景:MyISAM查询更优,InnoDB更新更优

        MyISAM适合读多,更新少的场景。MyISAM使用非聚簇索引,数据和索引分开存的,而InnoDB数据和索引存一起的,数据量大时,一个内存页大小固定,读进内存的数据MyISAM就多一点(数据量小看不出差距,数据量大时差距就明显)。

        因为MyISAM只把索引指针读进内存,可以存更多,查询速度也就更快,而且InnoDB还需要维护其他数据,比如其他隐藏字段 row_id、tx_id等。

二十五、详解自增主键

自增主键:

        InnoDB引擎的自增值,一开始是保存在了内存里,并且到了MySQL 8.0版本后,才有了“自增值持久化”的能力。

        也就是才实现了“如果发生重启,表的自增值可以恢复为MySQL重启前的值”,具体情况是:(查看表结构,会看到自增主键=多少)

  • 在MySQL 5.7及之前的版本,自增值保存在内存里,并没有持久化。每次重启后,第一次打开表的时候,都会去找自增值的最大值max(id),然后将max(id)+1作为这个表当前的自增值。

  • 在MySQL 8.0版本,将自增值的变更记录在了redo log中,重启的时候依靠redo log恢复重启之前的值。

自增值修改机制:

  1. 如果插入数据时id字段指定为0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT值填到自增字段;

  2. 如果插入数据时id字段指定了具体的值,就直接使用语句里指定的值。

自增值新增机制

  1. 如果准备插入的值>=当前自增值,新的自增值就是“准备插入的值+1”;

  2. 否则,自增值不变。

为什么自增主键不连续

  • 在MySQL 5.7及之前的版本,自增值保存在内存里,并没有持久化

  • 事务回滚(自增值不能回退,因为并发插入数据时,回退自增ID可能造成主键冲突)

  • 唯一键冲突(当插入一条数据的主键和已插入数据的主键冲突时,会导致自增值+1,且插入失败)

假设,表t里面已经有了(1,1,1)这条记录,这时我再执行一条插入数据命令:

insert into t values(null, 1, 1); (自增id,唯一键c,普通字段d)

这个语句的执行流程就是:

  1. 执行器调用InnoDB引擎接口写入一行,传入的这一行的值是(0,1,1);

  2. InnoDB发现用户没有指定自增id的值,获取表t当前的自增值2;

  3. 将传入的行的值改成(2,1,1);

  4. 将表的自增值改成3;

  5. 但是由于已经存在c=1的记录,所以报Duplicate key error,语句返回。

        这个表的自增值改成3,是在真正执行插入数据的操作之前。这个语句真正执行的时候,因为碰到唯一键c冲突,所以id=2这一行并没有插入成功,但也没有将自增值再改回去。

        所以,在这之后,再插入新的数据行时,拿到的自增id就是3。也就是说,出现了自增主键不连续的情况。

二十六、隔离级别原理及解决问题分析

  1. 读未提交:原理:直接读取数据,不能解决任何并发问题

  2. 读已提交:读操作不加锁,写操作加排他锁,解决了脏读。原理:利用MVCC实现,每一句语句执行前都会生成Read View(一致性视图)

  3. 可重复读:MVCC实现,只有事务开始时会创建Read View,之后事务里的其他查询都用这个Read View。解决了脏读、不可重复读,快照读(普通查询,读取历史数据)使用MVCC解决了幻读,当前读(读取最新提交数据)通过间隙锁解决幻读(lock in share mode、for update、update、detete、insert),间隙锁在可重复读下才生效。(默认隔离级别

  4. 可串行化:原理:使用锁,读加共享锁,写加排他锁,串行执行

总结:读已提交和可重复读实现原理就是MVCC Read View不同的生成时机。可重复读只在事务开始时生成一个Read View,之后都用的这个;读已提交每次执行前都会生成Read View。

二十七、SQL优化思路

(1)数据库中设置SQL慢查询

(2)分析慢查询日志

可以通过show processlist命令定位低效率执行sql;可以用explain 分析执行计划,是否索引失效,用到索引没,用了哪些。

(3)优化

索引

1、尽量覆盖索引,5.6支持索引下推

2、组合索引符合最左匹配原则

3、避免索引失效

4、再写多读少的场景下,可以选择普通索引而不要唯一索引

更新时,普通索引可以使用change buffer进行优化,减少磁盘IO,将更新操作记录到change bufer,等查询来了将数据读到内存再进行修改.

5、索引建立原则(一般建在where和order by,基数要大,区分度要高,不要过度索引,外键建索引)

sql语句

1、分页查询优化

2、优化insert语句

  • 多条插入语句写成一条

  • 在事务中插数据

  • 数据有序插入(主键索引)

数据库结构优化

1、将字段多的表分解成多个表

有些字段使用频率高,有些低,数据量大时,会由于使用频率低的存在而变慢,可以考虑分开。

2、对于经常联合查询的表,可以考虑建立中间表

3、分库分表

分表通常来说有水平分表、垂直分表两种划分方式。

垂直分表将一张表的数据,根据场景切分成多张表,本质是由于前期抽象不足,需要将业务数据进一步拆分。

水平分表则是将一张大表拆成多个结构相同的子表。直观来看表结构都是一样的,可以按某个字段来进行业务划分,也可以按照数据量来划分,划分的规则实际就是按某种维度,预判数据量进行拆分。

优化器优化

磁盘预读:请求一页的数据时,可以把后面几页的数据也一起返回,放到数据缓冲池中,这样如果下次刚好需要下一页的数据,就不再需要到磁盘读取(局部性原理)

架构优化

读/写分离(主库写,从库读)

猜你喜欢

转载自blog.csdn.net/ThinPikachu/article/details/121600266