数据库方面?

索引什么时候会失效?

失效的常见场景与原因

  1. 最左前缀法则 :如果是联合索引,查询从索引的最左侧开始,不跳过其他索引. 如果跳过,则索引失效
  2. 范围查询:使用范围查询时,范围查询条件的右侧的列的索引失效
  3. like查询是以%开头,索引失效;以%结尾,索引有效
  4. 如果MYSQL评估使用索引比全表扫描还慢,则MYSQL自动放弃索引查询
  5. 条件中有or必须每个列都加上索引,如果没用就失效
  6. where中索引列有运算
  7. where中索引列使用了函数(因为查询的时候,每一层都会调用一次函数 ,函数调用的返回值你不能说他是一定的,所以如果一开始判断是向左移动,但是再次调用的时候,是向右了?)

数据库慢查询优化?

  1. 慢SQL定位(慢查询日志)
    定位慢SQL相对来说很简单,因为Mysql中已经提供了对应的工具,我们只需要开启对应的“慢查询日志”功能,然后稍作配置即可,开启功能有Mysql会把查询时间大于你设置时间的SQL记录下来,并且保存到一个专门的文件中,你只需要查看这个文件内容就可以找到对应查询慢的SQL了,配置了慢查询日志后,它会记录在设定时间范围内的数据查询和数据修改语句。
  2. 具体优化(用Explain)
  3. 如果实在在语句上没办法优化了,可以考虑重新组织表结构,比如说分区分表,把数据总量降下来。

Mysql的Explain的参数?

答:

  • Explain的作用:
  1. 分析出表的读取顺序
  2. 数据读取操作的操作类型
  3. 哪些索引可以使用
  4. 哪些索引被实际使用
  5. 表之间的引用
  6. 每张表有多少行被优化器查询
  • Explain的具体参数:

1. id id代表执行select子句或操作表的顺序,例如,上述的执行结果代表只有一次执行而且执行顺序是第一(因为只有一个id为1的执行结果),id分别有三种不同的执行结果,分别如下:(1)id相同,执行顺序由上至下(2)id不同,如果是子查询,id的序号会递增,id值越大,优先级越高,越先被执行(3)id相同和不同,同时存在,遵从优先级高的优先执行,优先级相同的按照由上至下的顺序执行
2. select_type:查询的类型,主要用于区别普通查询,联合查询,子查询等复杂查询
(1)simple:简单的select查询,查询中不包含子查询或union查询
(2)primary:查询中若包含任何复杂的子部分,最外层查询则被标记为primary
(3)subquery:在select 或where 列表中包含了子查询
(4)derived: 在from列表中包含的子查询被标记为derived,mysql会递归这些子查询,把结果放在临时表里
(5)union:做第二个select出现在union之后,则被标记为union,若union包含在from子句的子查询中,外层select将被标记为derived
(6)union result: 从union表获取结果的select
3. table:显示一行的数据时关于哪张表的
4. type:告诉我们对表使用的访问方式,主要包含如下集中类型。
注意:查询类型从最好到最差依次是:system>const>eq_ref>ref>range>index>All,一般情况下,得至少保证达到range级别,最好能达到ref

这个参数的优化在于你的查询方式,如果发现type 在range以下,就必须优化,想着自己会不会可能用错列查询了,可以不可换成主键查询。或者是唯一性索引的列。

(1)all: 全表扫描,是最差的一种查询类型
(2) const:表示通过索引一次就找到了,const即常量,它用于比较primary key或unique索引,因为只匹配一行数据,所以效率很快,如将主键置于where条件中,mysql就能将该查询转换为一个常量。
(3) eq_ref: 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描
(4) fulltext: 进行全文索引检索。
(5) index: index类型只遍历索引树,这通常比All快,因为索引文件通常比数据文件小,index是从索引中读取,all从硬盘中读取。
(6) range: 只检索给定范围的行,使用一个索引来选择行,如where语句中出现了between,<,>,in等查询,这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。
(7) ref: 非唯一性索引扫描,返回匹配某个单独值的行,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
(8) system:系统表,表中只有一行数据;
5. possible_keys:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用

该列完全独立于EXPLAIN输出所示的表的次序。这意味着在possible_keys中的某些键实际上不能按生成的表次序使用。
如果该列是NULL,则没有相关的索引。在这种情况下,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询

这里的优化我们要想着,这里的索引有可能被用上,我们是否可以通过改写SQL语句让他用上索引,加快查询速率。

6. key:
key列显示MySQL实际决定使用的键(索引)

如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

7. key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

不损失精确性的情况下,长度越短越好
8. ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

9. rows:

表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
10. Extra:

该列包含MySQL解决查询的详细信息,有以下几种情况:

Using where:列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤

Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询

Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”

Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

Impossible where:这个值强调了where语句会导致没有符合条件的行。

Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

MySQL主从复制?

