Go语言面试Day01

59 阅读13分钟

一些Mysql和Redis基础知识

一、MySQL的事务隔离级别有几种?

1. 读未提交(Read Uncommitted)

  • 定义:事务可以读取其他未提交事务的修改。可能导致脏读。
  • 特点:最低的隔离级别,性能最好,但数据一致性最差。

2. 读已提交(Read Committed)

  • 定义:事务只能读取已提交事务的修改。避免了脏读。
  • 特点:每次读取数据时,都会看到当前已提交的最新数据,但可能会出现不可重复读。

3. 可重复读(Repeatable Read)

  • 定义:在同一事务中,多次读取同一数据项的值都是相同的。避免了脏读和不可重复读。
  • 特点:MySQL的默认隔离级别,使用MVCC来实现,能够防止幻读,但在某些情况下可能仍然会出现幻读(如插入新行)。

4. 串行化(Serializable)

  • 定义:最高的隔离级别,强制事务串行执行,完全避免了幻读、不可重复读和脏读。
  • 特点:性能最低,但数据一致性最好。

二、幻读是什么?会出现在哪种隔离级别下

1. 定义

  • 幻读(Phantom Read):幻读是指在同一事务中,多次读取同一查询条件的数据时,发现结果集发生了变化(如新增或删除了行)。
    例如,事务A在查询某个范围的数据后,事务B在此期间插入了一条新记录,导致事务A再次查询时结果集发生变化。

2. 出现的隔离级别

可重复读(Repeatable Read)和 读未提交(Read Uncommitted)

三、可重复读会出现幻读吗?

1. 特性

在MySQL中,可重复读(Repeatable Read)隔离级别通过MVCC机制可以防止脏读和不可重复读,但在某些情况下仍然可能发生幻读。这是由于在某些查询中,插入新记录可能导致查询结果集的变化。 例如,假设事务A在执行某个范围查询时,事务B在此期间插入了一条符合查询条件的新记录,事务A再次执行查询时,会发现结果集发生了变化,这就是幻读的表现。

四、MVCC(多版本并发控制)

1. 定义

MVCC(多版本并发控制)是数据库管理系统中一种并发控制机制,它允许多个事务并发执行,并确保数据一致性。MVCC通过记录每个事务对数据的修改,并生成一个版本控制表,来管理并发事务对数据的访问。

2. 工作原理

MVCC的工作原理如下:

  • 版本管理:每次对数据的修改都会生成一个新的版本,旧版本仍然保留,允许其他事务读取。
  • 时间戳:每个事务在开始时会被分配一个时间戳,读取数据时会根据时间戳确定可见的版本。
  • 快照读取:在可重复读隔离级别下,事务读取数据时会看到一个快照,确保读取到的是事务开始时的数据状态。

3. 优点

  • 提高并发性:多个事务可以并发读取数据,而不需要等待其他事务释放锁。
  • 减少锁竞争:通过避免长时间持有锁,降低了事务间的干扰。

4. 实例

在MySQL中,MVCC通过InnoDB存储引擎实现。当一个事务开始时,它会看到一个一致的快照,所有修改在事务提交之前对其他事务不可见。

五、redo log和undo log的作用

1. redo log(重做日志)

  • 作用:用于保证事务的持久性(Durability)。在事务提交后,redo log记录了所有修改操作的日志,以便在系统崩溃后能够恢复未持久化的数据。
  • 实现机制:当事务提交时,先将修改记录写入redo log,再将数据写入数据文件。即使系统崩溃,重启后可以通过redo log重做未持久化的操作,确保数据不丢失。

2. undo log(撤销日志)

  • 作用:用于支持事务的原子性(Atomicity)和一致性(Consistency)。当事务需要回滚时,undo log记录了所有操作的反向操作,以便能够撤销已执行的操作。
  • 实现机制:每当进行数据修改时,undo log会记录该操作的反向操作。如果事务回滚,系统会根据undo log将数据恢复到修改前的状态。

六、从ACID层面探讨,redo log和undo log是否能保证?

1. 原子性(Atomicity)

保证方式:通过undo log实现。如果事务未能成功完成,可以使用undo log撤销所有已执行的操作,确保事务的原子性。

2. 一致性(Consistency)

保证方式:在事务开始和结束时,系统状态保持一致。通过使用undo log和redo log,确保在事务回滚或崩溃恢复时,数据保持一致。

3. 隔离性(Isolation)

