宇宙最全系列 | 数据库面试题v1.0 写在前面 数据库是非常非常非常重要的科目,很多人以为它偏应用所以不去认真复习,这样的想法是错误的。 数据库的知识树非常清晰,关系数据库(SQL)和非关系数据库(NoSQL),其中关系型数据库代表是MySQL,非关系型数据库代表是Redis,具体而言就是:基础概念:数据库专业词汇,关系型和非关系型的区别;SQL语言:语法,数据类型,函数,左连接与右连接,约束,where,on和having;事务:四大特性;索引:索引的种类,功能,底层实现,聚簇索引和非聚簇索引,索引失效;锁:锁的分类,锁的功能和实现。MySQL:四种隔离级别,数据类型,执行流程,四种索引类型,MVCC,两种引擎Innodb和MyISAM;Redis:官方FAQ,数据结构,常用命令,两种持久化机制,运行模式,内存回收,key失效,单线程为何如此高效。。。数据库优化: 学习数据库最好方法是自己安装MySQL和Redis,然后玩一下,比单背八股强太多。 MySQL下载地址:https://dev.mysql.com/downloads/mysql/ Redis下载地址:https://redis.io/download/ 高频考点我用❤标出来了。每个问题揣测一下面试官到底想听什么样的回答,这样才能不断提高自己面试能力。 【友情提示】可以“迹寒编程”,sigusoft“数据库面试题”,本文章的pdf版哦~ 一 基础概念 一些基础的数据库定义等等。 DBMS:数据库管理系统; DDL:数据定义语言。Data Definition Language. 执行完毕会自动提交。 DML:数据操纵语言。Data Manipulation Language.执行完毕不会自动提交。 DTL:数据事务语言。Data Transaction Language. DCL: 数据控制语言。Data Control Language. ODBC:开放式数据库连接:Opened Database Connectivity NoSQL:“不仅仅”是SQL。类似的还有NewSQL。 OLAP:联机分析处理。侧重分析数据和决策支持,是OLTP之后的巨大进步。 OLTP:联机事务处理。指一般的事务处理。 SQL:结构化查询语言。Structured Query Language. 01 ❤关系型数据库和非关系型数据库的区别。各自有什么特点。 关系型数据库(SQL)指用关系模型来组织数据的数据库。常见的关系型数据库有Oracle, MySQL, Microsoft SQL Server, PostgreSQL, SQLite, IBM DB2… 非关系型数据库(NoSQL),不仅仅是SQL,数据以对象的形式存储在系统中,对象之间的关系通过每个对象自身的属性来决定。通常用于存储非结构化数据。常见的NoSQL有: 键值数据库: Redis, Memcached, Riak列族数据库:BigTable, HBase, Cassandra文档数据库:MongoDB, CouchDB, MarkLogic图形数据库:Neo4j, InfoGrid 关系数据库和非关系数据库对比: 关系型数据库中,数据集中存储,数据高度结构化,独立性高,类似一张二维表。而非关系型数据库:支持非结构化的数据,比如kv,文档,多媒体等,而SQL只支持结构化数据适用于高并发的场景,SQL不能做到适用于海量数据,成本低,可以轻松实现数据分布式处理 02 ❤什么是关系模型 关系模型是指用二维表的形式表示实体和实体间联系的数据模型。主要由数据结构,数据操作和数据完整性三部分组成。数据完整性包括:实体完整性,参照完整性和用户定义完整性。
03 SQL中的一些术语。 数据库:一些管理表的集合。 数据表:表是数据矩阵。 列:一列包含了相同类型的数据。 行:等于组,是一组相关的数据,比如用户的资料。 冗余:存储数据的副本和版本,通过这种办法提高数据可靠性。 主键:主键是唯一的,一个数据表中只包含一个主键,可以通过主键来查询数据。 复合键:复合键将多列作为索引,一般用于复合索引。 索引:使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构。类似于书籍的目录。 二 SQL 01 讲一讲你知道的SQL命令。 SELECT – 从数据库中提取数据UPDATE – 更新数据库中的数据DELETE – 从数据库中删除数据INSERT INTO – 向数据库中插入新数据CREATE DATABASE – 创建新数据库ALTER DATABASE – 修改数据库CREATE TABLE – 创建新表ALTER TABLE – 变更(改变)数据库表DROP TABLE – 删除表CREATE INDEX – 创建索引(搜索键)DROP INDEX – 删除索引 02 ❤请你说一下DROP、TRUNCATE、DELETE的区别? drop可以用来删除表或数据库并且将表所占用的空间全部释放 truncate会清除表数据并重置id从1开始; delete就只删除记录; truncate和delete只删除数据不删除表的结构。drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger),依赖于该表的存储过程/函数将保留,但是变为 invalid 状态。 速度上一般来说: drop> truncate > delete 使用上,想删除部分数据行用 delete,想删除表用 drop,想保留表而将所有数据删除,如果和事务无关,用truncate即可。如果和事务有关,或者想触发trigger,还是用delete。 delete是DML语句,不会自动提交。drop/truncate都是DDL语句,执行后会自动提交。 03 ❤数据库分页是怎么做的。 。N表示偏移量,M表示返回的行数。 不同的数据库实现分页的方式是不同的,下面我们以MySQL为例来说明如何分页。在MySQL中,分页是通过LIMIT子句实现的,LIMIT的语法如下。总之,带有一个值的LIMIT总是从第一行开始,返回指定的行数。带两个值的LIMIT可以从指定行号处开始,返回指定的行数。 04 接上题,对于很大的偏移量,有没有提高分页性能的方式? 通常我们用limit N M来获得第N行开始的M行数据,但是这样会读前10000行数据,显然它们并没有用,还会带来巨大性能开销。 第一次优化:我们可以采用where子句判断和自增id: 但这种方法要求表必须有自增索引,而且还必须是连续的。 第二次优化:我们先查找需要数据的索引列,再通过索引列找出需要的数据。设需要查找的用户是xxx。 第三次优化:in操作在数据量大的时候效率就不行了,我们可以用inner join代替。 参考资料:mysql中走与不走索引的情况汇集(待全量实验) 04 ❤介绍一下SQL中的聚合函数。 count()、avg()、sum()、max()、min()COUNT(*)计算表中总的行数,不管某列是否有数值或者为空值。 COUNT(字段名)计算指定列下总的行数,计算时将忽略空值的行。 AVG()函数:通过计算返回的行数和每一行数据的和,求得指定列数据的平均值。 SUM()函数:是一个求总和的函数,返回指定列值的总和。 MAX()函数:返回指定列中的最大值,不仅适用于查找数值类型,也可应用于字符类型。 MIN()函数:返回查询列中的最小值,不仅适用于查找数值类型,也可应用于字符类型。 此外,聚合函数可以与GROUP BY关键字一起使用,对每个分组进行计算。 05 ❤数据库中的表是怎么关联的。 内连接、外连接、等值连接。 从表的关联形式来说,一对多关联、多对多关联、自关联。一对多关联:这种关联形式最为常见,一般是两张表具有主从关系,并且以主表的主键关联从表的外键来实现这种关联关系。另外,以从表的角度来看,它们是具有多对一关系的,所以不再赘述多对一关联了。 多对多关联:这种关联关系比较复杂,如果两张表具有多对多的关系,那么它们之间需要有一张中间表来作为衔接,以实现这种关联关系。这个中间表要设计两列,分别存储那两张表的主键。因此,这两张表中的任何一方,都与中间表形成了一对多关系,从而在这个中间表上建立起了多对多关系。 自关联:自关联就是一张表自己与自己相关联,为了避免表名的冲突,需要在关联时通过别名将它们当做两张表来看待。一般在表中数据具有层级(树状)时,可以采用自关联一次性查询出多层级的数据。 06 ❤请你说一说SQL中各种join。 left join(左连接)左表的所有记录,右表没有对应匹配的记录则为NULL。
right join(右连接)右表的所有记录,左表没有对应匹配的记录则为NULL。
inner Join(等值连接,内连接)只返回两个表中联接字段相等的行。
outer Join (外连接)只要一个表存在匹配则返回行。
例子:现在有两个表 A:
B:
A left Join B
返回的是A表所有数据和B表id在A表的行。 2. A right join B
返回的是B表所有数据和A表id在B表的行。 3. A inner join B
得到的是共同部分。 各连接关系总结:
07 ❤你说一下SQL约束有哪些。 NOT NULL:非空约束,用于控制字段的内容一定不为空。 UNIQUE:字段唯一性约束; PRIMARY KEY: 主键约束,在一个表只能有一个主键。 FOREIGN KEY: 外键约束,防止破坏表之间的连接。 CHECK:check约束,用于控制字段范围。 08❤请你说一说数据库的三大范式。 第一范式(1NF)原子性约束:要求每列都是最小的数据单,不可分割。第二范式(2NF)唯一性约束:在第一范式的基础上,要求每列都和主键相关。第三范式(3NF)冗余性约束:在第二范式的基础上,要求每列都和主键直接相关,而不是间接相关。 09 没有冗余数据的数据库是最好的吗? 没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据,具体做法是:在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑,降低范式就是增加字段,允许冗余。 10 什么是子查询。 条件:一条SQL语句的查询结果作为另一条SQL语句输入。嵌套:多条SQL语句嵌套使用,内部SQL查询语句称为子查询。 11 存储过程(procedure)和函数(function)区别 标识符不同,函数标识符是function,存储过程是procedure。函数返回单个值或者表对象,而过程没有返回值, 但是可以通过out参数返回多个值;函数的参数只能是 IN 类型,存储过程的参数可以是 IN OUT INOUT 三种类型。 函数使用 select 调用,存储过程需要使用 call 调用。SQL语句不可调用存储过程。 12 ❤听说过视图吗?游标呢? 视图(view)是虚拟的表,不包含任何数据,视图只包含动态检索数据的查询。它可以针对不同用户提供不同的数据查询,从而保证了数据安全性。 视图不能被索引,也不能有关联的触发器或者默认值。 游标是对查询出来的结果集作为一个单来处理。 视图使用方法:create view view_name as select * from tb; 13 where, on 和having有什么区别? where和having的根本区别在于:where在group by和聚合函数之前对数据进行过滤;having在group by和聚合函数之后对数据进行过滤; where和on的区别在于:内连接中,on和where等价;外连接,on中的过滤条件在连接操作之前执行,而where在连接操作之后执行。 在查询过程中执行顺序:from>where>group(含聚合)>having>order>select。 14 in 和 exists 区别 in : 确定给定的值是否与子查询或列表中的值相匹配。in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。所以相对内表比较小的时候,in的速度较快。 exists:指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。匹配上就将结果放入结果集中。如果查询的两个表大小相当,那么用in和exists差别不大。 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。 not in 和not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。 参考资料:Sql语句中IN和exists的区别及应用 三 事务 事务型的数据库被应用在重要场合,比如金融,交易等。 01 ❤请你说一说数据库事务?ACID? 数据库事务(Database Transaction)指单个工作的逻辑单执行的一系列操作,要么完全执行,要么完全不执行,不可分割。事务处理可以确保除非事务性单的所有操作都成功完成,否则不会永久更新面向数据的资源。一个逻辑单要成为事务,必须满足ACID(Atomicity, Consistency, Isolation, Durability) 原子性,一致性,隔离性,持久性。 原子性:数据库中事务要么完全执行,要么完全不执行。即使失败了也不能对数据库有任何影响。 一致性:一致性是指事务必须使数据库一致性状态不变,也就是说一个事务执行之前和执行之后都必须处于一致性状态。 拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。 隔离性:是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。这指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。 持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的(固化到硬盘),即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。 02 MySQL的ACID特性分别是怎么实现的? 原子性: 原子性的含义就是之前成功执行的sql语句能够撤销。InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。 持久性 持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。redo log被引入来解决这个问题:当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行存盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。 隔离性:(一个事务)写操作对(另一个事务)写操作的影响:锁机制。 保证隔离性事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。(一个事务)写操作对(另一个事务)读操作的影响:MVCC保证隔离性: 1)隐藏列:InnoDB中每行数据都有隐藏列,隐藏列中包含了本行数据的事务id、指向undo log的指针等。 2)基于undo log的版本链:前面说到每行数据的隐藏列中包含了指向undo log的指针,而每条undo log也会指向更早版本的undo log,从而形成一条版本链。 3)ReadView:通过隐藏列和版本链,MySQL可以将数据恢复到指定版本;但是具体要恢复到哪个版本,则需要根据ReadView来确定。所谓ReadView,是指事务(记做事务A)在某一时刻给整个事务系统(trx_sys)打快照,之后再进行读操作时,会将读取到的数据中的事务id与trx_sys快照比较,从而判断数据对该ReadView是否可见,即对事务A是否可见。 一致性 一致性是事务追求的最终目标,前问所诉的原子性、持久性和隔离性,其实都是为了保证数据库状态的一致性。 当然,上文都是数据库层面的保障,一致性的实现也需要应用层面进行保障。 参考资料:深入学习MySQL事务:ACID特性的实现原理 03 事务可以嵌套吗? 可以。嵌套事务(Nested Transactions)是一个层次结构框架,由一个顶层事务(top-level transaction)控制着各个层次的事务,顶层事务之下嵌套的事务被称为子事务,其控制每一个局部的变换。 04 事务如何回滚?哪些语句不可以回退? 事务回滚用的是ROLLBACK。务处理用来管理 INSERT 、 UPDATE 和DELETE 语句。你不能回退 SELECT 语句。(这样做也没有什么意义。)你不能回退 CREATE 或 DROP 操作。事务处理块中可以使用这两条语句,但如果你执行回退,它们不会被撤销。 四 索引 索引是数据库的“神笔马良”。 01 ❤索引的种类有哪些? 按照约束条件可以分为:普通索引,全文索引,唯一索引,主键索引。 按照列数可以分为:单列索引和组合索引。 02 ❤请你说一说数据库索引? 索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。如果想按特定职员的姓来查找,则与在表中搜索所有的行相比,索引有助于更快地信息。 索引的一个主要目的就是加快检索表中数据的方法,亦即能协助信息搜索者尽快的找到符合限制条件的记录ID的辅助数据结构。 索引的优点:通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。可以大大加快数据的检索速度,这也是创建索引的最主要原因。帮助服务器避免排序和临时表将随机IO变为顺序IO。可以加速表与表之间的连接,在实现参照完整性有重要意义。 03 ❤索引底层是如何实现的? 一般分为B+树索引和哈希索引:B+树索引:其非叶子节点均为key,叶子节点是key-data键值对,叶子节点前后相连且有序。哈希索引:对key进行散列化,存储在不同的桶中,可以做到常数时间复杂度的查找,删除和插入。但要注意哈希冲突的避免(链表法,线性检测,二次探测,公共溢出区方法) 背景知识: 什么是B树? B树每个节点可以有多个子节点,是“多叉树”。B树是专门为磁盘,SSD这类外部存储器设计的。传统的平衡树,比如红黑树,AVL树等在数据量小的情况下性能非常好,但在大数据量情况下便无能为力了,无法很好的利用磁盘预读的局部性原理。 索引的效率依赖于磁盘 IO 的次数,快速索引需要有效的减少磁盘 IO 次数,如何快速索引呢? 索引的原理其实是不断的缩小查找范围,就如我们平时用字典查单词一样,先找首字母缩小范围,再第二个字母等等。平衡二叉树是每次将范围分割为两个区间。为了更快,B-树每次将范围分割为多个区间,区间越多,定位数据越快越精确。那么如果节点为区间范围,每个节点就较大了。所以新建节点时,直接申请页大小的空间(磁盘存储单位是按 block 分的,一般为 512 Byte。磁盘 IO 一次读取若干个 block,我们称为一页,具体大小和操作系统有关,一般为 4 k,8 k或 16 k),计算机内存分配是按页对齐的,这样就实现了一个节点只需要一次 IO。
上图是一棵简化的B-树,多叉的好处非常明显,有效的降低了B-树的高度,为底数很大的 log n,底数大小与节点的子节点数目有关,一般一棵B-树的高度在 3 层左右。层数低,每个节点区确定的范围更精确,范围缩小的速度越快(比二叉树深层次的搜索肯定快很多)。 B+树与B树相比:1. 除了叶子节点以外,不存储实际数据。2. 叶子节点按照从小到大链接起来。
04 ❤为什么使用B+树而不使用B树? 从磁盘IO的角度: IO次数更少:由于B+树在非叶子节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。 遍历次数少:B+树的叶子结点都是相连的,因此对整棵树的遍历只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的素可能在内存中不相邻,所以缓存命中性没有B+树好。从数据结构的角度: 1.B+树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为 log n。而B-树查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)。 2. B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而B-树每个节点 key 和 data 在一起,则无法区间查找。 3.B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确 。 参考资料:MySQL用B+树(而不是B树)做索引的原因, https://www.sigusoft.com/p/ace3cd6526c4 05 为什么有了B+树还要索引还要hash索引? B+树默认有序而hash表默认无序,所以哈希索引无法用于排序;哈希索引O(1) 在速度上快于B+树O(logn)哈希索引只能进行等值查询(因为它要计算hash(key)再去匹配),而B+树可以进行等值、部分前缀、范围查询;底层实现结构不同:B+树是非线性结构,hash的桶是线性结构。对于某些场景如热点页/活跃查询页,需要借助哈希索引来快速查询。 06 ❤聚簇索引和非聚簇索引的区别。 当表有了聚簇索引的时候,表的数据行都存放在索引树的叶子页中。无法把数据行放到两个不同的地方,所以一张表只允许有一个聚簇索引。InnoDB的聚簇索引实际上是将索引和数据保存中同一个B-Tree中。InnoDB通过主键聚集数据,如果没有定义主键,InnoDB会选择一个唯一的的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键来作为聚簇索引。 非聚簇索引(NoClustered Index),又叫二级索引。二级索引的叶子节点中保存的不是指向行的物理指针,而是行的主键值。当通过二级索引查找行,存储引擎需要在二级索引中找到相应的叶子节点,获得行的主键值,然后使用主键去聚簇索引中查找数据行,这需要两次B-Tree查找。 07 ❤InnoDB和MyISAM实现索引的区别? MyISAM,B+Tree叶节点的data域存放的是数据记录的地址,在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址读取相应的数据记录,这被称为“非聚簇索引”。InnoDB,其数据文件本身就是索引文件,相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的节点data域保存了完整的数据记录,这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引,这被称为“聚簇索引”或者聚集索引,而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。 08 ❤添加索引的原则 索引虽然能非常高效的提高查询速度,同时却会降低更新表的速度。 需要建立索引的情况有:主键自动建立唯一索引频繁作为查询条件的字段应该建立索引查询中与其它表管理的字段,外键关系建立索引很多情况,复合索引比单列索引更有效率查询中排序的字段,如果通过索引将大大提高查找速度查询中统计或者分组字段 不需要建立索引的情况表记录太少,遍历索引时间比查询时间还长经常增删改的表过滤性不好的列不适合作索引,比如性别列 参考资料:MySQL 如何创建索引?怎么优化? 09 ❤只要创建了索引,就一定会走索引吗? 索引什么时候失效。 不一定。在有些情况下,索引可能不被使用:使用了!或<> 隐式类型转换 下面salary设计的时候是double,被写错为整数了,不会使用索引。 索引列参与了计算 索引列使用了函数 索引列使用了Like %XXX or只有所有条件建立索引,才会使用索引 使用了order by 在ORDER BY操作中,排序的列同时也在WHERE中时,MYSQL将无法使用索引; 参考资料:一个程序员的成长:导致MySQL索引失效的几种常见写法 10 ❤如何判断数据库的索引有没有生效? 在查询语句前加explain。 explain作用:显示了MySQL索引相关参数。
我们需要type字段:按照性能从优到差:system > const > eq_ref > ref >fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range >index> ALL。一般来说只要不是ALL就是使用了索引。处于性能考虑,最好在range以上。 参考资料:如何查看sql查询是否用到索引(mysql) 五 MySQL MySQL,什么重要性不用我说了吧 01 MySQL有哪些数据类型。 (1)整数类型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、INT、BIG INT。 (2)浮点数类型:FLOAT、DOUBLE、DECIMAL。 (3)字符串类型:CHAR、VARCHAR、TINY TEXT、TEXT、MEDIUM TEXT、LONGTEXT、TINY BLOB、MEDIUM BLOB、LONG BLOB。 (4)日期类型:DATE、DATETIME、TIMESTAMP、TIME、YEAR 使用的时候建议遵循从小原则。 (1)使用char或者varchar的时候,注意char会去掉字符串末尾的空格。 (2)使用text或者blob的时候,注意定期清理碎片空间,使用OPTIMIZE TABLE命令。 (3)浮点数会造成精度丢失,尽量使用decimal。 02 char,varchar的区别是什么?适用场景。 char存储定长的字符串,比如char(M),存储M个字符。 varchar存储变长字符串,它比char更灵活。 对于定长或者短字符串,使用char更好,不容易产生内存碎片。 03 ❤请你说一下MySQL的四种隔离级别。 Read Uncommitted 未提交读:一个事务在提交之前,对其它事务是可见的,即事务可以读取未提交的数据。存在“脏读”(读到了脏数据)的问题。Read Committed 提交读:事务在提交之前对其它事务是不可见的。虽然解决了“脏读”的问题,但是存在“不可重复读”的问题(多次查询得到的结果不同)。Repeatable Read 可重复读:在同一事务中多次读取的数据是一致的。解决了脏读和不可重复读问题,但是如果在两次查询的间隙数据被修改,这还是不一致,即“幻读”。MySQL默认的隔离级别。Serializable Read 串行读:强制事务串行化执行,是最高的隔离级别,可以解决上述所有问题。 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问 这个数据,然后使用了这个数据。 不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不 可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果 只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。这是MySQL默认支持的隔离级别。 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。幻读可以通过MVCC+间隙锁(Next-key locking)来防止幻读。 不可重复读的重点是修改,幻读的重点在于新增或者删除。 04 什么是触发器,MySQL中都有哪些触发器? 触发器是特殊的存储过程,当触发某个事件时,自动执行这些代码。在MySQL数据库中有如下六种触发器:1、Before Insert2、After Insert3、Before Update4、After Update5、Before Delete6、After Delete before/after: 触发器是在增删改之前执行,还是之后执行 delete/insert/update: 触发器由哪些行为触发(增、删、改) on 表名: 触发器监视哪张表的(增、删、改)操作 触发SQL代码块: 执行触发器包含的SQL语句 注意: 触发器也是存储过程程序的一种,而触发器内部的执行SQL语句是可以多行操作的,所以在MySQL的存储过程程序中,要定义结束符。 05 ❤请你说一下MySQL是如何执行一条SQL的?具体步骤有哪些? MySQL分为两层:服务层和引擎层。
步骤如下:客户端请求连接器(身份验证,权限grant)查询缓存(如果缓存命中则直接返回)分析器(对SQL进行词法分析和语法分析操作)优化器(主要对执行的sql优化选择最优的执行方案方法)执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)去引擎层数据返回(如果开启查询缓存则会缓存查询结果) 06 请你说一下MySQL的四种索引类型? (注意这里的索引类型指索引实现类型,务必与索引种类区别开)FULLTEXT: 即为全文搜索。即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。HASH :由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。 HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。BTREE :BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中(二叉树),每次查询都是从树的入口root开始,依次遍历node,leaf。这是MySQL里默认和最常用的索引类型。RTREE :RTREE在MySQL很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。 相对于BTREE,RTREE的优势在于范围查找。 07 ❤InnoDB为什么要用自增ID作为主键? 如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。 如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置, 频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE(optimize table)来重建表并优化填充页面。 08 ❤请你解释一下MySQL的MVCC? MVCC是多版本并发控制的意思; 是MySQL的InnoDB存储引擎实现隔离级别的一种具体方式用于实现提交读和可重复读这两种隔离级别。 MVCC是通过保存数据在某个时间点的快照来实现该机制,其在每行记录后面保存两个隐藏的列。分别保存这个行的创建版本号和删除版本号,然后InnoDB的MVCC使用到的快照存储在Undo的日志中。该日志通过回滚指针将一个数据行的所有快照连接起来。 09 ❤请你说一下MySQL引擎和它们之间的区别? MySQL的存储引擎主要有:MyIsam, InnoDB, Memory, Blackhole, CSV, Performance_Schema, Archive, Federated, Mrg_Myisam. 最常见的是MyIsam和InnoDB. InnoDB数据库引擎(MySQL5.5以后默认使用): InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID)行锁定和外键。其主要特性有:Innodb引擎提供了对数据库ACID事务的支持,并且实现了SQL标准的四种隔离级别。InnoDB支持外键完整性约束,存储表中的数据时,每张表的存储都按主键顺序存放。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统。InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB将它的表和索引在一个逻辑表空间中,表空间可以包含数个文件(或原始磁盘文件)。 容灾能力强,,支持热备份 MyISAM数据库引擎: MyISAM引擎,由于支持全文索引,它是在Web,数据仓库和其它应用场景下最常使用的存储引擎。具有较高的插入、查询速度,但不支持事务。主机宕机后,MyISAM表易损坏,灾难恢复性不佳。MYISAMMemoryInnoDBArchive存储限制256TBRAM64TBNone支持事务NNYN支持全文索引YNNN支持树索引YYYN支持哈希索引NYNN支持数据缓存NNYN支持外键NNYN 总结 InnoDB:优点:支持事务,外键,行级锁(甚至间隙锁),MVCC,并发量较大,适合大量 update。缺点:不适合大量的 select。 MyISAM:优点:查询数据相对较快,适合大量的 select,可以全文索引。存储容量256T比InnoDB大。缺点:不支持事务,不支持外键,并发量较小,不适合大量 update。容灾性能不佳。 10 MySQL为什么要有回滚机制? 而在 MySQL 中,恢复机制是通过回滚日志(undo log)实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后在对数据库中的对应行进行写入。 当事务已经被提交之后,就无法再次回滚了。 回滚日志作用: 1)能够在发生错误或者用户执行 ROLLBACK 时提供回滚相关的信息 2) 在整个系统发生崩溃、数据库进程直接被杀死后,当用户再次启动数据库进程时,还能够立刻通过查询回滚日志将之前未完成的事务进行回滚,这也就需要回滚日志必须先于数据持久化到磁盘上,是我们需要先写日志后写数据库的主要原因。 11 说一说你对redo log、undo log、bin log、relay log的了解 mysql实现事务是根据undo日志和redo日志实现的 undo:记录修改前的信息。为了实现事务原子性,要么全部完成commit,要么全部回滚rollback,Innodb用于实现mvcc(多版本并发控制) redo:记录修改后的信息。如果使用undo log作为持久化数据,意味着修改数据和undo log必须同时写入磁盘持久化,这必定带来巨大的磁盘io,解决方案为了平衡磁盘io和一致性,引入redo log。数据和undo log可定时从缓冲刷至磁盘,但是redo log必须实时写入磁盘,当系统奔溃时,可依据redo log进行数据重做。InnoDB存储引擎层的日志。 bin:redo log可以实现数据重做,那为什么要使用binlog?因为redo log是Innodb实现的物理日志,一旦涉及到多种存储引擎,无法进行重做。bin log 记录下所有数据的更改,可用于本机数据恢复和主从同步。 relay:用于主从同步。Mysql 主节点将binlog写入本地,从节点定时请求增量binlog,主节点将binlog同步到从节点。从节点单独进程会将binlog 拷贝至本地 relaylog中。从节点定时重放relay log。 参考链接:Mysql redo、undo、bin、relay log 区别 12 ❤MySQL主从同步是如何实现的? MySQL主从同步采用的是复制,分为以下步骤:主库的更新事件会被写到二进制日志(bin log)日志中。从库启用slave服务,发起连接,连接到主库,从数据库通过一个I/O线程与主服务器保持通信,并监控master的二进制日志文件的变化。如果发现master二进制日志文件发生变化,则会把变化复制到自己的中继日志(Relay log)中从库再创建一个SQL线程,从relay log里面读取内容,把相关的“事件”执行到自己的数据库。以此实现从数据库和主数据库的一致性,也就实现了主从复制。 复制的工作原理并不复杂,其实就是一个完全备份加上二进制日志备份的还原。不同的是这个二进制日志的还原操作基本上实时在进行中。这里特别需要注意的是,复制不是完全实时地进行同步,而是异步实时。这中间存在主从服务器之间的执行延时,如果主服务器的压力很大,则可能导致主从服务器延时较大。 主从复制的好处是:一旦主库发生故障,可以主库立刻切换到从库,以实现高可用;可以实现读写分离,从库负责读,主库负责写,以实现更高的并发;有利于架构扩展,后面加入多个数据库。
参考资料:https://blog.csdn.net/chongbin007/article/details/ 13 MySQL读写分离是如何实现的? 一般采用代理服务器。代理一般介于应用服务器和数据库之间,代理数据库服务器接收到应用服务器的请求后根据判断后转发到数据库。 比如: MySQLProxy或者mycat进行相应的分析,判断出是读操作还是写操作,分发至对应的MySQLServer上。对于多节点Slave集群,也可以起做到负载均衡的效果,就是可以让读操作分配到不同的服务器,而不至于一直读一个服务器的数据造成压力。 14 ❤分库分表是怎么做的? 之前提到数据库读写分离,当写操作过多时,就会出现“too many connections”错误,因为访问量过大,或者数据库最大连接量过小导致的,因此需要进行分库。 分库分为两种:垂直拆分和水平拆分。 垂直拆分:不同的数据库负责不同的功能,比如用户数据库负责保存用户信息,订单数据库保存订单信息; 水平拆分:当一张表数据量过大,可以根据数据属性(比如所属地,种类)拆分库,需要分布式事务的支持; 分表也分为两种:垂直划分和水平划分。 垂直划分:把不同的访问频率的数据进行分表,例如热点数据放一张表,冷数据放另外的表; 水平划分:控制一张表数据行数,比如可以用过id进行取模,根据取模的结果分到不同的表。 参考资料:数据库的分库分表、读写分离和主从复制——简书 15 MySQL DoubleWriteBuffer是什么,为什么要关闭? double write buffer 是mysql系统表空间的一块存储区域。 在Innodb将数据页写到数据存储文件之前,存储从Innodb缓存池刷过来的数据页。且只有将数写入doublewriter buffer后,Innodb才会进行数据页的物理存储。如果在数据页写盘时发生操作系统、存储系统、或者myql进程中断,Innodb可以从doublewriter buffer存储中找回丢失的数据页备份。 虽然数据总是双份写,但并不意味着需要双倍的IO操作,或者IO能力。通过向操作系统发送fsync()命令,将数据以较大的连续数据块的形式写入doublewriter buffer。 doublewriter buffer多数情况下是默认开启的,可以通过配置innnodb_doublewrite=0关闭。 关闭doublewriter buffer可以在一定程度上提升数据库层面性能,但是要处理好容灾。可以在应用层面通过重试补偿,或者数据检查机制等处理写中断引发的问题。 mysql write buffer_mysql 优化之 doublewrite buffer 机制 14 如何保证缓存-db一致性 读的时候先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。 更新的时候先更新缓存,再更新数据库。不然的话,数据库更新数据是新数据,而缓存还是旧数据,出现数据不一致。 删除的时候,先删缓存,再删数据库。 原则:缓存先操作,数据库后操作。 六 锁 锁也是很重要的一环,在面试中属于较难的问题。 01 ❤数据库里面锁有哪些? 按照锁粒度分:锁定的数据量越少(粒度越小),并发程度越高,但相应的加锁、检测锁、释放锁用的系统开销也随之增大。共享锁(S锁,读锁):互不阻塞,优先级低;排他锁(X锁,写锁):阻塞其它锁,优先级高,即确保在一个事务写入时,不受到其它事务的影响。 按照锁策略分:锁开销与数据安全性之间的权衡表锁:锁住整张表,读锁互不阻塞,写锁阻塞其他所有的读写锁(同一张表),开销最小,包括读写意向锁,自增锁;行级锁:对每一行数据加锁,开销大,并发程度高,包括记录锁,Next-Key Lock,间隙锁和插入意向锁。 参考资料:Java架构师想静静:把MySQL中的各种锁及其原理都画出来 02 介绍一下读写意向锁 由于表锁和行锁虽然锁定范围不同,但是会相互冲突。所以当你要加表锁时,势必要先遍历该表的所有记录,判断是否加有排他锁。这种遍历检查的方式显然是一种低效的方式,MySQL 引入了意向锁,来检测表锁和行锁的冲突。 意向锁也是表级锁,也可分为读意向锁(IS 锁)和写意向锁(IX 锁)。当事务要在记录上加上读锁或写锁时,要首先在表上加上意向锁。这样判断表中是否有记录加锁就很简单了,只要看下表上是否有意向锁就行了。 意向锁之间是不会产生冲突的,也不和 表锁冲突,它只会阻塞表级读锁或表级写锁,另外,意向锁也不会和行锁冲突,行锁只会和行锁冲突。 03 介绍一下自增锁 锁又叫自增锁(一般简写成 AI 锁),是一种表锁,当表中有自增列(AUTOINCREMENT)时出现。当插入表中有自增列时,数据库需要自动生成自增值,它会先为该表加 表锁,阻塞其他事务的插入操作,这样保证生成的自增值肯定是唯一的。 锁具有如下特点: 锁互不兼容,也就是说同一张表同时只允许有一个自增锁;自增值一旦分配了就会 +1,如果事务回滚,自增值也不会减回去,所以自增值可能会出现中断的情况。 显然, 表锁会导致并发插入的效率降低,为了提高插入的并发性,MySQL 从 5.1.22 版本开始,引入了一种可选的轻量级锁(mutex)机制来代替 锁,可以通过参数 来灵活控制分配自增值时的并发策略。 04 介绍一下间隙锁 间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。这个间隙可以跨一个索引记录,多个索引记录,甚至是空的。使用间隙锁可以防止其他事务在这个范围内插入或修改记录,保证两次读取这个范围内的记录不会变,从而不会出现幻读现象。 值得注意的是,间隙锁和间隙锁之间是互不冲突的,间隙锁唯一的作用就是为了防止其他事务的插入,所以加间隙 S 锁和加间隙 X 锁没有任何区别。 05 介绍一下Next-Key锁 Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。假设一个索引包含 15、18、20 ,30,49,50 这几个值,可能的 Next-key 锁如下: (-∞, 15],(15, 18],(18, 20],(20, 30],(30, 49],(49, 50],(50, +∞) 通常我们都用这种左开右闭区间来表示 Next-key 锁,其中,圆括号表示不包含该记录,方括号表示包含该记录。前面四个都是 Next-key 锁,最后一个为间隙锁。和间隙锁一样,在 RC (读提交)隔离级别下没有 Next-key 锁,只有 RR(可重复读) 隔离级别才有。还是之前的例子,如果 id 不是主键,而是二级索引,且不是唯一索引,那么这个 SQL 在 RR 隔离级别下就会加如下的 Next-key 锁 (30, 49](49, 50) 此时如果插入一条 id = 31 的记录将会阻塞住。之所以要把 id = 49 前后的间隙都锁住,仍然是为了解决幻读问题,因为 id 是非唯一索引,所以 id = 49 可能会有多条记录,为了防止再插入一条 id = 49 的记录。 06 介绍一下插入意向锁 插入意向锁是一种特殊的间隙锁(简写成 II GAP)表示插入的意向,只有在 INSERT 的时候才会有这个锁。注意,这个锁虽然也叫意向锁,但是和上面介绍的表级意向锁是两个完全不同的概念,不要搞混了。 插入意向锁和插入意向锁之间互不冲突,所以可以在同一个间隙中有多个事务同时插入不同索引的记录。譬如在上面的例子中,id = 30 和 id = 49 之间如果有两个事务要同时分别插入 id = 32 和 id = 33 是没问题的,虽然两个事务都会在 id = 30 和 id = 50 之间加上插入意向锁,但是不会冲突。 插入意向锁只会和间隙锁或 Next-key 锁冲突,正如上面所说,间隙锁唯一的作用就是防止其他事务插入记录造成幻读,正是由于在执行 INSERT 语句时需要加插入意向锁,而插入意向锁和间隙锁冲突,从而阻止了插入操作的执行。 07 InnoDB中行级锁是怎么实现的? InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。 参考资料:mysql锁–innodb的行级锁 – 秋雨声 – 博客园 08 数据库在什么情况下会发生死锁? 解决方法是什么? 数据库死锁问题根本是死锁产生的四个条件:互斥,不可剥夺,请求和保持,循环等待。事务之间具有隔离性,所以互斥条件满足,由于表锁和行锁,不可剥夺条件满足,请求和保持条件也满足。所以只要满足循环等待条件就容易产生死锁。具体情况:事务之间对资源访问顺序的交替 一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。 解决方法:写事务时,尽量保证对多表操作时按照相同的顺序处理,如操作A和B两张表时,总是按先A后B的顺序处理。并发修改同一记录 用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。 解决方法:采用乐观锁,乐观锁通常基于数据版本记录机制,为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。索引不当导致全表扫描 如果在事务中执行了一条不满足条件的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。 解决方法:SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。 参考资料:数据库常见死锁原因及处理 七 Redis 推荐看:Redis官方FAQ。 官方给的面试题。 以及官方英文FAQ: https://redis.io/docs/getting-started/faq/#:~:text=However%2C%20to%20maximize%20CPU%20usage%20you%20can%20start,start%20thinking%20of%20some%20way%20to%20shard%20earlier . 01 请你介绍一下Redis Redis (Remote Dictionary Server) 被称为数据结构服务器,它是典型的NoSQL,值可以是1. 字符串(String),2. 列表(list),3. 哈希(Map),4. 集合(Set)或 5.有序集合(Sorted Sets)。Redis数据在内存中所以速度非常块。Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载到内存使用。Redis 不仅支持简单的 key-value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Redis 支持主从复制,即 master-slave 模式的数据备份。 02 ❤redis有哪些数据结构。 字符串(String),列表(list),哈希(Map),集合(Set)或 有序集合(Sorted Sets, zset) 上面是最基本的,除此之外还有范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 拓展:Redis的数据类型有哪些,底层怎么实现? 字符串: 列表:压缩列表,双向链表 哈希:压缩列表,字典 集合:整数集合,字典 有序集合:压缩列表,跳表和字典 03 hyperLogLogs了解吗? hyperLogLogs作用是基数统计。HyperLogLog接收多个素作为输入,并且将不同素的数量作为基数。它只能计算基数而不能存储,即使输入素数量巨大,但计算所需空间是一样的。 使用:pfadd 添加, pfcount计算。 04 单个Redis实例最多可以存储多少键?哈希表、列表、集合和有序集合最大可以包含多少素? Redis最大可以处理2^32键,实践测试每个实例最少可以处理2.5亿键。 换句话说,Redis极限容量就是系统可用内存。 05 Redis常用命令,说几个试试看。 Keys [pattern] 列出所有键值; Set [Key] [Value]设置键值对; Get [key] 值。 Setnx [Key] [Value] 相当于If not exist then set [key] [value]; 如果存在返回0; Del [Key] 删除某个键;如果不存在返回0; Expire [key] [seconds]设置过期时间; TTL [key] 查看过期时间。 Setex [key] [seconds] [value] 设置键值以及过期时间; 06 ❤什么是Redis持久化?有哪几种持久化机制? 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。 Redis 提供了两种持久化方式:RDB(默认) 和AOF RDB:Redis Data Base简称,服务端通常定期将数据固化到磁盘上。
AOF:Append Only File。
服务端以极短的间隔(通常1s)将用户执行过的命令保存到AOF文件中,调用flushAppendOnlyFile函数,完成如下两个功能:WRITE:将aof_buf中的数据写到AOF文件中;SAVE:根据条件,调用fsync或fdatasync,将OF文件保存到磁盘; 存储结构:内容以redis通讯协议RESP格式存储 比较:aof比rdb持久化频率高,可靠性好rdb比aof性能好如果两个都配置,默认使用aof 07 Redis磁盘快照是不是原子操作? 是的,当服务器不在执行命令时,Redis后台保存进程总是被创建,因此每个命令在RAM中是原子的,并且在磁盘快照过程也是原子的。 08 ❤Redis集群有哪些运行模式,简单介绍一下。 有单机模式,主从复制模式,哨兵模式,代理模式,去中心模式。 单机模式:在单机上运行Redis服务器,又被称为伪分布式。存在严重的单点故障和性能瓶颈。
主从复制模式:master节点压力大,负载不均衡
Redis 的复制(replication)功能允许用户根据一个 Redis 服务器来创建任意多个该服务器的复制品,其中被复制的服务器为主服务器(master),而通过复制创建出来的服务器复制品则为从服务器(slave)。 只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。 特点: 1、master/slave 角色 2、master/slave 数据相同 3、降低 master 读压力在转交从库 问题:无法保证高可用 2. 没有解决 master 写的压力哨兵模式(Redis Sentinel):设计多个哨兵来监控集群。
Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性: 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。 特点: 1、保证高可用 2、监控各个节点 3、自动故障迁移 缺点:主从模式,切换需要时间丢数据 没有解决 master 写的压力 直连模式
Redis-Cluster采用无中心结构,每个节点保存数据和整个集群的状态,每个节点都和其它所有节点连接。 特点: 1、无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。 2、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。 3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。 4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本 5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。 缺点: 1、资源隔离性较差,容易出现相互影响的情况。 2、数据通过异步复制,不保证数据的强一致性。 代理模式
通过Redis实例默认提供的代理连接地址,客户端的请求由代理节点转发至数据节点。 直连模式:通过直连地址,客户端可以绕过代理服务器,直接访问后端的数据节点,相比代理模式,直连模式节约了通过代理处理请求的时间,可以在一定程度上提高Redis服务的响应速度。目前流行的代理框架有: predixy:高性能全特征redis代理,支持Redis Sentinel和Redis Cluster。 twemproxy:快速、轻量级memcached和redis代理,Twitter推特公司开发。 codis:redis集群代理解决方案,豌豆荚公司开发,需要修改redis源码。 09 什么是Redis分区? 集群模式会自动对数据进行分区,存储到不同的节点上,集群通过分区来提供一定程度的可用性,当某个节点宕机或者不可达的情况下继续处理请求。 那么集群模式怎么对数据进行分片呢?使用哈希槽。 Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么: 节点A包含0到5500号哈希槽。 节点B包含5501到11000号哈希槽。 节点C包含11001到16384号哈希槽。 如果要添加节点D,可以从节点A, B, C中分出部分槽到D上,如果要移除节点A,可以将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可。由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。 参考资料:https://blog.csdn.net/u0/article/details/ 10 ❤Redis的内存回收机制。 这里需要介绍Redis的近似LRU算法了,它和常规的LRU算法不一样。近似LRU算法通过随机采样淘汰数据,每次随机出5个key,选择最近最少使用的那个进行淘汰。Redis为每个key存储一个24bit的字段,用于存储该key最后一次被访问的时间。 那为什么不使用标准的LRU算法呢? 因为标准LRU需要占更多的内存,标准LRU采用链表,除了kv外,每个node需要保存前后node的地址。 扩展:Redis 3.0对近似算法做了优化:新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。 Redis4.0采用LFU,网上资料很多,这里不再赘述。 11 ❤Redis的key失效机制。 Redis的key失效机制有两种:被动方式和主动方式。 被动方式:当客户端尝试访问该key,如果发现key已经失效了则删除该key,并由服务器告诉客户端已经失效了。 主动方式:存在一些过期key,且客户端不再访问。Redis不会遍历所有的key,这样太浪费时间了,Redis会随机抽取一些key进行校验,如果失效了就删除淘汰。 12 Redis的定时机制是如何实现的。 Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:文件事件(服务器对套接字操作的抽象)和时间事件(服务器对定时操作的抽象)。Redis的定时机制就是借助时间事件实现的。 一个时间事件主要由以下三个属性组成:id:时间事件标识号;when:记录时间事件的到达时间;timeProc:时间事件处理器,当时间事件到达时,服务器就会调用相应的处理器来处理时间。一个时间事件根据时间事件处理器的返回值来判断是定时事件还是周期性事件。 13 ❤如何用Redis实现分布式锁? 分布式锁是用于分布式环境下并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用。如下图所示:
切换为 先拿setnx来抢锁,然后同expire给锁加一个过期时间防止忘记释放。 对应的命令是SET lock_key unique_value NX PX 10000.lock_key 就是 key 键;unique_value 是客户端生成的唯一的标识,区分来自不同客户端的锁操作;NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。 分布式锁的优点:性能高效实现方便,很多研发直接用redis来实现分布式锁。避免单点故障 缺点:超时时间不好设置。如果锁的超时时间设置过长,会影响性能,如果超时时间过短就保护不到共享资源。解决方案:写一个守护线程,然后去判断锁的情况,当锁快失效的时候,再次进行续约加锁,当主线程执行完成后,销毁续约锁即可,不过这种方式实现起来相对复杂。Redis主从模式中,数据是异步复制的,这样导致分布式锁不可靠。如果在 Redis 主节点到锁后,在没有同步到其他节点时,Redis 主节点宕机了,此时新的 Redis 主节点依然可以锁,所以多个应用服务就可以同时到锁。 参考:面试官:如何用 Redis 实现分布式锁? 14 使用过Redis做异步队列么,你是怎么用的?有什么缺点? 一般使用list作为队列,lpush产生消息,lpop消费消息,当lpop没有消息的时候需要sleep一会儿再重试。 缺点: 在消费者下线的情况下,生产的信息会丢失,使用专业的消息队列如Rabbitmq等。 能不能生产一次消费多次? 使用pub/sub主题订阅模式。 15 ❤Redis真的是单线程吗? 单线程指【接收和处理客户请求】由单个线程完成。但其实redis还有其它的线程:Redis 2.6,会在后台启动两个线程,分别处理关闭文件、AOF刷盘这两个任务;Redis 4.0 在以后,新增了一个线程,专门用于异步内存释放(lazyfree)。 16 Redis是单线程的,怎么利用多CPU/核? CPU基本不可能成为的Redis的瓶颈,因为通常Redis受限于内存或网络。例如使用Pipelining,Redis运行在普通的Linux系统上,每秒可以处理50万请求,所以如果你的应用程序主要使用O(N) 或者 O(log(N))命令,几乎不会使用太多的CPU。 然而为了最大限度利用CPU,你可以在一台机器上启动多个Redis实例,并把它们设置为不同服务器。某些时候单个机器是不够的,所以如果你想使用多个CPU,你可以提前考虑使用分片。 关于使用多Redis实例,你可以在Partitioning page找到更多的信息 17 ❤Redis是单线程的,为何如此高效? Redis是基于内存操作,采用高效的数据结构。CPU不可能是Redis的瓶颈,Redis瓶颈更有可能是内存或者网络带宽。Redis采用单线程可以避免线程之间资源竞争,减少线程上下文切换带来的性能开销。Redis采用I/O多路复用来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与Redis服务器中其他同样以单线程运行的模块进行对接,这保持了Redis内部单线程设计的简单性。
切换为居中 18 请问Redis的rehash怎么做的,为什么要渐进rehash,渐进rehash又是怎么实现的? 因为redis是单线程,当K很多时,如果一次性将键值对全部rehash,庞大的计算量会影响服务器性能,甚至可能会导致服务器在一段时间内停止服务。不可能一步完成整个rehash操作,所以redis是分多次、渐进式的rehash。渐进性哈希分为两种: 1)操作redis时,额外做一步rehash 对redis做读取、插入、删除等操作时,会把位于table[dict->rehashidx]位置的链表移动到新的dictht中,然后把rehashidx做加一操作,移动到后面一个槽位。 2)后台定时任务调用rehash 后台定时任务rehash调用链,同时可以通过server.hz控制rehash调用频率 19 Redis主要用途? 计数器 可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。 缓存将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。 会话缓存 可以使用 Redis 来统一存储多台应用服务器的会话信息。当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。 全页缓存(FPC) 除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。 查找表 例如 DNS 记录就很适合使用 Redis 进行存储。查找表和缓存类似,也是利用了Redis快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。 消息队列(发布/订阅功能) List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息。不过最好使用 Kafka、RabbitMQ 等消息中间件。 分布式锁实现 在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。 其它 Set 可以实现交集、并集等操作,从而实现共同好友等功能。ZSet可以实现有序性操作,从而实现排行榜等功能。 20 ❤Redis的QPS能达到多少?怎么做到的? 读写性能优异, Redis 能读的速度是 次/s,写的速度是 81000 次/s(来自官方)。 Redis通过三种方式来实现高吞吐的:高效的数据结构;IO多路复用(epoll);事件机制; 21 有没有方法降低Redis内存使用率? 如果可以的话使用Redis 32位实例。另外,还要善于使用哈希表,列表,有序集合和整数集,因为在特殊情况下Redis使用这些数据类型可以更紧凑存储一些素。可以在内存优化页面更多信息。 22 Redis内存不足时会发生什么?如何改进? Redis要么被Linux内核OOM杀掉,抛出错误崩溃,要么开始变得卡顿。随着现代操作系统malloc方法通常都不返回NULL,而是服务器开始内存交换,因此Redis性能降低,因此你可能会观察到一些错误现象。 INFO命令返回Redis使用内存总量,因此你可以编写脚本监控Redis服务器内存临界值。 Redis内置保护措施允许用户在配置文件中使用maxmemory选项,设置Redis最大占用内存。如果达到此限制,Redis将开始返回错误给写命令(但是将继续接受只读命令),或者当最大内存限制达到时也可以配置为键淘汰,在这种情况下Redis作为缓存使用。 我们有文档描述Redis作为LRU缓存使用。 23 在Linux系统中,即使我有很多空闲内存,后台保存失败报fork()错误! 精简回答:echo 1 > /proc/sys/vm/overcommit_memory 详细回答: Redis后台保存模式依赖现代操作系统的写时拷贝技术。Redis fork(创建一个子进程)是父进程精确拷贝。子进程存储数据到磁盘并且最终退出。从理论上讲,子进程应该和父进程使用同样多内存,作为父进程副本,但是得益于多数现代操作系统实现的写时复制技术,父进程和子进程共享内存页。内存页在父进程或子进程改变时将被复制。当子进程保存时,理论上所有页面都可能改变,Linux无法提前告知子进程需要多少内存,因此如果overcommit_memory设置为0,fork将会失败。除非有足够的空闲RAM真正复制父进程内存页。结果是,如果你有3G Redis数据集,只有2G可用内存将会失败。 overcommit_memory设置为1,意味着Linux 使用更乐观方式fork,这确实是你所期望的Redis。 八 数据库高可用和安全 让你的数据库永不停机! 01 如何保证缓存-数据库一致性。 读的时候先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。 更新的时候先更新缓存,再更新数据库。不然的话,数据库更新数据是新数据,而缓存还是旧数据,出现数据不一致。 删除的时候,先删缓存,再删数据库。 原则:缓存先操作,数据库后操作。 02 ❤什么是缓存穿透?什么是缓存雪崩?什么是缓存击穿?如何避免? 缓存穿透指:大量请求不存在的key导致服务器必须绕过redis去后台数据库查找数据,造成很大的压力。 如何避免: 1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。 2:对一定不存在的key进行过滤,比如使用布隆过滤器。 缓存雪崩:当缓存服务器重启或者大量缓存在同一个时间段失效,这样会给后端系统带来很大的压力,导致服务器崩溃。 如何避免: 1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。 2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期 3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。 缓存击穿:一个存在的key,在缓存过期的瞬间,同时有大量请求,导致对DB访问暴增。 如何避免:设置热点数据永不过期,或者设置互斥锁。 参考资料:缓存雪崩、击穿、穿透,该如何避免? – 云+社区 – 腾讯云 03 ❤SQL注入是什么?如何防范? 所谓SQL注入,就是通过把SQL命令插入到Web表单递交或者输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意SQL命令的目的。 防范方法无外乎以下几种:永远不要相信用户的输入。可以采用正则表达式;永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存储;永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装sql注入的检测方法一般采取辅助软件或网站平台来检测,软件一般采用sql注入检测工具jsky,网站平台就有亿思网站安全平台检测工具。MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻击等。 九 数据库优化 数据库优化,提高性能,降低成本。 01 ❤ 你对数据库优化的理解 数据库的优化可以从数据库层面,缓存方面和SQL语句方面来优化。设计良好的E-R模型,允许部分数据冗余,尽量避免join冗余,提高效率;选择合适的表字段数据类型和存储引擎,适当的添加索引;MySQL主从读写分离;找规律分表,减少表单中的数据量,分区,分库等;添加缓存机制,如Redis,Memcached,APc等;不经常改动的界面,生成静态界面;写高效率SQL,减少使用select * from;根据慢查询日志有针对性的优化 02 ❤该如何优化MySQL的查询? 选取合适的字段类型,比如对于邮政编码或者电话号码,用varchar就不太合适,char(N)更合适一些;使用连接来代替子查询,比如 可以被更有效率的join替代 使用union来代替手动创建的临时表避免使用让索引失效的语句。比如!=或者LIKE %,is null等尽量在where 和 order by 上建立索引。 参考资料:MySQL优化查询速度的方法 03 表中包含几千万条数据该怎么办? 合理设计表结构,使得统计汇总最高效(包括fk设计和用数字id,不用varchar,索引设计,计算字段); 合理分表,使得单表数据规模适当; 用存储器分多个步骤处理。 数据预先处理。 分布在多台server上同时处理。 04 MySQL慢查询日志了解吗? 慢查询日志用来记录在 MySQL 中执行时间超过指定时间的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率低,以便进行优化。 通俗的说,MySQL 慢查询日志是排查问题的 SQL 语句,以及检查当前 MySQL 性能的一个重要功能。如果不是调优需要,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。 参考资料:MySQL慢查询日志(Slow Query Log) 总结 数据库是和生产生活息息相关的,对于企业而言,数据就是生存之本。因此,可以考察的点非常的多。后端开发和测试的岗位应该尤其重视。许多问题都是在长期的复杂的业务实践中出现的,如果不会答也没关系,但要给面试官留下一种独立思考,善于总结的印象,让面试官觉得”未来这人大有可为“。 国外计算机课程 CMU 15445 CMU 15721 cs 245 斯坦福 CS346 华盛顿大学 CSE444
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/28533.html