答:

  • 原理:根据二进制日志文件来实现,这个二进制日志是用来记录一些数据库变化的数据的。主库中发生修改时,记录到二进制文件中,然后从库启动一个IO线程去接受改变的二进制文件内容,然后在用SQL线程去执行,并且这里的主从复制分为两种:基于行的复制,基于语句的复制,这两种复制都是基于二进制日志。在实际应用中,MySQL是两种混合用,如果基于语句的无法完成任务,那么就使用基于行的。

  • 作用: 1、做数据的热备,作为后备数据库,主数据库服务器故障后,可切换到从数据库继续工作,避免数据丢失。架构的扩展。业务量越来越大,I/O访问频率过高,单机无法满足,此时做多库的存储,降低磁盘I/O访问的评率,提高单个机器的I/O性能。2、读写分离,使数据库能支持更大的。在报表中尤其重要。由于部分报表sql语句非常的慢,导致锁表,影响前台服务。如果前台使用master,报表使用slave,那么报表sql将不会造成前台锁,保证了前台速度。

  • 带来的问题: 1、主从延迟:MySQL默认的主从复制是异步的,如果在主库插⼊数据后⻢上去从库查询,可能会发⽣查不到的情况。正常情况下主从复制会存在毫秒级的延迟,在DB负载较⾼的情况下可能存在秒级延迟甚⾄更久,但即使是毫秒级的延迟,对于实时性要求较⾼的业务来说也是不可忽视的。所以在⼀些关键的查询场景,我们会将查询请求绑定到主库来避免主从延迟的问题。2、从库的数量是有限的:⼀个主库能挂载的从库数量是很有限的,没办法做到⽆限的⽔平扩展从库越多,虽然理论上能承受的QPS就越⾼,但是从库过多会导致主库主从复制IO压⼒更⼤,造成更⾼的延迟,从⽽影响业务,所以⼀般来说只会在主库后挂载有限的⼏个从库。3、⽆法解决TPS⾼的问题:从库虽然能解决QPS⾼的问题,但没办法解决TPS⾼的问题,所有的写请求只有主库能处理,⼀旦TPS过⾼,DB依然有宕机的⻛险。

  1. MySql主库在事务提交时会把数据变更作为事件记录在二进制日志Binlog中;
  2. 主库推送二进制日志文件Binlog中的事件到从库的中继日志Relay Log中,之后从库根据中继日志重做数据变更操作,通过逻辑复制来达到主库和从库的数据一致性;
  3. MySql通过三个线程来完成主从库间的数据复制,其中Binlog Dump线程跑在主库上,I/O线程和SQL线程跑着从库上;
  4. 当在从库上启动复制时,首先创建I/O线程连接主库,主库随后创建Binlog Dump线程读取数据库事件并发送给I/O线程,I/O线程获取到事件数据后更新到从库的中继日志Relay Log中去,之后从库上的SQL线程读取中继日志Relay Log中更新的数据库事件并应用,如下图所示。
图例
  • 基于语句的复制(逻辑复制):主库会记录那些造成数据更改的SQL,当备库读取并重放这些事件时,实际上只是把主库上执行过的SQL再执行一遍。比如UPDATE enormous_table SET col1= O(更新全表)
    优点:实现相当简单,并且占用的资源少,比如更新上百万条数据的SQL,他只需要记录那个SQL语句,而不需要记录整个改变的数据
    缺点:同一条SQL在主库和备库上执行的时间可能稍微或很不相同,因此在传输的二进制日志中,除了查询语句,还包括了一些元数据信息,如当前的时间戳。即便如此,还存在着一些无法被正确复制的SQL。可能这个语句调用了获取当前时间这个函数,但是因为主备库执行时间不同,那么很有可能导致数据不一致。
  • 基于行的复制(物理复制):这种方式会将实际数据记录在二进制日志中。
    优点:可以正确地复制每一行。可以处理基于语句复制会出错的情况
    缺点:对于一些全表操作的SQL,会很消耗资源,采用基于语句的会节省很多资源
  • 配置过程:
  1. 在主库中先建立数据同步用户,赋予相应的权限,让备库能从主库中读取数据。
  2. 配置主库和备库,在My.cnf文件中配置,最主要的就是配置Server ID,这个ID必须全局唯一,另外还有开启二进制日志
[mysqld]
## 设置server_id,同一局域网中需要唯一
server_id=102
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql
## 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
## 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
## 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
## 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
## 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
## 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
## relay_log配置中继日志
relay_log=mall-mysql-relay-bin
## log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
## slave设置为只读(具有super权限的用户除外)
read_only=1
  1. 在MySQL中用命令配置
    change master to master_host='192.168.6.132', master_user='slave', master_password='123456', master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=617, master_connect_retry=30;

主从复制延迟应该如何解决?

答:

  • 原因: 1、主库DML请求频繁:某些业务高峰期间,特别是对于数据库主库有大量的写请求操作,即大量insert、delete、update等并发操作的情况下,会出现主从复制延时问题。在短时间产生了大量的binlog。这些操作需要全部同步到从库,并且执行,因此产生了主从的数据复制延时。从库来不及同步 2、从库自身压力过大:有时候,从库性能压力很大的情况下,跟不上主库的更新速度,就产生了主从复制延时。3、从库的机器性能比主库要差:跟不上主库的速度

  • 解决:1、降低多线程大事务并发的概率,优化业务逻辑
    2、优化SQL,避免慢SQL,减少批量操作,建议写脚本以update-sleep这样的形式完成。3、提高从库机器的配置,减少主库写binlog和从库读binlog的效率差。4、尽量采用短的链路,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。5、实时性要求的业务读强制走主库,从库只做灾备,备份。

MySQL组提交?

