数据库知识

文章目录

1. 什么是数据库, 数据库管理系统, 数据库系统, 数据库管理员?

数据库 : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。
数据库管理员 : 数据库管理员(Database Administrator, 简称 DBA)负责全面管理和控制数据库系统。
数据库系统 : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。
数据库管理系统 : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。
DBMS

2. 什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性?

元组 : 元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
码 :码就是能唯一标识实体的属性,对应表中的列。
候选码 : 若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
主码 : 主码也叫主键。主码是从候选码中选出来的。 一个实体集中只能有一个主码,但可以有多个候选码。
外码 : 外码也叫外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。
主属性 : 候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门). 显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
非主属性: 不包含在任何一个候选码中的属性称为非主属性。比如在关系——学生(学号,姓名,年龄,性别,班级)中,主码是“学号”,那么其他的“姓名”、“年龄”、“性别”、“班级”就都可以称为非主属性。

3. 主键和外键有什么区别?

主键(主码) :主键用于唯一标识一个元组,不能有重复,不允许为空。一个表只能有一个主键。
外键(外码) :外键用来和其他表建立联系用,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键。

**键字=码字,所以 主键=主码=主关键字,候选键=候选码=候选关键字...**
在这里插入图片描述
 1、码=超键:能够唯一标识一条记录的属性或属性集。
标识性:一个数据表的所有记录都具有不同的超键
非空性:不能为空
有些时候也把码称作“键”
 
 2、候选键=候选码:能够唯一标识一条记录的最小属性集
标识性:一个数据表的所有记录都具有不同的候选键
最小性:任一候选键的任何真子集都不能唯一标识一个记录(比如在成绩表中(学号,课程号)是一个候选键,单独的学号,课程号都不能决定一条记录)
非空性:不能为空
候选键是没有多余属性的超键
举例:学生ID是候选码,那么含有候选码的都是码。
少部分地方也有叫超级码的,但是见得不多

3、主键=主码:某个能够唯一标识一条记录的最小属性集(是从候选码里人为挑选的一条)

唯一性:一个数据表只能有一个主键
标识性:一个数据表的所有记录都具有不同的主键取值
非空性:不能为空
人为的选取某个候选码为主码

4、主属性  包含在任一候选码中的属性称主属性。简单来说,主属性是候选码所有属性的并集

  非主属性  不包含在候选码中的属性称为非主属性。 非主属性是相对于主属性来定义的。

5、外键(foreign key):子数据表中出现的父数据表的主键,称为子数据表的外键。

6、全码:当所有的属性共同构成一个候选码时,这时该候选码为全码。(教师,课程,学生)假如一个教师可以讲授多门课程,某门课程可以有多个教师讲授,学生可以听不同教师讲授的不同课程,那么,要区分关系中的每一个元组,这个关系模式R的候选码应为全部属性构成 (教师、课程、学生),即主码。

4. 为什么不推荐使用外键与级联?

使用外键与级联操作是数据库设计和开发中的一种实践,但是否推荐使用它们取决于具体的情境和需求。下面是一些关于为什么可能不推荐使用外键与级联的考虑:

  1. 性能问题: 级联操作可能引发性能问题,特别是在大型数据库中。当启用级联删除或更新时,数据库可能需要执行大量的额外工作,涉及检查和修改相关的表。这可能导致性能下降,特别是在关系复杂、数据量庞大的情况下。

  2. 维护的复杂性: 使用外键和级联操作可能引入复杂性,使数据库更难以理解和维护。级联操作的影响可能在系统中的多个地方扩散,增加了代码的复杂性和维护的困难度。

  3. 潜在的数据一致性问题: 当使用级联操作时,可能会发生意外的删除或更新,导致数据一致性问题。例如,在级联删除时,可能会不小心删除了依赖表中的数据,而这并不是用户所期望的行为。

  4. 应用层控制: 有些开发者更倾向于在应用层进行控制,而不是依赖数据库的级联操作。这样可以更精确地控制删除和更新的行为,使开发者更容易理解和调试应用程序。

  5. 性能优化的困难: 一旦启用了级联操作,对于性能优化的选择和控制可能受到限制。禁用级联操作可能使得在删除或更新记录时更容易进行性能优化。

虽然存在这些潜在的问题,但使用外键和级联操作在某些情境下仍然是有益的。它们可以确保数据库的一致性,并简化开发者的工作,特别是在处理复杂的数据关系时。决定是否使用外键与级联应该根据具体的项目需求、性能要求和开发团队的偏好来权衡和决策。

5. 什么是 ER 图?

E-R 图 也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。 它是描述现实世界关系概念模型的有效方法。 是表示概念关系模型的一种方式。
下图是一个学生选课的 ER 图,每个学生可以选若干门课程,同一门课程也可以被若干人选择,所以它们之间的关系是多对多(M: N)。另外,还有其他两种关系是:1 对 1(1:1)、1 对多(1: N)。
E-R
我们试着将上面的 ER 图转换成数据库实际的关系模型(实际设计中,我们通常会将任课教师也作为一个实体来处理):
ER

6. 数据库范式了解吗?

这些范式是关系型数据库中的规范化(Normalization)概念,用于设计数据库表结构,减少数据冗余和提高数据一致性。以下是它们的简要概括:

  1. 第一范式(1NF):

    • 数据表中的每一列都包含不可再分的原子数据,即每一列的值都是不可拆分的基本单元。
  2. 第二范式(2NF):

    • 在1NF的基础上,确保表中的非主键列完全依赖于整个主键,而不是仅依赖于主键的一部分。
  3. 第三范式(3NF):

    • 在2NF的基础上,确保表中的非主键列之间没有传递依赖关系。换句话说,非主键列直接依赖于主键,而不是依赖于其他非主键列。
  4. 巴斯 - 科德规范形式(BCNF):

    • 在3NF的基础上,进一步确保每个非主键列对于任何候选键而言都是完全函数依赖的。这意味着表中不会存在对部分候选键而言是部分函数依赖的情况。
  5. 第四范式(4NF):

    • 在BCNF的基础上,处理表中的多值依赖问题。确保多值依赖的数据被适当地分解,以减少冗余和提高数据一致性。