保证方式:虽然redo log和undo log主要用于持久性和原子性,但它们结合MVCC等机制也有助于实现事务之间的隔离性。

4. 持久性(Durability)

保证方式:通过redo log实现。事务提交后,所有修改会被记录到redo log中,即使系统崩溃,也能通过redo log恢复数据。

七、额外保证ACID

  • 锁机制:用于实现事务之间的隔离性,防止并发事务对同一数据的冲突。
  • 时间戳机制:用于确保事务的顺序性和一致性,特别是在高并发环境下。
  • 一致性检查:在事务开始和结束时检查数据的一致性,确保不违反业务规则。
  • 故障恢复机制:在系统崩溃时,能够通过日志和快照恢复数据状态。

八、MySQL的索引是如何做的?为什么能做到这么快的查找?有什么优缺点?

1. 索引的实现

  • 数据结构:MySQL主要使用B+树和哈希表作为索引的底层数据结构。
  • B+树索引:在B+树中,所有值都存储在叶子节点,非叶子节点只存储键值,树的高度较低,查找效率高。
  • 哈希Hash索引:适用于等值查询,通过哈希函数将键映射到存储位置。

2. 快速查找的原因

  • 减少I/O操作:索引可以显著减少需要扫描的数据量,从而减少磁盘I/O操作,提高查找速度。
  • 树结构:B+树的层高通常较低,查找路径短,能够快速定位到目标数据。(B+树的层高一般为O(log n),其中n为树中的元素总数,因此查找效率较高。)

3. 节点与节点之间的联通

  • 叶子节点:B+树的所有叶子节点通过指针相连,形成一个链表结构。这使得范围查询和遍历操作变得高效。
  • 非叶子节点:非叶子节点通过指向子节点的指针进行连接,形成树形结构

4. 优缺点

  • 优点: 提高查询效率。 加速排序和聚合操作。
  • 缺点: 增加存储空间开销。 在数据插入、更新和删除时需要维护索引,可能导致性能下降。

九、最左匹配原则

1. 定义

最左匹配原则是指在使用复合索引时,查询条件中必须从左到右依次匹配索引的列,才能有效利用索引。

2. 例子

假设有一个复合索引 (A, B, C),以下查询能有效使用索引:

  • WHERE A = 1 AND B = 2(符合最左匹配原则)
  • WHERE A = 1(符合最左匹配原则)

以下查询不能有效使用索引:

  • WHERE B = 2(不符合最左匹配原则)
  • WHERE C = 3(不符合最左匹配原则)

3. 影响

遵循最左匹配原则可以提高查询效率,确保索引的有效利用,从而加快数据检索速度。

十、聚簇索引和非聚簇索引的区别

1. 聚簇索引(Clustered Index)

1.1 定义

聚簇索引是将数据存储在索引中的一种方式,数据行的物理顺序与索引的顺序相同。每个表只能有一个聚簇索引

1.2 特性
  • 数据和索引存储在同一结构中,查找时直接定位到数据行。
  • 适合范围查询,性能较高。
  • 更新时可能导致数据行的移动,从而影响性能。

2. 非聚簇索引(Non-Clustered Index)

2.1 定义

非聚簇索引是指索引与数据存储分开,索引中存储的是数据行的指针(或行号),每个表可以有多个非聚簇索引

2.2 特性
  • 数据和索引分开存储,查找时需要先通过索引找到指针,再定位到数据行
  • 适合单值查询,索引结构较小,更新时对数据行的影响较小。
  • 性能相对聚簇索引略低,但灵活性更高。

十一、索引下推

1. 定义

索引下推是一种优化技术,允许数据库在使用索引时,将某些查询条件下推到索引扫描阶段,而不是在获取完整数据后再进行过滤。

2. 优势

  • 提高性能:通过在索引层面过滤不必要的数据,减少了需要读取的数据量,从而提高查询效率。
  • 减少I/O操作:降低了对数据页的访问频率,减少了磁盘I/O。

3. 适用场景

当查询条件中有多个列,并且这些列被包含在索引中时,索引下推能够显著提高查询性能。

十二、慢查询的定位与优化

1. 定位慢查询

  • 开启慢查询日志:在MySQL配置文件中设置slow_query_log为ON,并设置long_query_time以记录超过指定时间的查询。
  • 使用EXPLAIN命令:对慢查询使用EXPLAIN语句,分析查询的执行计划,查看索引是否被使用。
  • 性能分析工具:使用工具如pt-query-digest分析慢查询日志,找出瓶颈。