答:
redo log 落盘机制
mysql innodb刷盘参数解析
redo log 和 bin log组提交
组提交解决的问题
2pc 文章
MySQL binlog和redo的组提交

  • 组提交(group_commit):组提交 (group commit) 是为了优化写日志时的刷磁盘问题,从最初只支持 InnoDB redo log 组提交,到 5.6 官方版本同时支持 redo log 和 binlog 组提交,大大提高了 MySQL 的事务处理性能。

  • 在没有开启binlog时: Redo log的刷盘操作将会是最终影响MySQL TPS的瓶颈所在。为了缓解这一问题,MySQL使用了组提交,将多个刷盘操作合并成一个,如果说10个事务依次排队刷盘的时间成本是10,那么将这10个事务一次性一起刷盘的时间成本则近似于1。

  • 当开启binlog时: 因为主从数据库一致性问题为了保证Redo log和binlog的数据一致性,MySQL使用了二阶段提交,由binlog作为事务的协调者。而引入二阶段提交使得binlog又成为了性能瓶颈,先前的Redo log组提交不能允许执行,mysql 采用 锁将redo log 和 bin log 串行化(因为如果你允许组提交,也就是说你提交的时候会顺带把其他组的redo log 也提交了,但是commit的顺序可能不一样,最终导致数据不一致,也就是说,事务的顺序为 T1、T2、T3、commit顺序为:c2、c3、c1、此时如果bin log写完成了,但是commit没完成到c1就关机了,重启后就会回滚事务,但是从服务器的binlog还是有这个记录的 。这里本质就是redo log 和 commit顺序不一致导致的)。为了再次缓解这一问题,MySQL增加了binlog的组提交,目的同样是将binlog的多个刷盘操作合并成一个,结合Redo log本身已经实现的 组提交,分为三个阶段(Flush 阶段、Sync 阶段、Commit 阶段)完成binlog 组提交,最大化每次刷盘的收益,弱化磁盘瓶颈,提高性能。

自 5.1 之后,binlog 和 innodb 采用类似两阶段提交的方式,不过不支持 group commit;在 5.6 中,将 binlog 的 commit 阶段分为三个阶段:flush stage、sync stage 以及 commit stage。这三个阶段中,每个阶段都会去维护一个队列,各个列表的定义如下。

Mutex_queue m_queue[STAGE_COUNTER];

如上,每个阶段都在维护一个队列,第一个进入该队列的作为 leader 线程,否则作为 follower 线程;leader 线程会收集 follower 的事务,并负责做 sync,follower 线程等待 leader 通知操作完成。

尽管维护了三个队列,但队列中所有的 THD 实际上都是通过 next_to_commit 连接起来。binlog 在事务提交阶段,也就是在 MYSQL_BIN_LOG::ordered_commit() 函数中,开始 3 个阶段的流程。

接下来,看看 MySQL 中事务是如何提交的。


2阶段提交

以上提到单个事务的二阶段提交过程,能够保证 InnoDB 和 binlog 保持一致,但是在并发的情况下怎么保证存储引擎和 binlog 提交的顺序一致?当并发提交的时,如果两者不一致会造成什么影响?

  • 组提交异常:
    并发提交异常

如上所示,事务按照 T1、T2、T3 顺序开始执行,并依相同次序按照写入 binlog 日志文件系统缓存,调用 fsync() 进行一次组提交,将日志文件永久写入磁盘。

但是存储引擎提交的顺序为 T2、T3、T1,当 T2、T3 提交事务之后做了一个 On-line 的备份程序新建一个 slave 来做复制;而搭建备库时,CHANGE MASTER TO 的日志偏移量在 T3 事务之后。

那么事务 T1 在备机恢复 MySQL 数据库时,发现 T1 未在存储引擎内提交,那么在恢复时,T1 事务就会被回滚,此时就会导致主备数据不一致。

  • 结论:上图的并发提交的时候,导致了prepare的顺序和commit的顺序不同,事务执行的顺序也就不同了,导致了数据错误,需要保证 binlog 的写入顺序和 InnoDB 事务提交顺序一致,用于 xtrabackup 备份恢复。

  • 解决方案:接下来,看看如何保证 binlog 写入顺序和存储引擎提交顺序是一致的,并且能够进行 binlog 的组提交?5.6 引入了组提交,并将提交过程分成 Flush stage、Sync stage、Commit stage 三个阶段。这样,事务提交时分为了如下的阶段:

  1. InnoDB, Prepare:SQL已经成功执行并生成了相应的redo和undo内存日志;
  2. Binlog, Flush Stage:所有已经注册线程都将写入binlog缓存;
  3. Binlog, Sync Stage:binlog缓存将sync到磁盘,sync_binlog=1时该队列中所有事务的binlog将永久写入磁盘;
  4. InnoDB, Commit stage:leader根据顺序调用存储引擎提交事务;

每个 Stage 阶段都有各自的队列,从而使每个会话的事务进行排队,提高并发性能。

如果当一个线程注册到一个空队列时,该线程就做为该队列的 leader,后注册到该队列的线程均为 follower,后续的操作,都由 leader 控制队列中 follower 行为。

leader 同时会带领当前队列的所有 follower 到下一个 stage 去执行,当遇到下一个 stage 为非空队列时,leader 会变成 follower 注册到此队列中;注意:follower 线程绝不可能变成 leader 。

也就是利用队列的有序行去解决顺序不一致的问题,并且利用三个队列提高并发性,如果是一个队列的话会比较难处理,每一个队列各司其职,责任清晰

MySQL并行复制?

