1、一条SQL查询语句是如何执行的
连接器:连接器负责与客户端建立连接,获取权限,维持和管理链接等等。
查询缓存:连接器在建立连接之后,会查询缓存,如果这条 SQL 语句在之前执行过,在缓存中就会存在这条语句的 key-value 形式,从而省区查询步骤,直接返回缓存就可以。在MYSQL8.0以后就删除了这个缓存功能。
解析器:对 SQL 语句进行词法分析和语法分析,词法分析主要分析关键字、表名、字段名。语法分析主要是用来分析这个 SQL 语句是否符合语法规则,不符合就会报错。
优化器:在表结构信息的基础上,根据表的连接方式、选择哪种索引等,选择一条最优的方案来决定如何执行这条语句,生成一个执行计划。
执行器:根据这个执行计划,调用存储引擎的 handler api,逐步获取数据,完成执行计划。
2、事务的四大特性有哪些
事务的四大特性主要有ACID四大特性。
原子性:每个事务的执行必须具有原子性,事务在执行操作要么全部成功,要么全部失败,不存在部分成功的情况。
一致性:每个事务在执行前后,数据库必须从一个一致性状态转换到另一个一致性状态。一致性是最终的目的,AID都是为C做准备。
隔离性:两个事务在执行期间是相互隔离的,不能互相影响。
持久性:一个事务被提交之后,他对数据库的更改是永久的,不会因为系统的故障和崩溃而影响。
3、数据库的事务隔离级别有哪些
读未提交:一个事务读到了其他事务还没有提交的数据。这是最低的隔离级别,可能存在脏读、不可重复读和幻读。脏读就是一个事务读到了另一个事务还没有提交的数据,如果那个事务回滚了,那么当前事务读到的就是不存在的数据,就是脏读。
读已提交:解决了脏读的问题,读已提交能读到别的事务提交之后的数据,其他事务的修改在事务提交之后才可见。解决了脏读, 但是可能出现不可重复读和幻读的问题。不可重复读就是事务在执行期间,多次读到的同一数据前后不一致的问题。
可重复读:可重复读可以保证事务在开始和结束期间读到的同一数据保持一致,就是说别的事务对这个数据的更改对该事务是不可见的。但是存在幻读的问题。幻读就是同一事务在前后两次进行同一范围查询,第二次却多出了第一次没有的数据,就好像是产生了幻觉一样。
串行化:串行化保证了事务之间的并发执行和串行的效果是一样的,不存在脏读、不可重复读、幻读的问题。
4、MySQL的执行引擎有哪些
InnoDB 存储引擎是当前 MYSQL 数据库采用的存储引擎,支持事务,也支持行级锁和外键的功能,适合于使用事务和高并发的场景。
MyISAM 存储引擎是早期的存储引擎,不支持事务、外键和行级锁,使用的是表级锁,主要适用于数据量比较少,并发量不高的场景。
Memory 就是将数据放到内存中,服务器重启之后就消失了。
5、MySQL为什么使用B+树来作索引
单点查询:B 树在查询的时候的时间复杂度是 log(n),相比于 B+ 树平均时间复杂度要快一点,B 树的每个间点既存储索引又存储数据,导致 B 树的高度不一,查询的时候产生的波动较大。B+ 树采用非叶子节点只存储索引,叶子节点存储数据的方式,导致 B+ 树要比 B 树更加矮胖一点,平均访问磁盘数更少,平均查找时间相对更短。
增加和修改:B+ 树在删除节点的时候,只会删除叶子节点,不会删除非叶子节点,通常比较快速。在添加的时候可能会引起节点的分裂,但是也只是会影响一条路上的节点。B 树在删除或新增节点的时候可能会导致树发生形变。
全部查询:B+ 树中所有的叶子节点用一个链表穿起来,可以支持O(n)的时间复杂度进行全部访问,但是B 树只能遍历整个树,涉及到的磁盘操作更多,效率不如 B+ 树。
6、说一下索引失效的场景
最左前缀原则:当一个索引是联合索引,按顺序构建,但是查询的时候跳过了最左列或中间列,这会导致后面的列的查找不能用到索引。
使用 LIKE:以通配符 % 开头的 LIKE 查询通常会索引失效。
使用 or 条件:当使用 or 连接多个条件的时候,有一个条件没有索引可能就会引起索引失效,就算是都有索引在数据量比较大的时候也可能会发生索引失效。
使用函数或表达式:在列上使用函数或者表达式的时候,可能会引起索引失效。
对列使用类型转换:在对列使用类型转换的情况下索引会失效,但是对常量的类型转化不会失效。
7、什么是慢查询,原因是什么,可以怎么优化
慢查询是指对数据库的操作超过了 long_query_time 这个时间阈值,通常为10s,生产环境中可以自己设置。超过这个时间会被记录到慢查询日志。
查询的数据量过大,会导致查询操作变慢。
查询语句比较复杂,比如涉及到多个表的连接和子查询,可能导致查询变慢。
索引失效的情况下,查询会退化成全表扫描,导致查询变慢。
硬件资源不足,比如数据库服务器上同时运行了多个查询语句,这些语句之间竞争 cpu、内存等资源,导致服务器负载过高,查询变慢。
确保开启了慢查询日志,运行语句,找到查询慢的 SQL 语句。
对目标语句执行 EXPLAIN,主要关注 type :为all 表示全表扫描,通常为ref,或者range的时候更好;key :表示用的索引名称,为null 就说明没走索引,性能较差;rows :预估扫描行数,如果过大的话需要注意;extra :优先关注 ueing-index,就是很好。using-filesort 就需要警惕。
可以自己创建索引,减少回表查询。
减少返回的字段,尽量不要写select *,需要什么就返回什么就可以了。
8、undo log、redo log、binlog 有什么用