总体而言,这些范式是规范化的逐步过程,目的是设计出结构紧凑、减少冗余、易于维护的数据库表。在实际应用中,根据具体情况可能不需要一次性满足所有范式,而是根据需求和性能权衡来选择适当的规范化水平。

区别和应用场景:
1NF vs. 2NF vs. 3NF:
1NF 主要解决数据的原子性,确保每个字段都是不可再分的原子值。
2NF 解决部分依赖的问题,确保非主键字段完全依赖于主键。
3NF 解决传递依赖,确保非主键字段不依赖于其他非主键字段。
BCNF vs. 4NF:
BCNF 通过对非主键属性的超键进行分解,确保 每个属性完全依赖于键。
4NF 主要关注多值依赖,确保表中没有多值依赖于非超键属性。

什么是数据库内模式、外模式、模式?

在这里插入图片描述

数据库的内模式(Internal Schema)、外模式(External Schema)和模式(Schema)是数据库设计中的三个重要概念,它们用于描述数据库的不同层次和抽象级别。

  1. 内模式(Internal Schema)

    • 定义:内模式是数据库的最低级别,描述了数据在物理存储层面上的组织和表示方式。它包括了存储在磁盘上的数据结构、索引、存储方式等细节。
    • 目的:提供了数据库引擎和存储系统的实现细节,对于数据库管理系统(DBMS)的开发者和维护者而言,允许对数据库的物理实现进行管理和优化。
  2. 外模式(External Schema)

    • 定义:外模式是数据库的中间层,描述了用户或应用程序对数据库的某个特定部分的视图。它定义了用户或应用程序能够看到和操作的数据。
    • 目的:提供了对数据库的高级抽象,使得用户和应用程序能够以更简单、更易理解的方式与数据库进行交互。外模式隐藏了内部的实现细节,使得用户无需关心底层的物理结构。
  3. 模式(Schema)

    • 定义:模式是数据库的逻辑结构描述,它定义了数据库中所有数据和关系的总体结构。模式包括了所有的表、字段、关系、约束等元素的定义。
    • 目的:提供了对数据库的逻辑视图,为数据库的设计者和管理员定义了数据的整体结构,使其能够理解和管理数据库的逻辑层面。
  • 1)定义外模式(局部逻辑结构)与模式(全局逻辑结构)之间的对应关系,映象定义通常包含在各自外模式的描述中,
    每一个外模式,数据库系统都有一个外模式/模式映象。
    用途:保证数据的逻辑独立性
    当模式改变时,数据库管理员修改有关的外模式/模式映象,使外模式保持不变
    应用程序是依据数据的外模式编写的,从而应用程序不必修改,保证了数据与程序的逻辑独立性,简称数据
    的逻辑独立性。
  • 2)
    模式/内模式映像(空间利用率,存取效率)
    模式/内模式映像是唯一的,它定义了数据全局逻辑结构与存储结构之间的对应关。数据库中模式/内模式映
    象是唯一的。该映象定义通常包含在模式描述中。
    用途:保证数据的物理独立性
    当数据库的存储结构改变了(例如选用了另一种存储结构),数据库管理员修改模式/内模式映象,使模式保
    持不变。应用程序不受影响。保证了数据与程序的物理独立性,简称数据的物理独立性。
    优点:
    (1)保证了数据库外模式的稳定性。
    (2)从底层保证了应用程序的稳定性,除非应用需求本身发生变化,否则应用程序一般不需要修改。
    (3)数据与程序之间的独立性,使得数据的定义和描述可以从应用程序中分离出去。

为什么这么设计?

这三个层次的设计有以下优点:

  • 数据独立性: 通过将数据的不同层次进行抽象,实现了数据的独立性。内模式的变化不会影响到外模式,使得数据库的物理实现可以灵活变化而不影响用户和应用程序。

  • 安全性: 外模式限制了用户或应用程序对数据库的访问权限,防止了对敏感信息的未授权访问。模式的定义也可以包含访问控制信息,进一步增强了数据库的安全性。

  • 易用性: 外模式提供了更高层次的抽象,使用户和应用程序能够以更简单的方式与数据库交互。这样设计提高了数据库的易用性,减少了用户和应用程序对底层实现的理解需求。

  • 维护性: 内模式的变化不会影响到外模式,这种分层设计使得数据库的维护更为灵活。可以对底层实现进行优化和改进,而不影响用户和应用程序的正常使用。

综合而言,内模式、外模式和模式的分层设计有助于提高数据库的灵活性、安全性、易用性和维护性,是数据库管理系统设计的重要原则之一。

7. 什么是存储过程?

我们可以把存储过程看成是一些 SQL 语句的集合,中间加了点逻辑控制语句。存储过程是一组在数据库中预先定义并存储在数据库管理系统中的 SQL 语句集合。它们可以被视为数据库中的一种批处理或脚本,通常执行一系列操作,另外,使用存储过程比单纯 SQL 语句执行要快,因为存储过程是预编译过的。

8. drop、delete 与 truncate 区别?