答:

  • 组提交(Group Commit):是logic clock并行复制的基础。Group Commit将所有的事务进行了分组,并为每个事务分配了last_committed和sequence_number。last_committed表示数据库中上一个事务的提交编号,同一组事务的last_committed的值相同;sequence_number是顺序增长的,每个事务对应一个序列号。

  • MySQL5.6实现:MySQL 5.6版本开启并行复制功能,那么SQL线程就变为了coordinator线程,coordinator线程主要负责以前两部分的内容:1、若判断可以并行执行,那么选择worker线程执行事务的二进制日志 2、若判断不可以并行执行,如该操作是DDL,亦或者是事务跨schema操作,则等待所有的worker线程执行完成之后,再执行当前的日志。

上述机制实现了基于schema的并行复制存在问题:并行复制效果并不高,如果用户实例仅有一个库,那么就无法实现并行回放,甚至性能会比原来的单线程更差。而单库多表是比多库多表更为常见的一种情形 。

  • MySQL5.7实现:MySQL 5.7才可称为真正的并行复制,这其中最为主要的原因就是slave服务器的回放与主机是一致的即master服务器上是怎么并行执行的slave上就怎样进行并行回放。不再有库的并行复制限制,对于二进制日志格式也无特殊的要求(基于库的并行复制也没有要求)

binlog 中记录了 sequence_number 和 last_commited,如上图,mysqlbinlog 解析日志可以看到这两个值。

sequence_number 是自增事务 ID,last_commited 代表上一个提交的事务 ID。

如果两个事务的 last_commited 相同,说明这两个事务是在同一个 Group 内提交的。

很显然的,主库能同时进入prepare阶段的事务之间不会冲突,那么这些事务在备库回放时也不会冲突。因为如果冲突了,先到的事务会获取锁,直到事务提交,这样后面的事务也没办法执行,所以同时进入prepare的就一定是不会冲突的事务。

MySQL 5.7并行复制的思想简单易懂,一言以蔽之:** 一个组提交的事务都是可以并行回放 ,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。**

索引除了提升效率还有啥优点?

答:

  1. 可以大大加快 数据的检索速度,这也是创建索引的最主要的原因。
  2. 索引可以帮助避免一些排序和临时表的产生,因为索引本身就是有序的
  3. 索引可以将随机IO变为顺序IO,因为我们运用索引的时候,可以通过B+树的叶子节点去访问下一个隔壁的叶子节点,如果我们没有索引,可能要在这个不同的地方去访问数据,顺序IO的检索速度更快。

为什么不建议使用过长的字段建立索引?

答:知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。

B+树和B树的区别,为什么MySQL使用B+树?

答:
B 树& B+树两者有何异同呢?

B 树的所有节点既存放键(key) 也存放 数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
B树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
B树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。

优缺点:

  1. B 树检索效率不稳定,可能没到叶子节点就结束了,而B+树只有到叶子节点才有数据,查询稳定
  2. B+树有一条引用连向相邻节点,更容易支持范围搜索,而B+树可能要不断的递归搜索
  3. B+树只在叶子节点存储数据,那么这样可以在增加在索引层的数量,减少B+树的层数,使查询更快

快照读在提交读和可重复读级别下有什么区别?

答:在读提交(RC),可重复读(RR)两个不同的事务的隔离级别下,快照读有什么不同呢?RC下,快照读总是能读到最新的行数据快照,当然,必须是已提交事务写入的。RR下,某个事务首次read记录的时间为T,未来不会读取到T时间之后已提交事务写入的记录,以保证连续相同的read读到相同的结果集。所以RC存在着幻读和不可重复读,而RP下全都解决了。

MyISAM 和 InnoDB 的区别?

答:
1. 是否支持行级锁

MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。

也就说,MyISAM 一锁就是锁住了整张表,这在并发写的情况下是多么滴憨憨啊!这也是为什么 InnoDB 在并发写的时候,性能更牛皮了!

2. 是否支持事务
MyISAM 不提供事务支持。

InnoDB 提供事务支持,具有提交(commit)和回滚(rollback)事务的能力

3. 是否支持外键

MyISAM 不支持,而 InnoDB 支持。

4. 是否支持数据库异常崩溃后的安全恢复

MyISAM 不支持,而 InnoDB 支持。

使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 redo log 。

拓展:

MySQL InnoDB 引擎使用 redo log(重做日志) 保证事务的持久性,使用 undo log(回滚日志) 来保证事务的原子性。
MySQL InnoDB 引擎通过 锁机制、MVCC 等手段来保证事务的隔离性( 默认支持的隔离级别是 REPEATABLE-READ )。
保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。

5. 是否支持 MVCC

MyISAM 不支持,而 InnoDB 支持。

讲真,这个对比有点废话,毕竟 MyISAM 连行级锁都不支持。

MVCC 可以看作是行级锁的一个升级,可以有效减少加锁操作,提供性能。

何为索引?有什么作用?

答:所谓索引就是用某种数据结构来存储我们的数据库数据,让我们查询更加快。

索引一定会回表吗?

答:不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么不需要进行回表查询。聚簇索引是不用回表的,因为相应的叶子节点就是对应的数据

简述事务的特性(ACID)?

答:
1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
2. 一致性:执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;(也就是脏读问题)
3. 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。

一条sql语句在mysql中如何执行的?

答:

概念分析
  • 连接器:身份认证和权限相关(登录 MySQL 的时候)。主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作。
  • 查询缓存:执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用,因为一般缓存我们会在其他层次去解决,另外查询语句一般是多变的,而且数据也是多变的)。
  • 分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。第一步,词法分析,一条 SQL 语句有多个字符串组成,首先要提取关键字,比如 select,提出查询的表,提出字段名,提出查询条件等等。做完这些操作后,就会进入第二步。第二步,语法分析,主要就是判断你输入的 sql 是否正确,是否符合 MySQL 的语法。
  • 优化器:按照 MySQL 认为最优的方案去执行。比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。
  • 执行器:执行语句,然后从存储引擎返回数据。
具体语句
  • 查询语句:权限校验(如果命中缓存)--->查询缓存--->分析器--->优化器--->权限校验--->执行器--->引擎
  • 更新语句:分析器---->权限校验---->执行器--->引擎---redo log(prepare 状态)--->binlog--->redo log(commit状态)

例子:update tb_student A set A.age='19' where A.name=' 张三 ';
其实这条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块是 binlog(归档日志) ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志),我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:

  1. 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
    2 .然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
  2. 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。
  3. 更新完成。

数据库三范式和BC范式?

答:

1 .第一范式(1NF):列不可再分

1.每一列属性都是不可再分的属性值,确保每一列的原子性
2.两列的属性相近或相似或一样,尽量合并属性一样的列,确保不产生冗余数据
例子:如果我们有班级这个字段,(大三1班),但是我们的需求中需要知道年级,和班级分别是什么,那么就得分开。
比如说我们的数据库需要存储用户发表的一篇文章数据,(我们只有这么一张表,用户信息和其他信息都存在这里的)
(用户ID,姓名,文章编号,文章内容,文章类型编号,文章类型名,所属学院,所属学院院长,文章收藏数)

  1. 产生的问题:

(1)数据冗余:一个学生选了多门课程
(111、吴某、0、xxx、123、算法、互金、xxx、1000)
(111、吴某、1、xxx、321、后端、互金、xxx、1000)
我们可以看出学院和院长都是多余的

(2)插入异常:
那么现在来一个转学生B,转学生初来乍到还没有发表文章,但是学生信息必须先录入那该怎么办呢?我们会发现我们遭遇了这样的窘状:
(111、吴某、null、null、后端、互金、xxx、1000)
当我们在录入文章编号和文章名的时候,完全无法填写,那么我们就无法插入新的记录。造成了插入异常。
(3)删除异常:
那么现在假设我们的B同学发表了一篇文章,然后不满意删除了:如果我们B同学恰恰只有了1篇文章,那么我们删除的这条信息,也就将他整个从数据库中删除。我们发现,刚刚老师在删除小B选的语文课记录时,这个关系模式中同样包含了小B的姓名学号等其他信息,当我们删除这条记录以后,可能会给我们带来其他数据的损失,这就是删除异常。
(4)更新异常:
如果我们有同学改个名,那么他所选的全部课程,也就都需要改名,这不合理,而且会导致资源消耗非常大。

2 .第二范式(2NF)属性完全依赖于主键
  1. 所以从函数依赖关系上,我们可以将原来的表分为
    (1). R0(用户ID,用户名,所属学院,所属学院院长)
    (2). R2 (文章类型编号,文章类型名)
    (3). R3(用户ID,文章编号,文章内容,文章收藏数)
  2. 存在的问题:同上,还是会导致数据冗余、插入异常、删除异常、更新异常。因为还是有重复列
3 .第三范式(3NF)属性不依赖于其它非主属性 ,属性直接依赖于主键

所以变成第三范式
(1)R0(用户ID,用户名,所属学院)
(2)R2 (文章类型编号,文章类型名)
(3)R3(用户ID,文章编号,文章内容,文章收藏数)
(4)R4(所属学院,所属学院院长)

4. BC范式:消除主属性对于码的依赖(3NF只是消除其他属性对非主属性的依赖)

仓库(仓库编号,仓库管理员编号,货物编号,仓库总人数,仓库货物总数)
其中每一个仓库管理员只管理一个仓库。
那么我们可以发现这里其实主码可以有两种,分别是:

  1. (仓库编号) 可唯一确定 (仓库管理员编号,仓库总人数、仓库货物总数)
  2. (仓库管理员编号) 可唯一确定 (仓库编号,仓库总人数、仓库货物总数)
    必须要承认上述关系是符合第三范式的吧,但是有没有觉得这样仓库管理员编号会出现大量的没必要的冗余啊,因此BC范式就是解决这个问题的,需要将其改为两个表,顺便可以将货物的数量加进来。

至于为什么上面关系中我不将货物数量加进来,是因为一旦加进来后那个关系就不符合第二范式了,想想看,如果加入货物数量,那么主键就变成了(仓库编号,货物编号),可是仓库管理员只与仓库编号有关,不依赖于货物编号了呀,就不构成对主键的完全依赖关系了。

下面放上BC范式的修改版:

  1. 仓库与管理员表(仓库编号,仓库管理员编号)
  2. 仓库货物表(仓库编号,货物编号,货物数量)

Mysql commit后是先写binlog还是redolog?