undo log 主要是记录逻辑日志 ,他记录的是一次操作的反向操作,用来实现事务的回滚和MVCC。主要用来实现事务的原子性。

redo log 主要记录的是事务执行过程中 的物理日志 ,他记录的是一次操作对哪里,如何进行的修改,用来解决内存中的脏页刷新到磁盘的时候发生错误的时候进行数据恢复,主要用来实现事务的持久性。
binlog 是 server 层的日志,主要用来主从复制 和数据恢复。
9、MySQL和Redis的区别是什么
redis 是非关系型数据库,主要存储键值对,支持多种数据结构;mysql 是关系型数据库,使用表结构来存储数据。
redis 数据存储在内存,通过持久化机制写入磁盘;mysql 是直接存储到磁盘上。
redis 主要使用自己的命令集来操作,mysql 通过 SQL 语言操作数据库。
redis 主要以高性能低延迟为目标,适合用作查询多操作较少的场景,适合用作缓存;mysql 存储在磁盘上,适合存储大规模的数据,支持复杂查询和事务处理。在实际应用中通常一起使用。
10、Redis有什么优缺点,为什么用Redis查询会比较快
redis 是基于内存的数据库,读写的速度非常快,常被用作缓存、消息队列和分布式锁。支持多种数据结构,比如字符串、哈希表、列表和集合等数据结构。redis 有分布式的特性,可以把数据放到多个节点上,提高了可扩展性和可靠性。redis 受限于内存的大小,不适合存储较大的数据。因为内存空间有限。
redis 基于内存操作,减少了磁盘IO的时间,提高了操作速度。redis 提供了多种数据结构,比如string、hash、list、set,多种数据结构提高了读写效率。redis的读写操作主要是单线程,减少了上下文切换带来的 cpu 的开销,也没有进程阻塞的问题,避免了死锁现象的发生。支持 I/O 多路复用
11、Redis的数据类型有哪些
Redis是以键值对来进行存储的,他的key 是字符串类型,他的vaue支持多种类型,包括常见的物种基本数据类型有:String、Hash、List、Set、ZSet。
字符串 String:存储字符串数据
哈希表 Hash:用来存储键值对
列表 List:存储字符串元素列表
集合 Set:用来存储唯一的字符串元素
有序集合 ZSet:存储的唯一的字符串,每个字符串对应一个分数,按照分数进行排序,添加的时候手动输入每个字符串对应的分数。
12、Redis是单线程的还是多线程的,为什么
Redis 的核心命令的执行是单线程的,我们通常说的单线程指的是从接受客户端请求、解析命令、执行读写到返回结果,这一套核心流程都是由一个线程完成的。
但是 Redos6.0 版本之后的网络 I/O 部分引入了多线程机制。网络数据的读取、协议解析以及结果的返回,可以交给多个 I/O 线程并行处理,但是实际执行命令比如 GET/SET 操作还是由主线程单线程串行执行,以此保证 Lua 脚本、事务执行的原子性。单线程模型简化了并发控制,省去了线程同步的操作,也不用线程之间切换和锁竞争的开销。确保了操作的原子性。单线程确保请求的顺序执行。
严格来说,Redis 进程从来都不只有一个线程,还有后台线程 BIO 负责 AOF 刷盘、关闭文件等操作,避免阻塞主线程。
13、说一说Redis持久化机制有哪些、
Redis 的持久化机制主要有 RDB 和 AOF。
RDB 是把内存中某一时刻 的状态以二进制的格式写入到磁盘文件中,方便后续的数据恢复。可以自动触发和手动触发。
先说手动触发,使用 save,但是这种方法会阻塞 redis,生产中一般不使用,一般使用 bgsave。这种方法只有在 fork 子进程的时候主进程是阻塞的。
再说自动触发,通过配置 ,比如 save 900 1,就代表在 900 秒内有一个 key 发生变化就会执行一个 RDB 操作,默认使用 bgsave 方式进行备份,备份出来的文件叫做 dump.rdb,是一个二进制文件。
写入的方法:bgsave方法。这种方法会在主进程中 fork 一个子进程,这个进程专门用来拷贝内存中的一个个键值对,以二进制的方式存储到磁盘中,在这个过程中,redis除了在 fork 阶段是阻塞的之后,都不阻塞,如果期间主进程又修改了数据,主进程会把要修改的数据复制一份生成副本,然后子进程把这个副本再写入 RDB 文件中(这个副本中不含这次修改的数据)。
AOF 主要是通过对执行过的操作 进行保存,Redis 重启时通过重放这些命令来恢复数据。每隔一段时间,就会用 AOF 重写机制,会删掉被覆盖的命令、删掉重复命令。可以减小 AOF 文件的体积,从而减少磁盘的占用空间。重写机制可以手动触发也可以自动触发。
手动触发就是执行一条命令,也是主进程 fork 子进程之后,完成重写。
被动触发就是指当 AOF 文件大小超过限制之后,就会自动触发。
重写的方法:主进程通常也会 fork 出一个子进程,让子进程来重写这个 AOF 文件,在这期间,如果主进程又修改了数据,于是新增了一个 aof 重写缓冲区,创建子进程之后,后续的修改就在这里面,当子进程重写之后,再把这个重写缓冲区中的命令写到新的 aof 文件中,然后把这个新的 aof 文件替换掉原来的旧的 aof 文件。
14、介绍一下Redis缓存雪崩和缓存穿透和缓存击穿
缓存穿透是指请求的数据既不在 redis 缓存中,又不在数据库中,这个数据没办法被缓存,导致数据库的压力比较大,增加数据库的负载,可以通过缓存空值来解决缓存穿透。
缓存雪崩是指大量的 key 同时失效,导致大量的请求同时到达数据库,数据库的负担加重,可以通过给不同的 key 设置不同的 TTL 过期时间,解决缓存雪崩。
缓存击穿是指某个热点 key 在一段时间内缓存过期了,又有大量的请求到达数据库,导致数据库的负担加重,可以通过使用互斥锁 和逻辑过期 的方法来解决。
互斥锁的方法是每个线程访问缓存的时候需要加锁,发现缓存失效之后,去请求数据库,然后写入缓存。期间其他线程在请求锁的时候就会阻塞等待,直到获取到缓存中的值。
逻辑过期的方法是不给 key 设置过期时间,在 value 中存一个逻辑过期时间,访问的时候发现逻辑过期了,就去获取锁然后更改缓存,期间其他的线程获取不到锁,就知道有线程在更新缓存,于是返回已经逻辑过期的旧值。
15、如何保证数据库和缓存的一致性
在每次操作缓存的时候,是删除缓存而不是重写缓存,因为每次操作数据库都更新缓存可能涉及到很多重复且无用的操作,所以采取直接删除缓存,下次访问数据库的时候再更新缓存。
先操作数据库,再删除缓存。原因:在一个场景下,缓存和数据库中的数据都是10。我们假设如果是先删除缓存再操作数据库,一个线程要对数据进行修改,先删除缓存,就在删除之后,另一个线程开始查询操作,先查询缓存发现没有,转去查询数据库,查询到了值为10,写回缓存,缓存为10。线程结束,然后原先的线程继续修改数据库的值为20。这是就发生了缓存和数据库不一致的情况。但是先操作数据库再是删除缓存的情况也会触发缓存和数据库不一致,但是这种情况发生的机率很小。比如一个线程查询缓存恰好失效,然后查询数据库为10,另一个线程修改数据库为20,然后删除缓存,执行完成后,原先的线程写入缓存10,发生了缓存和数据库不一直的情况。