用法不同
drop(丢弃数据):drop table 表名,直接将表都删除掉,在删除表的时候使用。
truncate (清空数据):truncate table 表名,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。
delete(删除数据):delete from 表名 where 列名=值,删除某一行的数据,如果不加 where 子句和 truncate table 表名作用类似。
truncate 和 delete 只删除数据不删除表的结构(定义),执行 drop 语句,此表的结构也会删除
属于不同的数据库语言
truncate 和 drop 属于 DDL (数据定义语言)语句,操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。而 delete 语句是 DML (数据库操作语言)语句,这个操作会放到 rollback segement 中,事务提交之后才生效。
DML 语句和 DDL 语句区别:
DML 是数据库操作语言(Data Manipulation Language)的缩写,是指对数据库中表记录的操作,主要包括表记录的插入(insert)、更新(update)、删除(delete)和查询(select),是开发人员日常使用最频繁的操作。
DDL (Data Definition Language)是数据定义语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改的操作语言。它和 DML 语言的最大区别是 DML 只是对表内部数据的操作,而不涉及到表的定义、结构的修改,更不会涉及到其他对象。DDL 语句更多的被数据库管理员(DBA)所使用,一般的开发人员很少使用。
执行速度不同
drop > truncate > delete

9. 数据库设计通常分为哪几步?

  • 需求分析 : 分析用户的需求,包括数据、功能和性能需求。
  • 概念结构设计 : 主要采用 E-R 模型进行设计,包括画 E-R 图。
  • 逻辑结构设计 : 通过将 E-R 图转换成表,实现从 E-R 模型到关系模型的转换。
  • 物理结构设计 : 主要是为所设计的数据库选择合适的存储结构和存取路径。
  • 数据库实施 : 包括编程、测试和试运行
  • 数据库的运行和维护 : 系统的运行与数据库的日常维护。
    字符集
    字符集 就是一系列字符的集合。
    常见的字符集有 ASCII、GB2312、GBK、GB18030、BIG5、UTF-8…。
    ASCII:128 个字符
    GB2312:6700 多个汉字
    GBK:20000 多个汉字
    GB18030:最全面的汉字字符集
    BIG5:繁体中文
    utf8: utf8编码只支持1-3个字节 。
    utf8mb4:UTF-8 的完整实现,存储 emoji 符号。

#MySQL知识点&面试题总结

  1. MySQL 基础

1. 关系型数据库介绍

关系型数据库就是一种建立在关系模型的基础上的数据库。
关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。
关系型数据库中,我们的数据都被存放在了各种表中(比如用户表),表中的每一行就存放着一条数据(比如一个用户的信息)。
DB
大部分关系型数据库都使用 SQL 来操作数据库中的数据。并且,大部分关系型数据库都支持事务的四大特性(ACID)。
(MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是用的 SQLite))

2. MySQL 介绍

MySQL 是一种关系型数据库,主要用于持久化存储我们的系统中的一些数据比如用户信息。

存储引擎

1. 存储引擎相关的命令

查看 MySQL 提供的所有存储引擎

mysql> show engines;
1
MySQL 当前默认的存储引擎是 InnoDB,并且在 5.7 版本所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。
查看 MySQL 当前默认的存储引擎

mysql> show variables like ‘%storage_engine%’;
1
查看表的存储引擎

show table status like “table_name” ;
1

2. MyISAM 和 InnoDB 的区别