答:

  • 假设一:先写redo log再写binlog
    想象一下,如果数据库系统在写完一个事务的redo log时发生crash,而此时这个事务的binlog还没有持久化。在数据库恢复后,主库会根据redo log中去完成此事务的重做,主库中就有可这个事务的数据。但是,由于此事务并没有产生binlog,即使主库恢复后,关于此事务的数据修改也不会同步到从库上,这样就产生了主从不一致的错误。(假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。)

  • 假设二:先写binlog再写redo log
    想象一下,如果数据库系统在写完一个事务的binlog时发生crash,而此时这个事务的redo log还没有持久化,或者说此事务的redo log还没记录完(至少没有记录commit log)。在数据库恢复后,从库会根据主库中记录的binlog去回放此事务的数据修改。但是,由于此事务并没有产生完整提交的redo log,主库在恢复后会回滚该事务,这样也会产生主从不一致的错误。(如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。)

通过上面的假设和分析,我们可以看出,不管是先写redo log还是先写binlog,都有可能会产生主从不一致的错误,那么MySQL又是怎么做到binlog和redo log的一致性的呢?

在MySQL内部,在事务提交时利用两阶段提交(内部XA的两阶段提交)很好地解决了上面提到的binlog和redo log的一致性问题:

  1. 第一阶段: InnoDB Prepare阶段。此时SQL已经成功执行,并生成事务ID(xid)信息及redo和undo的内存日志。此阶段InnoDB会写事务的redo log,但要注意的是,此时redo log只是记录了事务的所有操作日志,并没有记录提交(commit)日志,因此事务此时的状态为Prepare。此阶段对binlog不会有任何操作。
  2. 第二阶段:commit 阶段,这个阶段又分成两个步骤。第一步写binlog(先调用write()将binlog内存日志数据写入文件系统缓存,再调用fsync()将binlog文件系统缓存日志数据永久写入磁盘);第二步完成事务的提交(commit),此时在redo log中记录此事务的提交日志(增加commit 标签)。

可以看出,此过程中是先写redo log再写binlog的。但需要注意的是,在第一阶段并没有记录完整的redo log(不包含事务的commit标签),而是在第二阶段记录完binlog后再写入redo log的commit 标签。还要注意的是,在这个过程中是以第二阶段中binlog的写入与否作为事务是否成功提交的标志。

两阶段事务图

redolog中的事务如果经历了二阶段提交中的prepare阶段,则会打上prepare标识,如果经历commit阶段,则会打上commit标识(此时redolog和binlog均已落盘)。

Step1. 按顺序扫描redolog,如果redolog中的事务既有prepare标识,又有commit标识,就直接提交(复制redolog disk中的数据页到磁盘数据页)

Step2 .如果redolog事务只有prepare标识,没有commit标识,则说明当前事务在commit阶段crash了,binlog中当前事务是否完整未可知,此时拿着redolog中当前事务的XID(redolog和binlog中事务落盘的标识),去查看binlog中是否存在此XID

a. 如果binlog中有当前事务的XID,则提交事务(复制redolog disk中的数据页到磁盘数据页)

b. 如果binlog中没有当前事务的XID,则回滚事务(使用undolog来删除redolog中的对应事务)

什么是数据库事务,MySQL 为什么会使用 InnoDB 作为默认选项?

答:事务可以认为是批量操作的总和,这批操作只能全部完成或者全部

a) 支持ACID,简单地说就是支持事务完整性、一致性;
b) 支持行锁,以及类似ORACLE的一致性读,多用户并发;
c) 独有的聚集索引主键设计方式,可大幅提升并发读写性能;
d) 支持外键;
e) 支持崩溃数据自修复;

mysql分页查询和limit、offset原理和优化?

答:

  1. 原理:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。

  2. 优化:

a):如果在我们的主键是自增的,我们可以通过一个记录前面第一页的最后一条记录的主键ID,然后在查询条件的where后面的第一个位置加上(防止索引失效)where id>这个我们缓存的ID,因为自增的原因,我们也可以断定后面的ID肯定是小于这个 ID,并且通过主键走了索引,大大减少无效查询,前面那些没有必要的数据都省去了,但是这里就需要我们每次去存储上一次分页的最后一条数据的ID(或者通过子查询,也就是下面的语句)

SELECT * FROM tableName
WHERE id >= (SELECT id FROM tableName ORDER BY id LIMIT 500000 , 1)
LIMIT 2;
因为子查询是在索引上完成的,而普通的查询时在数据文件上完成的,通常来说,索引文件要比数据文件小得多,所以操作起来也会更有效率。

实际可以利用类似策略模式的方式去处理分页,比如判断如果是一百页以内,就使用最基本的分页方式,大于一百页,则使用子查询的分页方式。

b):第二方案还是走索引,就是我们先根据条件查询出对应数据对应的ID,然后再根据ID去查询我们具体要的内容,因为我的这里查询的只是ID,而不是*,并且主键ID上有索引,所以这里并不会回表查询更具体的数据。就减少了回表查询的时间,然后我们再根据这些ID,作为条件去查出数据,这里主要优化的就是将回表查询的时间省去了,假如 现在是 limmit 300w 1 ,那么我们如果按照原来的方案,Mysql会将前300W的数据都查出来,并且会回表查询出更具体的内容,然后最后前300W数据都是丢弃的,只拿一条数据,这说明我们前面300W的回表查询都是无用功。而我们现在的方式是,先根据条件查询出对应的主键ID,这个过程也会进行查询300W数据,但是不会回表,因为ID就是索引列,然后返回1条的ID,然后我们就直接根据这个1条的ID去查询并回表,所以这里我们只回表了我们要的数据的那一次

SELECT * FROM tableName
WHERE id in (SELECT id FROM tableName ORDER BY id LIMIT 500000 , 1)
LIMIT 2;

mysql什么时候适合建索引,什么时候不适合?