2. 优化方法

  • 优化索引:根据查询条件添加合适的索引,避免全表扫描。
  • 调整查询语句:重写查询语句,避免复杂的联接和子查询。
  • 分表分库:对大表进行分表分库,减少单表数据量。
  • 增加缓存:使用缓存机制(如Redis)缓存频繁查询的数据,减少数据库压力。

十三、分库分表的注意事项及策略

1. 分库分表策略

  • 按业务功能分库:将不同业务模块的数据放入不同的数据库中。
  • 按用户ID或地理位置分表:根据用户ID的哈希值或地理位置将数据分散到不同的表中。
  • 水平分表:将大表按行数分割,形成多个小表。
  • 垂直分表:将表按列分割,将不同的字段放入不同的表中。

2. 注意事项

  • 数据一致性:确保在分库分表后,数据的一致性和完整性得到保证。
  • 查询性能:考虑跨库查询的性能,尽量减少跨库操作。
  • 事务管理:分库分表后,事务的管理和回滚变得复杂,需要使用分布式事务管理。
  • 扩展性:设计时要考虑未来的扩展性和易维护性。
  • 负载均衡:合理分配负载,避免某个库或表的性能瓶颈。

十四、分区和分表的区别

1. 分区(Partitioning)

1.1 定义

将一个表的数据分散存储在不同的物理位置,但从逻辑上仍然是一个表

1.2 特性
  • 数据在同一表中,使用分区键进行划分
  • 支持范围分区、列表分区、哈希分区等。
  • 适合大表的管理,提高查询性能,简化管理。

2. 分表(Sharding)

2.1 定义

将数据拆分到多个物理表中,通常是根据某种规则(如用户ID)将数据分散到不同的表。

2.2 特性
  • 每个表是独立的,通常在不同的数据库实例中。
  • 可以根据业务需求灵活扩展。
  • 适合高并发和大量数据场景。

十五、Redis的数据类型(详情看小林coding)

1. 字符串(String):

最基本的数据类型,可以存储任何形式的数据,包括文本、数字等。

2. 哈希(Hash):

用于存储键值对的集合,适用于表示对象。

3. 列表(List):

有序的字符串集合,可以在两端进行插入和删除操作,适合实现队列和栈。

4. 集合(Set):

无序的字符串集合,支持集合运算(如交集、并集、差集)。

5. 有序集合(ZSet):

类似于集合,但每个元素都有一个分数,按分数排序,支持范围查询。

6. 位图(Bitmap):

对字符串的位操作,常用于统计和计数。

7. 超日志(HyperLogLog):

用于基数统计,能够高效地估算唯一元素的数量。

8. 地理空间(Geospatial):

用于存储和查询地理位置数据。

十六、ZSet的底层实现

1. 定义

ZSet(有序集合)是Redis中的一种数据类型,每个元素都有一个分数,元素按分数排序。

2. 底层实现

  • 跳表(Skip List):
    • Redis使用跳表作为ZSet的主要数据结构。跳表是一种随机化的数据结构,允许快速的插入、删除和查找操作,时间复杂度为O(log n)。
    • 跳表由多个层级组成,每个层级都是一个有序链表,底层包含所有元素,越往上层包含的元素越少。
  • 哈希表: Redis还使用哈希表来存储ZSet中的元素及其分数,以便在需要时快速查找和更新。

3. 优势

  • 高效性:跳表支持快速的范围查询和排名操作,适合实时数据处理。
  • 灵活性:可以存储重复元素和分数相同的元素,便于复杂应用场景。

十七、压缩列表的结构

1. 定义

压缩列表(Ziplist)是Redis中用于存储小数量元素的高效数据结构,主要用于节省内存。

2. 结构特点

  • 扁平结构:压缩列表是一个连续的内存块,其中包含多个元素,元素之间没有指针,节省了指针的内存开销。
  • 元素存储:每个元素包含长度信息和实际数据,支持字符串和整数类型。
  • 动态调整:当元素数量超过一定阈值时,压缩列表会被转换为其他数据结构(如链表或哈希表)。

3. 优势

  • 节省内存:由于没有额外的指针开销,适合存储小型数据。
  • 快速访问:对于小数据量的操作,压缩列表提供了快速的访问性能。

4. 缺点

  • 性能限制:当元素数量较多时,压缩列表的性能会下降,因此适用于小数量元素的场景。