面试题-MySQL和Redis(更新版)
前言
MySQL和Redis部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,并添加了一些比较重要的问题,希望对大家起到一定的帮助。
系列文章:
MySQL
解释⼀下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接
池?池化思想其实在 面试题-线程池和原子变量 中 为什么要使用线程池中解释过,具体优点如下:
- 提高了连接的可管理性
- 降低频繁创建和销毁资源的开销
- 提前创建好资源,提高了任务的响应速度
范式你了解吗?简单说说
第一范式:每个属性都不可再分,如果仅仅支持第一范式,那么会出现一些问题
数据冗余
插入/删除/修改 异常
能具体说说这几个异常是什么意思吗?
如果有一张学生成绩表,包含了学生的所有成绩,但是其中有所在系的字段,那么针对同一个学生的多门课程成绩,系的字段就会冗余,这个就是数据冗余异常;因为系相关的信息没有自己的表,所以如果这个系没有学生,就无法插入系的信息,这个是插入异常;删除异常同理,如果删除学生信息,那么系的信息也会随之删除;修改异常也同理,当修改学生的系信息时,就会出现修改多次的现象。
第二范式:消除了 非主属性 对于 码的 部分函数依赖,简单说就是非主键的所有列,必须全部依赖于完整的主键,而不能只依赖于主键的一部分或者不依赖。
举个例子:如果有一个订单明细表,订单明细表中包含,orderId,productId,折扣,数量,商品单价和商品名称,其中主键是OrderID+ProductID,折扣和数量是完全依赖主键的,但是商品单价和商品名称是只依赖于商品id,这个设计就不符合第二范式的需求。需要拆分为一个商品表保存商品单价和商品名称的字段。
第三范式:消除了非主属性 对于 码的 传递函数依赖,简单说就是非主键的所有列,必须直接依赖于主键,不能依赖于非主键列,然后传递依赖主键列。
举例来说:有一个课程表,课程id,教师名,教师地址,主键是课程id,一个课程id都可以完全确定一个教师名和教师地址,符合第二范式,但是教师地址依赖于非主键列教师名,存在传递依赖,所以不符合第三范式。
redo log的底层实现你知道吗?简单说说
redolog实现是一个环,大小是固定的。redolog的文件个数和大小也是可以配置的。redolog中有两个指针,如果write指针追上了checkpoint指针,就需要等待刷新脏页,然后才能写入。
- write:代表了写入redolog的位置
- checkpoint:代表了刷新到磁盘的位置
redo log和bin log的区别?
- 是否共有:redo log是innodb特有的;binlog是server层的,属于共有的
- 是否追加写:redo log是一个环,是循环写;binlog是追加的覆盖写
- 作用不同:redo log用于数据库异常宕机的恢复工作;binlog用于备份
简单说说事务的ACID特性
- A:原子性,关注单个事务中的所有操作要么都成功要么都失败
- C:一致性,关注事务开始和结束之后数据库的完整性约束没有改变
- I:隔离性,关注多个事务的之间是否互相影响
- D: 持久性 ,关注事务结束后,对数据库的修改是否可以永久的保存到数据库中
如果没有隔离性,会发生什么问题?
如果没有隔离性,会出现下面三个问题
- 脏读:事务读取到其他事务未提交的数据
- 不可重复读:事务中对同一数据的查询结果不同
- 幻读:事务中同一个sql查询到的数据行数不同
事务的隔离级别有哪些?分别可以解决什么问题?
- 读不可提交:有脏读/不可重复读和幻读的问题
- 读可提交:解决了脏读的问题
- 可重复读:解决了脏读和不可重复读的问题
- 串行:解决了三个问题,但性能最差
隔离性是如何实现的?你知道MVCC吗?
隔离性的实现是数据库创建了一个视图,访问的时候以这个视图的逻辑结果为准
- 读不可提交,不创建视图
- 读可提交:每一条SQL都创建一个视图
- 可重复读:在事务开始的时候创建一个视图,事务中的所有sql都以这个视图为准
- 串行:用锁实现的
MVCC是多版本并发访问,通过回滚日志实现的,每一条更新操作都对应一条回滚日志,当前的值,都可以通过回滚日志找到历史更新过的值,多事务之间可以访问到不同的数据就是使用MVCC实现的。
你知道全局锁吗?
全局锁会锁住整个数据库实例,只能读,任何关于更新的操作都会阻塞。需要了解全局锁对业务的影响:
- 如果锁主库,那么业务基本停摆
- 如果锁从库,那么锁定期间,从库无法同步主库的更新,可能会导致主从不一致
简单说说表锁?
表锁是锁住整张表,以一个sql为例,lock tables t1 read,t2 write;这句sql执行完毕后:
- t1表只能读
- t2表执行者可以读写,其他人不可读写
你知道元数据锁吗?
元数据锁主要控制并发的DDL和DML操作,DML操作获取的是元数据读锁;DDL操作获取的是写锁,读写互斥,读读不互斥。
你知道共享锁排他锁和意向锁吗?
InnoDB存储引擎中支持行锁,行锁分为三种锁
- 共享锁:可以理解为读锁
- 排他锁:可以理解为写锁
- 意向锁:意向锁的出现主要是为了提高加表锁的判断效率,如果没有意向锁,线程需要加表锁时需要对每一行进行判断,是否有行级锁的存在,加了意向锁相当于多一个变量保存状态,这样可以O(1)的效率判断是否可以加表锁。
行锁的三种锁定范围(一致性锁定读时使用)
- 只锁定单行:查询条件中有索引,并且是唯一索引时,可以只锁定单行
- 锁范围,不包括记录本身
- next-key lock:单行和范围锁的合体:查询条件中有索引,并且不是唯一索引时使用
什么是索引?索引有什么作用?
索引是一种数据结构,可以方便我们快速的查找数据,提高查找效率。
Hash索引和B+树索引的区别?
- Hash索引适合等值查询,不适合范围查询
- Hash索引没办法利用索引完成排序
- 如果有大量重复键值对,Hash索引的效率会变低,因为需要解决hash碰撞的问题,解决hash碰撞一般采用拉链法
- 而 B+树索引是一种查询树,天然有序,所以可以利用它做范围查询和排序,并且因为B+树的层数少,IO次数也少
聚集索引和非聚集索引的区别?
- 聚集索引叶子节点存储的是整行数据;非聚集索引存储的是主键的值
- 如果使用非聚集索引时,待查询的列索引无法覆盖,那么就需要回表查询
联合索引和最左匹配原则是什么意思?
- Mysql中可以针对多列创建联合索引
- 最左匹配原则指的是,mysql使用索引时,会从最左边的一列开始匹配,建立一个(k1,k2,k3)的联合索引,相当于建立了(k1),(k1,k2)和(k1,k2,k3)三个索引
分库分表之后,id 主键如何处理?
分库分表之后,主键id就需要一个全局的id,一般来说生成主键id有以下几种方案:
- UUID:太长,无序不可读,不适合作为主键
- 利用redis生成id:性能较好,比较灵活,但是需要引入新的组件,增加了系统的复杂度
- 美团的Leaf:分布式唯一id生成器,也需要依赖关系型数据库和zookeeper等。
⼀条SQL语句在MySQL中如何执⾏的?
查询语句的执行流程如下:
- 连接器中校验是否有权限,如果没有权限,直接返回错误;mysql8.0之前的版本,会去查询缓存中检查是否有缓存,如果有直接返回
- 分析器进行词法分析和语法分析,词法分析主要关注关键字和相关的字段;语法分析主要检查sql语句是否有语法错误
- 优化器根据自己的算法确定执行计划,包括索引的选择等等。
- 执行器调用存储引擎接口,获取数据返回
更新语句的执行流程如下:
- 相关的校验等操作和查询操作是类似的,后面的更新操作不太一样
- 执行器调用存储引擎的更新接口后,存储引擎会更新数据,然后记录redo log,redo log此时为preopare状态
- 执行器写入binlog日志
- 执行器调用存储引擎的提交接口,存储引擎把redo log的状态更新为commit,此为两阶段提交,可以保证 异常宕机的重启恢复和二进制日志的备份的数据是一致的。
⼀条SQL语句执⾏得很慢的原因有哪些?
首先刨析这个问题,有两种情况:
- SQL语句平时执行的很快,只是偶尔变慢
- 在数据量一定的情况下,SQL语句的每次查询都很慢
针对第一种情况,SQL本身写的应该没什么问题,偶尔变慢的情况应该出在MySQL本身,有以下几种情况可能会导致查询变慢:
- 数据库在刷新脏页:redolog满了;内存不足需要淘汰一部分脏页
- 获取不到锁,阻塞了:如果某张表或者行已经被别的线程加锁,暂时获取不到锁
针对第二种情况,可能有以下的原因:
- 字段没正确建立索引;字段中包含表达式或者函数操作,导致有索引但是没有用上
- 数据库因为算法综合判断,可能不会选择索引,而选择全表扫描,算法中的影响因素主要有下面几个:
- 扫描行数
- 是否排序
- 是否回表
- 是否需要临时表
MyISAM引擎和innoDB引擎的区别?
- 是否支持事务
- 是否支持外键
- 表锁和行锁
- 是否支持MVCC
MySQL中如果有两个线程想要操作同一行数据,如何避免死锁?(重要)
核心有两点,知道mysql的锁定机制和防止死锁的理论知识,然后结合理论知识判断如何预防死锁即可:
防止死锁只要破坏死锁的四个条件之一就可以:
- 互斥:互斥本身无法破坏
- 请求并保持:一次性申请全部的锁即可,表现在sql语句中就是where条件中直接包括多个资源,直接锁定多个资源的数据。
- 不可剥夺:如果是应用层面,可以通过设置事务超时时间来实现事务超时释放锁并回滚事务,使用Transactional注解中的timeout即可实现。
- 循环等待条件:每个线程都按照同样的顺序申请资源,比如业务中会更新多条数据,更新操作都按照主键序号从小到大更新即可。
幻读在InnoDB中是如何被解决的?
innodb中的间隙锁就是为了解决幻读问题出现的。因为锁定了间隙,所以无法进行插入操作。这样自然就解决了可重复读隔离级别下的幻读问题。
InnoDB的MVCC如何解决脏读和不可重复读的问题?
- 一致性读只能读取事务开始时的快照版本(不加锁的select 解决了不可重复读的问题)
- 其他事务的读只能读到事务开始时,最新的已提交的版本快照(解决了脏读的问题)
- 当前读(for update和share mode)读的是当前行的最新版本并且会加行锁
- 一个事务内是可以读取到其所做的未提交的版本快照
Redis
为什么要用redis,不用map或者guava?
缓存分为本地缓存和分布式缓存,map和guava属于本地缓存,特点是:
- 轻量快速
- 生命周期随着JVM销毁而结束
- 不同实例中都需要各自持有一份本地缓存,缓存不具有一致性
分布式缓存,可以保证缓存的一致性,但是会增加系统架构的复杂度。
简单说说Redis的RDB持久化?
RDB持久化是指把当前进程数据生成快照保存到硬盘的过程。RDB的触发可以分为手动触发和自动触发两种。
- 手动触发:可以用save或者bgsave命令,区别在于bgsave不会阻塞当前redis实例,bgsave会fork一个子进程完成持久化工作,阻塞只发生在fork阶段
- 自动触发:可以在配置文件中使用save配置,配置m秒内存在n次修改,自动触发持久化过程。
说说RDB bgsave持久化的详细过程
- 父进程检查当前是否有在运行的子进程,如果有正在运行子进程,直接返回
- 父进程fork一个子进程出来,fork阶段会阻塞,fork完毕后不再阻塞父进程,可以响应其他指令
- 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子性替换
- 子进程发送信号给父进程通知完毕,父进程更新相关的统计信息
RDB持久化的优缺点?
- 优点:
- RDB方式产生的文件是一个压缩的二进制文件,非常适合全量备份和复制场景
- Redis加载RDB恢复数据远远快于AOF方式
- 缺点:
- RDB无法实时持久化/秒级持久化
- 老版本无法兼容新的RDB文件的格式问题
- 优点:
简单说说Redis的AOF持久化?
AOF持久化,是把每次写命令都记录到独立的日志中,重启恢复后再执行AOF命令重新恢复数据。AOF持久化主要是解决了实时性的问题。
说说AOF持久化的详细过程
- 所有命令写入追加到aof_buf缓冲区中
- 文件同步,文件同步可以在配置文件中配置三种同步机制,考虑到性能和数据安全性,一般推荐everysec
- always:写入到aof_buf以后,直接调用fsync系统调用同步到AOF文件
- everysec:写入到aof_buf后,先调用write系统调用,写入系统缓冲区后就会返回,然后由专门线程,每秒调用一次fsync操作
- no:写入到aof_buf后,调用write系统调用写入缓冲区后返回,等待操作系统执行同步磁盘的工作。
讲解下Redis线程模型?
Redis线程模型中包含四个组成部分:
- 套接字
- IO多路复用程序
- 事件分发器
- 事件处理器
IO多路复用程序用select或者其他系统调用管理多个套接字的网络事件,然后放入到一个统一的队列中,事件分发器从队列中取事件,然后分发到不同的处理器中进行处理。
谈谈 redis 和 memcached 的区别?
- 数据结构的丰富度:redis支持更丰富的数据结构。
- 持久化功能:redis支持RDB或者AOF方式的持久化;memcached不支持
- 集群功能:redis支持分布式;memcached不支持,需要客户端上使用分布式一致性hash算法来决定目标节点
- 线程模型不同:redis是单线程的,由一个线程负责处理所有的网络事件和业务处理;memcached是一主多从的reactor线程模型。
说说Redis中的数据类型
- String类型:一般用于复杂计数功能的缓存
- Hash类型:hash是以一个String类型为键,多个field和value的映射表作为值的数据结构,可以用于存储包括多列的行信息
- list类型:list简单理解就是链表,可以用来实现关注列表,消息列表等等
- Set类型:去重的数据结构,可以方便的实现交集并集等操作
- Sorted Set类型:增加了一个score权重,可以把Set中的数据按照score排序
redis的过期策略以及内存淘汰机制
redis中可以设置过期时间,当到了过期时间后,redis中会采取下面两种策略删除过期数据:
- 定期删除:每隔100ms随机抽取一些过期的key进行删除
- 惰性删除:当客户端访问到这个key了,才会实际删除
上面两种机制都无法保证完全清除过期的key,redis中引入了内存淘汰机制来保证内存不会被过期数据占满。默认有六种淘汰机制:
- 从已过期的数据中删除的:lru,random和ttl
- 从所有key中删除:lru,random
- 不删除:no-eviction
简单谈谈redis的事务
redis支持简单的事务,使用multi和exec来开始和执行事务,类似关系型数据库中的begin和commit。redis中不支持回滚事务,当出错时,根据不同的错误类型,处理机制也不同。
当有语法错误时,会导致整个事务中的命令都不会执行
当有运行时错误时,不会影响事务中的其他命令的执行
另外,redis中提供了watch命令,watch一个key之后,事务中如果有其他客户端修改了该key,整个事务不执行。
你知道缓存雪崩吗?简单说说
缓存雪崩指的是,当redis集群不可用或者同时有大量缓存失效时,会造成大量的请求都走数据库,把数据库服务击垮。
- 针对redis集群不可用,应该事前做好redis集群的高可用性检查,选择合适的内存淘汰策略。
- 针对大量缓存同时失效,可以采取设置随机过期时间的方式,来避免缓存同时失效
- 提前设计好 本地缓存和限流降级方案,本地缓存+redis中如果都没有,再去查数据库,查数据库的请求要通过限流降级组件来保护数据库。
你知道缓存穿透吗?
缓存穿透指的是,大量请求的key本身不在缓存中,导致直接走了数据库查询。举例来说,黑客攻击时,会伪造很多不存在的key来导致大量请求落到数据库中。解决方法有两种:
- 最基本的是做好参数校验,不合规的参数直接返回
- 使用布隆过滤器,其实本质就是一个hash函数加位数组,报存在,有一定概率的误判(hash冲突),但是不存在一定是准确的,比如两个字符串的hash值相同得到位数组的位置就相同。提前把有效的key存储在布隆过滤器中,这样当无效key传递过来时,直接返回。
如何用redis实现分布式锁?你还知道其他更好的方式吗?
实际业务中没有使用过,这块等待有真实业务场景应用后再来补充。
如何保证缓存与数据库双写时的数据⼀致性?
- 简单的解决方式:如果有更新需求,先删除缓存,再更新数据库,如果数据库更新失败,缓存也为空,不会出现不一致。
- 简单解决方式的不足:删除缓存完毕后,更新数据库之前,又有一个线程要访问该数据,结果发现缓存中没有数据,从数据库中查询出旧数据,然后更新到缓存中,这个时候更新数据库,那么缓存和数据库又发生了不一致。
- 解决方案:使用一个队列把读请求和写请求串在一起,写请求执行完毕后才会执行读请求和更新,但是要注意测试读请求的阻塞时长。如何保证缓存与数据库的双写一致性?
面试题-MySQL和Redis(更新版)的更多相关文章
- 测开常见面试题什么是redis
企业中redis是必备的性能优化中间件,也是常见面试题,首先Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库.Redis全称为:Rem ...
- 通过Gearman实现MySQL到Redis的数据同步
对于变化频率非常快的数据来说,如果还选择传统的静态缓存方式(Memocached.File System等)展示数据,可能在缓存的存取上会有很大的开销,并不能很好的满足需要,而Redis这样基于内存的 ...
- 比Redis更快:Berkeley DB面面观
比Redis更快:Berkeley DB面面观 Redis很火,最近大家用的多.从两年前开始,Memcached转向Redis逐渐成为潮流:而Berkeley DB可能很多朋友还很陌生,首先,我们简单 ...
- JAVA通过Gearman实现MySQL到Redis的数据同步(异步复制)
MySQL到Redis数据复制方案 无论MySQL还是Redis,自身都带有数据同步的机制,像比较常用的 MySQL的Master/Slave模式 ,就是由Slave端分析Master的binlog来 ...
- Docker 小记 — MySQL 与 Redis 配置
前言 本篇随笔是继 "Docker Engine" 与 "Compose & Swarm" 之后的一个实例补充,初衷是记录测试环境中的一次 MySQL ...
- 记录CentOS 7.4 上安装MySQL&MariaDB&Redis&Mongodb
记录CentOS 7.4 上安装MySQL&MariaDB&Redis&Mongodb 前段时间我个人Google服务器意外不能用,并且我犯了一件很低级的错误,直接在gcp讲服 ...
- 电商中的库存管理实现-mysql与redis
库存是电商系统的核心环节,如何做到不少卖,不超卖是库存关心的核心业务问题.业务量大时带来的问题是如何更快速的处理库存计算. 此处以最简模式来讨论库存设计. 以下内容只做分析,不能直接套用,欢迎 ...
- MySQL与Redis实现二级缓存
redis简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库 Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化, ...
- 云服务器配置 docker java mysql mongodb redis nginx 环境
磁盘挂载 fdisk -l #查看磁盘列表 mkfs.ext4 /dev/vdb #格式化磁盘 mount /dev/vdb /data #挂载磁盘在/data echo '/dev/vdb /dat ...
- PHP商品秒杀问题解决方案实例详解【mysql与redis】
本文实例讲述了PHP商品秒杀问题解决方案.分享给大家供大家参考,具体如下: 引言 假设num是存储在数据库中的字段,保存了被秒杀产品的剩余数量. if($num > 0){ //用户抢购成功,记 ...
随机推荐
- Solution Set -「NOIP Simu.」20221011
「Unknown」找 给出平面上 \(n\) 个点, 对于每个点, 求出它到其他点的欧式距离平方和. \(n\le2\times10^5\). Tag:「水题无 tag」 画风正常的签 ...
- JS利用浏览器进行语言识别
JS利用浏览器进行语言识别 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- IOC 操作Bean管理(xml 注入集合属性)+(bean 作用域)
1.注入数组类型属性2.注入 List 集合类型属性3.注入 Map 集合类型属性(1)创建类,定义数组.list.map.set 类型属性,生成对应 set 方法 public class Stu ...
- 拥有自己的解析器(C#实现LALR(1)语法解析器和miniDFA词法分析器的生成器)
拥有自己的解析器(C#实现LALR(1)语法解析器和miniDFA词法分析器的生成器) 参考lex和yacc的输入格式,参考虎书<现代编译原理-C语言描述>的算法,不依赖第三方库,大力整合 ...
- RocketMQ实战—5.消息重复+乱序+延迟的处理
大纲 1.根据RocketMQ原理分析为什么会重复发优惠券 2.引入幂等性机制来保证数据不会重复 3.如何用死信队列处理优惠券系统数据库宕机 4.基于RocketMQ的订单库同步为什么会消息乱序 5. ...
- Q:浏览器打开控制台报错:net::ERR_CONTENT_LENGTH_MISMATCH 206
一.问题描述 F12查看浏览器的控制台,提示net::ERR_CONTENT_LENGTH_MISMATCH 206 (Partial Content) ,如下图, HTTP状态码206表示" ...
- mac安装gcc7
查看gcc版本 gcc --version 1.安装gcc brew install gcc@7 cd /usr/local/Cellar 改名mv gcc\@7/ gcc 2.打开mac的SIP ...
- FLink处理函数ProcessFunction、KeyedProcessFunction、ProcessWindowFunction、 ProcessAllWindowFunction
一.处理函数简介 在底层,我们可以不定义任何具体的算子(比如 map,filter,或者 window),而只是提炼出一个统一的"处理"(process)操作--它是所有转换算子的 ...
- 【Java基础总结】集合框架
集合和数组的区别 集合只存储对象,长度是可变的: 数组既可以存储基本数据类型,又可以存储对象,但长度是固定的. 1. Collection接口 代码演示 1 List<String> c1 ...
- Vue 前端页面利用MediaRecorder实现音频录制
Don't Talk, code is here: 重点是startRecord 方法 <template> <div> <el-tooltip class=" ...