答:

  1. 当某个列经常是全表扫描的
  2. 列的区分度不高,比如性别,只有男和女,这种区分度就不高,索引最多也就能帮你分出是男还是女
  3. 大部分时间都是适合简历索引的
  4. 经常Update, insert,delete的 表,因为索引的维护也是需要另外的空间的,如果你的表查询非常的少,那么建立索引其实相当于没建立

mysql中的三种日志类型?

答:binlogredo logundo log

日志详解

注意binlog没有crash safe的功能,因为binlog写入的时机跟redo log的时机不同,redo log是write ahead log

  1. binlog:redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。
  • 而 binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层。

  • 不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。

  • 那 binlog 到底是用来干嘛的?

  • 可以说MySQL数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。

  1. redo log:redo log(重做日志)是InnoDB存储引擎独有的,它让MySQL拥有了崩溃恢复能力。比如 MySQL 实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。
  • MySQL 中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。

  • 后续的查询都是先从 Buffer Pool 中找,没有命中再去硬盘加载,减少硬盘 IO 开销,提升性能。

  • 更新表数据的时候,也是如此,发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。

  • 然后会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。

  • 刷盘时机:InnoDB 存储引擎为 redo log 的刷盘策略提供了 innodb_flush_log_at_trx_commit 参数,它支持三种策略:0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作。1 :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值)。2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache

    刷盘图

  1. undo log
    我们知道如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用 回滚日志 中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。
  • 另外,MVCC 的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建 Read View 之前已经提交的修改和该事务本身做的修改
    三个日志参考链接

除了索引还有其他的吗(做了什么优化)查询这么快?底层的buffer机制?

答:

  1. 说到buffer机制,首先要说的就是底层数据库读取数据的方式,其实在行的基础上,还有一个页的概念,在底层Mysql读数据是按页读取的。每个数据页存放着多条的数据,MySQL在执行增删改首先会定位到这条数据所在数据页,然后会将数据所在的数据页加载到 Buffer Pool 中。这样也可以说是预读。磁盘读写,并不是按需读取,而是按页读取,一次至少读一页数据(一般是4K),如果未来要读取的数据就在页中,就能够省去后续的磁盘IO,提高效率(并且一般你读那条数据的左右两边的数据也很可能会被读)。
  2. 然后是管理缓存页的方式,也就是说如何去淘汰和移动缓存中的数据,这里MySQL使用的是LRU,但是传统LRU有两个问题:
  3. 预读失效:由于预读 (Read-Ahead),提前把页放入了缓冲池,但最终 MySQL 并没有从页中读取数据,称为预读失效。
    解决思路:(1)让预读失败的页,停留在缓冲池 LRU 里的时间尽可能短;(2)让真正被读取的页,才挪到缓冲池 LRU 的头部;
    解决方案:(1)将LRU将节点分为了新生代 (new sublist),老生代 (old sublist)。(2)新老生代收尾相连,即:新生代的尾 (tail) 连接着老生代的头 (head);(3)新页(例如被预读的页)加入缓冲池时,只加入到老生代头部:如果数据真正被读取(预读成功),才会加入到新生代的头部。如果数据没有被读取,则会比新生代里的 “热数据页” 更早被淘汰出缓冲池
    新生代与老年代
  1. 缓冲池污染:当某一个 SQL 语句,要批量扫描大量数据时,可能导致把缓冲池的所有页都替换出去,导致大量热数据被换出,MySQL 性能急剧下降,这种情况叫缓冲池污染。也就是说有些数据虽然预读成功了,但是他只读取一次,就会导致原来那些一直被读取的数据失效了
    解决思路:即使是预读成功也不立即加入到头部。而是在访问T次后才放到头部
    解决方案:1)假设 T = 老生代停留时间窗口;(2)插入老生代头部的页,即使立刻被访问,并不会立刻放入新生代头部;(3)只有满足 “被访问” 并且 “在老生代停留时间” 大于 T,才会被放入新生代头部;

Buffer机制底层详解

简述数据库中什么情况下进行分库,什么情况下进行分表?

答:

  1. 水平分库:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。

结果:每个库的结构都一样;每个库的数据都不一样,没有交集;所有库的并集是全量数据;

场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。分析:库多了,io和cpu的压力自然可以成倍缓解。

  1. 水平分表:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。

结果:每个表的结构都一样;每个表的数据都不一样,没有交集;所有表的并集是全量数据;

场景:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。分析:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。

  1. 垂直分库:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。

结果:每个库的结构都不一样;每个库的数据也不一样,没有交集;所有库的并集是全量数据;

场景:系统绝对并发量上来了,并且可以抽象出单独的业务模块。

分析:到这一步,基本上就可以服务化了。例如,随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化

  1. 垂直分表:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。

结果:每个表的结构都不一样;每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据;所有表的并集是全量数据;
场景:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈。
分析:可以用列表页和详情页来帮助理解。垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,非热点数据放在一起作为扩展表。这样更多的热点数据就能被缓存下来,进而减少了随机读IO。拆了之后,要想获得全部数据就需要关联两个表来取数据。但记住,千万别用join,因为join不仅会增加CPU负担并且会将两个表耦合在一起(必须在一个数据库实例上)。关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据。

简述一致性哈希算法的实现方式及原理?

一致性hash

联合索引的存储结构是什么?

答:


例图