是否支持行级锁
MyISAM 只有表级锁(table-level locking)(一锁就是锁住了整张表)
InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
是否支持事务
MyISAM 不提供事务支持。
InnoDB 提供事务支持,具有提交(commit)和回滚(rollback)事务的能力。
是否支持外键
MyISAM 不支持,而 `InnoDB 支持。
是否支持数据库异常崩溃后的安全恢复
MyISAM 不支持,而 InnoDB 支持(依赖于 redo log)。

3. 锁机制与 InnoDB 锁算法

表级锁:资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
行级锁: 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
InnoDB 存储引擎的锁的算法有三种:
Record lock:记录锁,单个行记录上的锁
Gap lock:间隙锁,锁定一个范围,不包括记录本身
Next-key lock:record+gap 临键锁,锁定一个范围,包含记录本身

  1. 区别:
    范围:Gap lock锁定的是一个范围,而Next-Key lock既锁定范围内的数据行,也锁定下一个可能插入的键值。
    适用场景:Gap lock通常用于防止幻读,而Next-Key lock更全面,可以同时防止幻读和插入的问题。
    性能影响:Next-Key lock相对于Gap lock来说,会引入更多的锁,可能对性能产生一些影响。因此,在实际应用中需要权衡隔离性和性能之间的关系。
    在事务并发控制中,选择适当的锁机制取决于应用的需求和性能的考虑。 Gap lock和Next-Key lock在MySQL数据库中的使用为开发人员提供了更灵活的工具,以确保数据的一致性和隔离性。

锁机制是数据库管理系统用于管理并发访问的一种重要手段,而InnoDB是MySQL数据库中常用的存储引擎之一,它使用了多种锁算法来实现事务的隔离性。以下是关于锁机制和InnoDB锁算法的简要介绍:

锁机制:

数据库锁分为共享锁和排他锁,用于控制对数据库中数据的并发访问。

  1. 共享锁(Shared Lock) S锁

    • 允许多个事务同时持有相同的锁。
    • 主要用于读操作,多个事务可以同时读取相同的数据。
  2. 排他锁(Exclusive Lock) X锁

    • 一次只允许一个事务持有该锁。
    • 用于写操作,确保在写入时不会有其他事务同时读取或写入相同的数据。

在数据库中,通常有两种常见的锁,即共享锁(S锁)和排他锁(X锁)。这两者之间存在一些规则,通常不能同时加上,因为它们代表了不同的锁定级别。

  1. S锁和X锁互斥: 通常情况下,一个事务在某个数据项上持有X锁时,其他事务就不能再获得该数据项上的S锁,反之亦然。这是因为X锁是排他锁,表示对数据项的独占访问,而S锁是共享锁,表示对数据项的共享访问。互斥关系确保了对同一数据项的并发事务之间的数据一致性。

  2. 同一事务可以同时持有S锁和X锁: 一个事务在执行的过程中,可能需要对同一个数据项既进行共享访问(S锁),又进行独占访问(X锁)。这通常是通过先获取S锁,然后升级为X锁的方式来实现的。但是,一旦获得了X锁,就不能再获取S锁,因为X锁代表了更高的锁定级别。

总体而言,S锁和X锁之间是有互斥关系的,一个事务在同一时间点要么持有S锁,要么持有X锁。这是为了确保对数据的并发访问不会导致数据不一致或丢失更新。

InnoDB存储引擎采用了多种锁算法来实现事务的隔离性。以下是InnoDB中常见的锁算法:

  1. 共享锁和排他锁(Shared and Exclusive Locks)

    • InnoDB使用共享锁和排他锁来实现事务的隔离性。共享锁用于读操作,排他锁用于写操作。多个事务可以同时持有共享锁,但只允许一个事务持有排他锁。
  2. 行级锁(Row-Level Locking)

    • InnoDB采用行级锁,使锁定的范围更小,提高了并发性。行级锁可以在一行数据上进行加锁,而不是锁定整个表或页。
  3. 事务的隔离级别

    • InnoDB支持多个事务隔离级别,包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。每个隔离级别有不同的锁定策略。
  4. 间隙锁(Gap Locks)

    • 用于防止幻读。当事务执行范围查询时,InnoDB可能在查询的范围之间设置间隙锁,以防止其他事务在这个范围内插入新的数据。
  5. Next-Key Locks

    • 一种综合了行级锁和间隙锁的锁机制。Next-Key Locks同时锁定一个范围内的数据行和下一个可能插入的键值,以防止幻读和插入的问题。
  6. 死锁检测

    • InnoDB实现了死锁检测机制,当检测到死锁时,系统会选择回滚其中的某个事务,以解开死锁。
  7. 自适应哈希索引互斥锁

    • InnoDB引入了自适应哈希索引,对其进行修改时,可能涉及到互斥锁,以保证并发修改的正确性。

这些锁算法和机制使得InnoDB能够在高并发的环境下提供可靠的事务隔离性,同时尽量减小锁的粒度,提高并发性能。选择适当的隔离级别和了解InnoDB的锁机制对于设计和优化数据库事务操作非常重要。

4. 查询缓存

执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除)。
my.cnf 加入以下配置,重启 MySQL 开启查询缓存

query_cache_type=1
query_cache_size=600000
1
2
MySQL 执行以下命令也可以开启查询缓存

set global query_cache_type=1;
set global query_cache_size=600000;
1
2
还可以通过 sql_cache 和 sql_no_cache 来控制某个查询语句是否需要缓存:

select sql_no_cache count(*) from usr;
1
开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果。
查询缓存不命中的三种情况:
(1)因此任何两个查询在任何字符上的不同都会导致缓存不命中。
(2)如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果也不会被缓存。
(3)缓存建立之后,MySQL 的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。

缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。

事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。

  1. 何为数据库事务?
    数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循:要么全部执行成功,要么全部不执行 。

开启一个事务

START TRANSACTION;

2. 何为 ACID 特性呢?

关系型数据库(例如:MySQL、SQL Server、Oracle 等)事务都有 ACID 特性:

原子性(Atomicity) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;(undo log (回滚日志) )
一致性(Consistency): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;(锁机制、MVCC )
持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。( redo log (重做日志) )

事务的ACID特性是指事务在数据库管理系统中必须具备的四个基本属性,这些属性确保了事务的可靠性、一致性和隔离性。ACID是以下四个属性的首字母缩写:

原子性(Atomicity):

定义:事务是一个原子操作,它要么全部执行,要么完全不执行。如果在事务执行过程中发生错误或中断,系统必须保证事务的所有操作都被回滚(撤销),不留部分执行的痕迹。
例子:转账操作中,从一个账户扣除一定金额并将其添加到另一个账户,要么两者都成功执行,要么都不执行。
一致性(Consistency):

定义:事务将数据库从一种一致性状态转移到另一种一致性状态。一致性的概念因应用而异,但必须符合数据库预定义的规则和约束。
例子:在银行转账中,一致性要求在转账前后,各个账户的总余额应该保持一致。
隔离性(Isolation):

定义:并发执行的事务之间应该是相互隔离的,一个事务的执行不应影响其他事务的执行。事务之间应该是并发独立的。
例子:当两个事务同时访问数据库时,一个事务的修改在另一个事务提交之前对其他事务不可见,防止读取到不一致的数据。
持久性(Durability):

定义:一旦事务被提交,其结果就应该是永久性的,即使系统崩溃或重新启动,事务的提交结果也不应丢失。
例子:在购物网站中,用户的订单信息一旦提交成功,应该持久保存,以确保即使系统发生故障,订单数据不会丢失。
这些ACID特性确保了数据库管理系统中事务的可靠性和一致性,使得在并发执行的情况下,数据库能够保持稳定和可靠的状态。 ACID属性对于许多应用场景,尤其是对于要求高度可靠性和数据完整性的应用,都是至关重要的。

3. 数据事务的实现原理呢?(以 MySQL 的 InnoDB 引擎为例)

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

4. 并发事务带来哪些问题?

脏读(Dirty read):当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。一个事务对数据进行修改,还没提交;另一个事务就访问了未提交的数据
丢失修改(Lost to modify):指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。一个事务对数据进行修改;另一个事务对同一个数据也进行了修改

幻读(Phantom read):它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。一个事务对数据进行访问;另一个事务对同一个数据也进行插入操作, 第一个事务再次查询时,发现了一些原本不存在的纪录
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次查询同一条查询语句(DQL)时,记录发现记录增多或减少了。

并发事务是指多个事务同时执行的情况,这在数据库系统中是常见的情况。然而,并发事务可能引发一些问题,其中一些主要的问题包括:

  1. 丢失更新(Lost Updates)

    • 问题描述: 多个事务同时对相同的数据进行读取和修改,最终只有一个事务的修改生效,而其他事务的修改被覆盖,导致数据的不一致。
    • 解决方法: 使用锁机制(如行级锁)或乐观并发控制(如版本控制)来确保数据的一致性。
  2. 脏读(Dirty Read)

    • 问题描述: 一个事务读取了另一个事务未提交的数据,当未提交的事务回滚时,读取的数据就是无效或错误的。
    • 解决方法: 使用事务隔离级别(如读已提交)来避免脏读的发生。
  3. 不可重复读(Non-Repeatable Read)

    • 问题描述: 一个事务在两次读取相同的数据之间,另一个事务修改了该数据,导致第一个事务两次读取的数据不一致。
    • 解决方法: 使用事务隔离级别(如可重复读)来避免不可重复读的问题。
  4. 幻读(Phantom Read)

    • 问题描述: 一个事务在两次查询之间,另一个事务插入了新的数据,导致第一个事务两次查询的结果不一致。
    • 解决方法: 使用事务隔离级别(如可重复读)或锁机制来避免幻读的问题。
  5. 死锁(Deadlock)

    • 问题描述: 多个事务相互等待对方释放资源,导致所有事务无法继续执行。
    • 解决方法: 使用死锁检测和回滚策略,或者通过合理的事务设计来避免死锁的发生。

这些并发事务可能引发的问题涉及到事务隔离性、数据一致性和性能等方面。数据库管理系统通过提供不同的事务隔离级别、锁机制和并发控制策略来解决这些问题,开发者在设计数据库应用时需要考虑这些问题并选择适当的解决方案。

5. 事务隔离级别有哪些?

READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

事务的隔离级别是数据库管理系统中用于控制事务之间相互影响程度的一个重要概念。隔离级别定义了一个事务内部的操作对其他事务的可见性,包括读取和写入的影响。标准的SQL定义了四个事务隔离级别,从低到高分别是:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。

以下是这些隔离级别的主要区别:

  1. 读未提交(Read Uncommitted)

    • 特点: 最低的隔离级别,事务中的修改对其他事务是可见的。
    • 问题: 可能导致脏读、不可重复读和幻读的问题。
  2. 读已提交(Read Committed)

    • 特点: 一个事务提交后,其修改对其他事务才变为可见。
    • 问题: 可能导致不可重复读和幻读的问题。
  3. 可重复读(Repeatable Read)

    • 特点: 事务开始时读取的数据在事务结束之前不发生变化,其他事务不能插入新的数据。
    • 问题: 可能导致幻读的问题。
  4. 串行化(Serializable)

    • 特点: 最高的隔离级别,确保事务串行执行,避免了脏读、不可重复读和幻读的问题。
    • 问题: 可能导致性能下降,因为事务无法并发执行。

具体区别:

  • 可见性范围:

    • 读未提交:事务对其他事务的修改立即可见。
    • 读已提交:事务对其他事务的修改在提交后才可见。
    • 可重复读:事务内部读取的数据在事务结束前都不发生变化,其他事务不能插入新数据。
    • 串行化:事务串行执行,确保最高的隔离性。
  • 并发性能:

    • 读未提交:最高并发性,但可能导致各种问题。
    • 读已提交:中等并发性,相对较好的平衡。
    • 可重复读:较好的平衡,一定程度上避免了一些问题。
    • 串行化:最低的并发性,事务串行执行可能导致性能下降。
  • 问题风险:

    • 读未提交:可能导致脏读、不可重复读和幻读。
    • 读已提交:可能导致不可重复读和幻读。
    • 可重复读:可能导致幻读。
    • 串行化:避免了脏读、不可重复读和幻读。

选择隔离级别时需要权衡隔离性和并发性能,并根据具体应用的需求来确定合适的隔离级别。

隔离级别 \ 问题脏读不可重复读幻读
读未提交
读已提交
可重复读
串行化
  • ✅ 表示该隔离级别 带来的问题。
  • ❌ 表示该隔离级别 不会带来的问题。

6. MySQL 的默认隔离级别是什么?

MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。
并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁读使用到的机制就是 Next-Key Locks (record+gap 临键锁,锁定一个范围,包含记录本身)。

因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 READ-COMMITTED (读取提交内容) ,但是你要知道的是 InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读) 并不会有任何性能损失。

Redis 知识点&面试题总结

1. 简单介绍一下 Redis

C 语言开发的数据库;存在内存中的 ,也就是它是内存数据库,所以读写速度非常快。
Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。

Redis(Remote Dictionary Server)是一款高性能的开源内存数据库,被广泛应用于各种应用场景。以下是一些使用Redis的主要原因:

  1. 快速的读写速度: Redis的数据存储在内存中,因此具有非常快速的读写速度。这使得Redis成为处理高并发请求和对延迟敏感的应用的理想选择。

  2. 丰富的数据结构支持: Redis支持多种丰富的数据结构,包括字符串、哈希、列表、集合、有序集合等。这种灵活性使得Redis不仅可以用作简单的键值对存储,还可以支持更复杂的数据操作,如计数器、排行榜等。

  3. 持久化支持: Redis支持将数据持久化到磁盘,以防止数据丢失。通过RDB(Redis Database File)和AOF(Append-Only File)两种持久化方式,可以根据需求选择合适的策略。

  4. 多种应用场景: Redis适用于多种应用场景,包括缓存系统、会话存储、消息队列、实时分析、计数器、地理位置服务等。其灵活性和高性能使得它成为各种应用的通用数据存储解决方案。

  5. 原子性操作: Redis支持原子性操作,能够保证某些操作的原子性,如递增、递减、集合操作等。这有助于避免并发冲突和确保数据的一致性。

  6. 分布式支持: Redis支持分布式架构,可以通过主从复制和Sentinel(哨兵)机制实现高可用性和故障恢复。这使得Redis在大规模应用和分布式系统中具有优势。

总体而言,Redis以其出色的性能、灵活的数据结构和多种应用场景的支持,成为了广泛应用于缓存、实时数据处理和分布式系统等领域的首选解决方案。

2. 分布式缓存常见的技术选型方案有哪些?

Memcached 和 Redis。
分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用信息的问题。因为,本地缓存只在当前服务里有效,比如如果你部署了两个相同的服务,他们两者之间的缓存数据是无法共同的。

  • Redis:

特点: Redis是一款开源的内存数据库,支持丰富的数据结构,包括字符串、哈希、列表、集合等。除了缓存功能,还可以用作消息队列、发布/订阅系统等。
优势: 高性能、支持持久化、丰富的数据结构。

  • Memcached:

特点: Memcached是一款分布式内存对象缓存系统,主要用于缓存键/值对。它简单而高效,适用于大规模的分布式缓存需求。
优势: 简单易用、高性能。

3. Redis 和 Memcached 的区别和共同点

共同点:
d

  1. 都是基于内存的数据库,一般都用来当做缓存使用。
  2. 都有过期策略。
  3. 两者的性能都非常高。

区别:

  1. Redis 支持更丰富的数据类型(支持更复杂的应用场景)。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
  2. Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 把数据全部存在内存之中。
  3. Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。
    Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。
    Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的。
    Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 引入了多线程 IO )
  4. Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。
  5. Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。

Redis和Memcached都是流行的分布式内存缓存系统,它们有一些区别和共同点:

共同点:

  1. 内存缓存: Redis和Memcached都是基于内存的缓存系统,数据存储在内存中,因此具有快速读写的特性。

  2. 键值存储: 两者都采用键值对的方式存储数据,通过唯一的键来检索数据。

  3. 分布式支持: Redis和Memcached都支持分布式架构,可以通过横向扩展添加节点以处理更大的数据量和更高的并发。

  4. 开源: Redis和Memcached都是开源软件,拥有活跃的社区支持和广泛的文档资源。

区别:

  1. 数据类型支持:

    • Redis: 提供丰富的数据类型支持,包括字符串、哈希、列表、集合、有序集合等。这使得Redis不仅可以用作缓存系统,还可以支持更复杂的数据操作。
    • Memcached: 主要支持简单的键值对存储,不提供复杂的数据类型支持。
  2. 持久化:

    • Redis: 支持数据持久化,可以将数据保存到磁盘,以防止数据丢失。
    • Memcached: 不提供数据持久化功能,一旦服务重启,缓存数据将丢失。
  3. 数据淘汰策略:

    • Redis: 提供多种数据淘汰策略,如LRU(Least Recently Used)、LFU(Least Frequently Used)等,用于在内存不足时确定要删除的数据。
    • Memcached: 使用简单的LRU策略,当内存不足时,删除最近最少使用的数据。
  4. 复制和高可用性:

    • Redis: 支持主从复制和Sentinel(哨兵)机制,以实现高可用性和故障恢复。
    • Memcached: 需要借助其他工具或自行实现复制和高可用性。
  5. 数据存储方式:

    • Redis: 除了内存存储,还支持持久化到磁盘,以及通过RDB(Redis Database File)和AOF(Append-Only File)两种方式。
    • Memcached: 不提供数据持久化,数据仅存在于内存中。

总体而言,选择Redis还是Memcached取决于具体的应用场景和需求。如果需要更复杂的数据结构和支持持久化,且能够接受稍微复杂的配置和管理,可以选择Redis。如果只需简单的键值对缓存,追求简单和高性能,可以选择Memcached。

4. 缓存数据的处理流程是怎样的?

在这里插入图片描述

如果用户请求的数据在缓存中就直接返回。
缓存中不存在的话就看数据库中是否存在。
数据库中存在的话就更新缓存中的数据。
数据库中不存在的话就返回空数据。

5. 为什么要用 Redis/为什么要用缓存?

简单,来说使用缓存主要是为了提升用户体验以及应对更多的用户。
高性能:保证用户下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。
不过,要保持数据库和缓存中的数据的一致性。 如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
高并发:
直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高了系统整体的并发。
(一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 redis 的情况,redis 集群的话会更高)。)
(QPS(Query Per Second):服务器每秒可以执行的查询次数。)

6. Redis 除了做缓存,还能做什么?

分布式锁
限流:Redis 和 lLua 脚本
消息队列:Redis 自带的 list 数据结构可以作为一个简单的队列使用。
复杂业务场景

除了作为缓存系统之外,Redis还可以用于许多其他用途,充分发挥其高性能、灵活的数据结构和支持多种操作的特点。以下是一些Redis的其他用途:

  1. 消息队列(Message Queue): Redis的发布订阅(Pub/Sub)机制和列表数据结构可用于构建轻量级的消息队列系统。生产者可以将消息发布到一个通道,而消费者则订阅并接收消息。

  2. 任务队列(Task Queue): Redis的列表数据结构和原子性操作可用于构建任务队列,用于异步处理任务。生产者将任务推送到队列,而消费者则从队列中取出并执行任务。

  3. 分布式锁: Redis的原子性操作和数据结构可用于实现分布式锁,确保在分布式系统中对共享资源的访问是原子的。

  4. 持久化存储: 除了作为缓存外,Redis还可以用作持久化存储。通过将数据保存到磁盘,可以实现数据的持久化,防止数据丢失。

  5. 分布式计算: Redis支持分布式数据结构和计算,可以在多个节点上执行复杂的计算任务。

  6. 实时数据更新: Redis的快速读写速度使其适用于实时数据更新场景,如在线游戏中的实时状态更新、实时通知等。

  7. 实时分析(Real-time Analytics): Redis的有序集合(Sorted Set)和计数器数据结构可用于实时分析场景,如排行榜、计数器统计等。这使得Redis成为处理实时数据的理想选择。

这些用途展示了Redis的多功能性和适用性,使其成为一个灵活且强大的数据存储和处理工具。根据具体应用的需求,可以充分利用Redis的特性来解决各种问题。

7. Redis 没有使用多线程?为什么不使用多线程?

Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主处理之外的其他线程来“异步处理”。

Redis6.0 之前 为什么不使用多线程?
单线程编程容易并且更容易维护;
Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
Redis6.0 之后为何引入了多线程?
主要是为了提高网络 IO 读写性能。(性能瓶颈(Redis 的瓶颈主要受限于内存和网络))
虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了,执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。

8. Redis 给缓存数据设置过期时间有啥用?

因为内存是有限的,如果缓存中的所有数据都是一直保存的话,分分钟直接 Out of memory。
(其他作用如验证码/登录信息等)

9. Redis 是如何判断数据是否过期的呢?

Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。
过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。

10. 过期的数据的删除策略了解么?

常用的过期数据的删除策略就两个

惰性删除:只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。(对CPU好)
定期删除:每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。(对内存好)
Redis 采用的是 定期删除+惰性删除。Redis 内存淘汰机制。

11. Redis 内存淘汰机制了解么?

Redis 提供 6 种数据淘汰策略:

volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
4.0 版本后增加以下两种:

volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰

allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

Redis使用内存淘汰机制来处理内存空间不足的情况,以确保系统的稳定性。当内存不足时,Redis通过淘汰一些键值对来释放内存。以下是Redis的常见内存淘汰机制:

  1. LRU(Least Recently Used): 最近最少使用。Redis会根据键的最近使用时间来选择最久未使用的键进行淘汰。

  2. LFU(Least Frequently Used): 最不经常使用。Redis会根据键的使用频率来选择使用频率最低的键进行淘汰。

  3. TTL(Time To Live): 生存时间。对于设置了过期时间的键,当键的生存时间到期时,Redis会将其淘汰。

  4. 随机淘汰: Redis也提供了一种随机选择键进行淘汰的方法。这种方法相对简单,但可能导致一些热门数据被随机淘汰。

这些淘汰机制可以单独使用,也可以结合使用。配置内存淘汰策略可以在Redis的配置文件中进行,通过设置maxmemory-policy参数来选择使用哪种淘汰策略。

示例配置文件中的一部分:

# 设置最大内存
maxmemory 2GB
# 设置淘汰策略为LRU
maxmemory-policy allkeys-lru

在上述示例中,Redis的最大内存限制为2GB,采用LRU淘汰策略。当内存使用达到最大限制时,Redis将根据LRU规则淘汰一些键值对以释放内存。

需要注意的是,选择合适的淘汰策略取决于具体的应用场景和数据访问模式。不同的淘汰策略可能对性能和数据命中率产生不同的影响。

12. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)

快照(snapshotting)持久化(RDB)(默认)
Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。
Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。

AOF(append-only file)持久化
与快照持久化相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存 server.aof_buf 中,然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。
为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。

Redis提供两种主要的持久化机制,用于在系统重启时保存数据,确保数据不会丢失。这两种机制分别是:

  1. RDB(Redis DataBase): RDB 是一种快照(snapshot)持久化机制,它定期将整个数据集保存到磁盘。RDB 是一个压缩过的二进制文件,包含了某个时间点上所有键值对的快照。可以通过配置文件中的 save 指令设置保存快照的条件,也可以手动触发保存。

    • 优点: RDB 适用于备份和恢复整个数据集,且生成的文件相对较小,对于大规模数据集的备份较为高效。
    • 缺点: 如果系统在持久化发生之间崩溃,可能会丢失最后一次快照之后的修改。

    配置示例:

    save 900 1       # 在900秒(15分钟)内,如果至少有1个键被修改,则执行快照
    save 300 10      # 在300秒(5分钟)内,如果至少有10个键被修改,则执行快照
    save 60 10000    # 在60秒内,如果至少有10000个键被修改,则执行快照
    
  2. AOF(Append-Only File): AOF 是一种追加文件持久化机制,它以日志形式记录每个写操作的命令。当系统需要重启时,可以通过重新执行这些命令来还原数据。AOF 文件以文本形式保存,内容是Redis协议格式的命令。

    • 优点: AOF 提供了更精确的持久化,即使系统在持久化发生之间崩溃,也可以通过执行AOF文件中的命令来恢复数据。
    • 缺点: AOF 文件相对较大,可能会占用更多的磁盘空间。

    配置示例:

    appendonly yes     # 启用AOF持久化
    appendfsync everysec   # 每秒钟执行一次同步操作
    

可以选择同时使用RDB和AOF,以提供额外的数据安全性。在配置文件中同时开启这两种持久化机制的示例:

save 900 1
save 300 10
save 60 10000
appendonly yes

这样配置后,Redis会定期保存RDB快照,同时将写操作追加到AOF文件中。在系统重启时,可以通过加载RDB文件和重新执行AOF文件中的命令来还原数据。

13. Redis bigkey

bigkey:简单来说,如果一个 key 对应的 value 所占用的内存比较大。会消耗更多的内存空间,bigkey 对性能也会有比较大的影响。
如何发现 bigkey?
1、使用 Redis 自带的 --bigkeys 参数来查找。
2、分析 RDB 文件。前提是RDB持久化,现成代码套用。

14. Redis 事务

Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能。

MULTI :开始 EXEC:执行 DISCARD:取消 WATCH:监听指定的键
(被 WATCH 命令监视的键被修改的话,整个事务都不会执行,直接返回失败。)
使用 MULTI 命令后可以输入多个命令。Redis 不会立即执行这些命令,而是将它们放到队列,当调用了 EXEC 命令将执行所有命令。
这个过程是这样的:

开始事务(MULTI)。
命令入队(批量操作 Redis 的命令,先进先出(FIFO)的顺序执行)。
执行事务(EXEC)。
Redis 是不支持 roll back 的,因而不满足原子性的(而且不满足持久性)。ACID

15. Redis 可以做消息队列么?

Redis 5.0 新增加的一个数据结构 Stream 可以用来做消息队列,Stream 支持:

发布 / 订阅模式
按照消费者组进行消费
消息持久化( RDB 和 AOF)
和专业的消息队列相比,还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。
选择市面上比较成熟的一些消息队列比如 RocketMQ、Kafka。
2. 消息队列(Message Queue): Redis的发布订阅(Pub/Sub)机制和列表数据结构可用于构建轻量级的消息队列系统。生产者可以将消息发布到一个通道,而消费者则订阅并接收消息。

16. 缓存穿透

大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。
有哪些解决办法?
1)缓存无效 key
如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间
2)布隆过滤器
把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。
布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。

当一个元素加入布隆过滤器中的时候,会进行哪些操作:

使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
根据得到的哈希值,在位数组中把对应下标的值置为 1。

17. 缓存雪崩

缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。
这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。
还有一种缓存雪崩的场景是:有一些被大量访问数据(热点缓存)在某一时刻大面积失效,导致对应的请求直接落到了数据库上。(如秒杀活动)

针对 Redis 服务不可用的情况:

采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
限流,避免同时处理大量的请求。
针对热点缓存失效的情况:

设置不同的失效时间比如随机设置缓存的失效时间。
缓存永不失效。

缓存穿透(Cache Penetration):

缓存穿透是指查询一个在缓存中不存在的、数据库中也不存在的数据,导致每次针对这个查询都要穿透缓存到达数据库,从而浪费系统资源。攻击者可通过构造恶意请求,大量查询不存在的数据,使缓存失效,直接查询数据库,导致数据库压力过大。

解决方案:

  1. Bloom Filter: 使用布隆过滤器,它可以快速判断某个数据是否存在,从而避免对数据库的频繁查询。
  2. 空值缓存: 将查询结果为空的键设置为一个特殊值(如null),并将其缓存起来,有效期设置短暂,避免频繁查询。

缓存雪崩(Cache Avalanche):

缓存雪崩是指缓存中的大量数据在同一时间失效,导致大量请求直接打到数据库,引起数据库压力过大,甚至宕机。通常发生在缓存中的大量键具有相同的过期时间时。

解决方案:

  1. 随机过期时间: 设置缓存的过期时间时,加上一个随机值,使得缓存的过期时间分散,避免大量缓存同时失效。
  2. 持久化缓存: 对于热点数据,可以考虑不设置过期时间,让其持久化存在,避免大量同时失效。
  3. 限流和降级: 在缓存失效时,对数据库的访问进行限流,避免同时大量请求打到数据库,可以考虑提供默认值或者返回错误信息,降低数据库压力。

综合来看,合理设置缓存的过期时间、使用布隆过滤器、对热点数据持久化、限流和降级策略等方法可以有效解决缓存穿透和缓存雪崩的问题。

18. 如何保证缓存和数据库数据的一致性?

如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:

缓存失效时间变短(不推荐,治标不治本) :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
增加 cache 更新重试机制(常用): 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。

保证缓存和数据库数据的一致性是非常重要的,特别是在使用缓存来加速数据访问时。以下是一些常用的方法来确保缓存和数据库数据的一致性:

  1. 读写时双写: 在更新数据库的同时,更新缓存。这确保了数据库和缓存中的数据一致。这种方式适用于写操作较少的情况。

  2. 缓存失效策略: 当数据库数据发生变化时,及时失效相应的缓存。这可以通过在数据库更新时主动使缓存失效,或者通过定时任务定期检查数据库变化并更新缓存。

  3. 数据库回写策略: 当写操作发生时,先更新数据库,再更新缓存。这样可以保证数据库和缓存的数据一致性,但会增加写操作的响应时间。

  4. 事件驱动更新: 使用消息队列或发布订阅系统,当数据库发生变化时,发送消息通知缓存更新。这样可以实现异步更新,减少对数据库写操作的影响。

  5. 使用事务: 对于涉及到数据库和缓存的复杂操作,可以使用事务来确保操作的原子性。事务中的所有操作要么全部成功,要么全部失败,从而保证一致性。

  6. 版本号控制: 在数据中引入版本号,每次更新数据时增加版本号。缓存中也保存相应的版本号,读取数据时比较版本号,如果不一致则重新从数据库加载数据。

  7. 读写分离: 将读操作和写操作分离,读操作从缓存中获取数据,写操作更新数据库和缓存。这样可以减轻对数据库写操作的压力,同时保持读操作的高性能。

  8. 采用缓存中间件: 使用支持事务性操作的缓存中间件,如Redis的事务特性,可以在缓存层面实现一致性控制。

选择合适的一致性保证策略取决于具体应用的需求和性能要求。不同的场景可能需要采用不同的策略或它们的组合来实现缓存和数据库数据的一致性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值