最左前缀匹配原则也是如此,因为你联合索引是按索引列的顺序排的,只有第一个是全局有序的,而第二个只有在第一个值相同的情况下才会产生局部有序

简述 MySQL 的主从同步机制,如果同步失败会怎么样?

答:1. 主从数据会不一致,这样一般是从库的进度要比主库的慢。

解决:

  • 方式一
    stop slave;

表示跳过一步错误,后面的数字可变

set global sql_slave_skip_counter =1;

start slave;

之后再用mysql> show slave status\G 查看:

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

select for update是表锁还是行锁?

当使用select ... for update ...where ...时,mysql进行row lock还是table lock只取决于是否能使用索引(例如主键,unique字段),能则为行锁,否则为表锁;未查到数据则无锁。而 使用'<>','like'等操作时,索引会失效,自然进行的是table lock

1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。

2、由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

3、当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。

4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。

5、检索值的数据类型与索引字段不同,虽然MySQL能够进行数据类型转换,但却不会使用索引,从而导致InnoDB使用表锁。通过用explain检查两条SQL的执行计划,我们可以清楚地看到了这一点。

如果我想要强制走某个索引,能实现吗?

mysql强制索引和禁止某个索引

1、mysql强制使用索引:force index(索引名或者主键PRI)

例如:

select * from table force index(PRI) limit 2;(强制使用主键)

select * from table force index(ziduan1_index) limit 2;(强制使用索引"ziduan1_index")

select * from table force index(PRI,ziduan1_index) limit 2;(强制使用索引"PRI和ziduan1_index")

2、mysql禁止某个索引:ignore index(索引名或者主键PRI)

例如:

select * from table ignore index(PRI) limit 2;(禁止使用主键)

select * from table ignore index(ziduan1_index) limit 2;(禁止使用索引"ziduan1_index")

select * from table ignore index(PRI,ziduan1_index) limit 2;(禁止使用索引"PRI,ziduan1_index")

mysql的隐式转换是否走索引?

答:

CREATE TABLE `user_message` (
  `user_id` varchar(50) NOT NULL COMMENT '用户ID',
  `msg_id` int(11) NOT NULL COMMENT '消息ID',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


EXPLAIN SELECT COUNT(*) FROM user_message WHERE user_id = 1;

上述结构会导致索引失效

  • 隐式转换是什么?
    当算子两边的操作数类型不一致时,MySQL会发生类型转换以使操作数兼容,这些转换是隐式发生的。下面描述了比较操作的隐式转换:
    1.如果一个或两个参数均为NULL,则比较结果为NULL;但是 <=> 相等比较运算符除外,对于NULL <=> NULL,结果为true,无需转换。
  1. 如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较。
  2. 如果两个参数都是整数,则将它们作为整数进行比较。
  3. 如果十六进制不是和数字作比较,它会被视作是二进制字符串。
  4. 如果参数之一是TIMESTAMP或DATETIME列,而另一个参数是常量,则在执行比较之前,该常量将转换为时间戳,但对于IN() 内的参数不执行此操作。为了安全起见,在进行比较时,请始终使用完整的时间、日期或时间字符串。例如,要在日期和时间参数上使用 BETWEEN 函数时,最好使用 CAST() 函数把参数显示转换成所需的数据类型。
  5. 一个或多个表中的单行子查询不视为常量。例如,如果子查询返回的整数要与DATETIME值进行比较,则比较将作为两个整数完成,子查询返回的整数不转换为时间值。参见上一条,这种情况下请使用CAST()将子查询的结果整数值转换为DATETIME。
  6. 如果参数之一是十进制值,则比较取决于另一个参数。如果另一个参数是十进制或整数值,则将参数作为十进制值进行比较;如果另一个参数是浮点值,则将参数作为浮点值进行比较。
  7. 在所有其他情况下,将参数作为浮点数(实数)进行比较。例如,将字符串和数字操作数进行比较,将其作为浮点数的比较。
  • 按理说,两边都是浮点数,那么应该能使用索引,为什么执行时没有使用到索引?

MySQL在执行我们的查询SQL时,会 CAST 函数把每一行主键列的值转换成浮点数,然后再与条件参数做比较。而 InnoDB 存储引擎中,在索引列上使用函数会导致索引失效,所以最后导致了全表扫描。
我们只需要把 SQL 中 WHERE 条件改成字符串,就可以使用到主键索引了:

MySQLWAL技术?

答:
上文提到MySQL在执行事务操作时,会先写redo log,redo log是记录数据修改时的物理操作,写入redo log之后需要等数据真的执行了物理操作之后再执行下一步操作吗?显然不是这样。redo log其实只是记录如何操作物理数据的日志,MySQL通过写redo log避免直接写磁盘,大大提高了写入速度,这个技术就是Write-Ahead Logging。

最后小结一下,大家可能会有疑惑WAL技术虽然不需要把更新的数据实时持久化,但是也需要写日志,而日志本身也是持久化的,写磁盘的次数似乎总体上并没有减少,甚至可能增加,这种想法本身是正确的,但是WAL技术之所以能提高数据更新的效率,主要原因在于写日志是一个顺序读写的过程,而要更新的数据页本身是随机的;另一个原因是MySQL对于持久化redo log和binlog也做了优化,使用了组提交减少fsync的次数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,042评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,996评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,674评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,340评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,404评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,749评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,902评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,662评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,110评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,577评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,258评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,848评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,726评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,952评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,271评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,452评论 2 348

推荐阅读更多精彩内容