面试题-Golang

面试题-Golang

你知道redis缓存击穿 雪崩 穿透吗

  1. 缓存击穿(Cache Breakdown):
    • 场景: 缓存击穿指的是某个热点数据在缓存中过期时,同时有大量请求访问这个数据,导致这些请求穿透缓存直接访问数据库,造成数据库负载剧增。
    • 解决方案: 使用互斥锁或分布式锁,确保只有一个请求能够重新生成缓存数据,其他请求等待缓存数据刷新。
  2. 缓存雪崩(Cache Avalanche):
    • 场景: 缓存雪崩是指缓存中的一批数据在同一时间内失效,导致大量请求直接落到数据库上,增加了数据库的负载。
    • 解决方案: 给缓存数据设置不同的过期时间,避免同一时间大量缓存数据同时失效。另外,使用热点数据预热、缓存数据永不过期等策略也可以减缓缓存雪崩的影响。
  3. 缓存穿透(Cache Penetration):
    • 场景: 缓存穿透是指请求查询一个在缓存中不存在的数据,且这个数据对应的数据库中也不存在,导致每次请求都要查询数据库,增加数据库负载。
    • 解决方案: 使用布隆过滤器(Bloom Filter)等机制,预先判断请求的数据是否存在于数据库中。另外,对于查询结果为空的情况,也可以设置一个空值缓存,防止频繁查询同一个不存在的数据。

redis有哪几种模式

Redis 支持多种模式,其中最常见的包括以下几种:

  1. 单机模式(Standalone Mode):
    • 单机模式是最简单的部署方式,一个 Redis 实例运行在一台服务器上,所有的数据都存储在这一台服务器的内存中。这种模式适用于小规模应用或者开发环境。
  2. 主从复制模式(Master-Slave Replication):
    • 主从复制模式包括一个主节点(Master)和多个从节点(Slave)。主节点负责写操作,从节点负责复制主节点的数据,提供读操作。主从复制模式提高了系统的可用性和可靠性。
  3. 哨兵模式(Sentinel Mode):
    • 哨兵模式是基于主从复制的模式,引入了 Sentinel 进程,用于监控主节点的健康状况。当主节点宕机或不可达时,哨兵会自动将一个从节点晋升为新的主节点,确保系统的高可用性。
  4. 集群模式(Cluster Mode):
    • Redis 集群模式将数据分片存储在多个节点上,每个节点负责一部分数据。这种模式提高了系统的横向扩展性和容错性。集群模式采用分区(partitioning)和副本(replication)相结合的方式。
  5. 持久化模式:
    • Redis 提供两种持久化方式,分别是 RDB 持久化和 AOF 持久化。RDB 持久化将数据保存在磁盘上的二进制文件中,而 AOF 持久化则记录每个写操作的日志,以便在重启时进行恢复。

这些模式可以根据具体的应用场景和需求进行选择和组合。例如,对于需要高可用性的应用可以使用主从复制或哨兵模式,对于需要横向扩展的应用可以使用集群模式。每种模式都有其适用的场景和优缺点。

redis 持久化方案有哪些

Redis 提供了两种主要的持久化方案,分别是 RDB(Redis DataBase)持久化和 AOF(Append Only File)持久化。这两种方案可以单独使用,也可以同时使用,根据具体的需求和应用场景进行配置。

  1. RDB 持久化:

    • RDB 持久化是将 Redis 的数据以二进制形式保存到磁盘上的一个文件中。这个文件是一个经过压缩的二进制文件,包含了某个时间点上所有键值对的数据快照。
    • RDB 持久化的优点是文件小巧、加载速度快,适用于备份和全量恢复。它适用于周期性的数据快照和灾难恢复。

    配置方式:

    save 900 1          # 如果 900 秒内至少有 1 个键被修改,则执行快照
    save 300 10         # 如果 300 秒内至少有 10 个键被修改,则执行快照
    save 60 10000       # 如果 60 秒内至少有 10000 个键被修改,则执行快照
    
  2. AOF 持久化:

    • AOF 持久化是通过追加方式记录 Redis 服务器接收到的每个写操作,以日志的形式保存到一个文件中。当服务器重启时,可以通过重新执行这个文件中的写操作来还原数据。
    • AOF 持久化的优点是对数据的持久化更加实时,可通过配置不同的同步方式(always、everysec、no)来平衡性能和可靠性。

    配置方式:

    appendonly yes              # 启用 AOF 持久化
    appendfilename "appendonly.aof"  # AOF 文件的名称
    
  3. 混合持久化:

    • 可以同时使用 RDB 持久化和 AOF 持久化,充分发挥两者的优势。Redis 在启动时会先使用 AOF 文件进行数据恢复,如果 AOF 文件不存在或者恢复失败,则尝试使用 RDB 文件进行数据恢复。

这两种持久化方案各有优劣,选择合适的方案取决于具体的应用场景和对数据恢复、性能等方面的需求。

mysql有哪些锁

MySQL 提供了多种类型的锁,用于控制并发访问数据库的数据。以下是一些常见的 MySQL 锁类型:

  1. 共享锁(Shared Locks):

    • 共享锁(也称为读锁)允许多个事务同时读取同一资源,但不允许任何事务对该资源进行写入。多个事务可以持有共享锁,不会相互阻塞。
    SELECT * FROM table_name LOCK IN SHARE MODE;
    
  2. 排他锁(Exclusive Locks):

    • 排他锁(也称为写锁)阻塞其他事务对同一资源的访问,包括读取和写入。只有一个事务可以持有排他锁。
    SELECT * FROM table_name FOR UPDATE;
    
  3. 行级锁(Row-level Locks):

    • 行级锁是一种粒度更细的锁,它仅锁定数据表中的某一行,而不是整个表。行级锁允许事务仅锁定需要修改的行,而不是整个表。
    SELECT * FROM table_name WHERE column_name = 'value' FOR UPDATE;
    
  4. 表级锁(Table-level Locks):

    • 表级锁是一种锁定整个数据表的锁,它会阻塞其他事务对整个表的读写操作。
    LOCK TABLES table_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE};
    UNLOCK TABLES;
    
  5. 意向锁(Intention Locks):

    • 意向锁是一种高层次的锁,用于表示事务准备在某个层次上加锁。它包括意向共享锁(IS)和意向排他锁(IX)。
    LOCK TABLES table_name [AS alias] {READ | WRITE};
    
  6. 自增锁(Auto-increment Locks):

    • 当使用自增主键插入数据时,InnoDB 使用自增锁来确保并发插入的唯一性。
    INSERT INTO table_name (auto_increment_column, other_columns) VALUES (NULL, 'value');
    
  7. 间隙锁(Gap Locks):

    • 间隙锁用于锁定一个范围,而不是具体的行。它可以防止其他事务插入新的行,从而避免新插入的行与当前事务中的行发生冲突。

MySQL 的锁机制是复杂的,不同的存储引擎可能有不同的锁行为。因此,在实际使用中,需要了解具体存储引擎的锁行为,并根据应用场景选择合适的锁策略。

go的GMP模型有了解吗

在 Go 语言中,GMP 指的是 Goroutine、M(线程)、P(处理器)模型,这是 Go 语言并发模型的核心组成部分。下面简要介绍一下 GMP 模型:

  1. Goroutine(G):

    • Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时(runtime)管理。与传统线程相比,Goroutine 的创建和销毁开销非常小,可以轻松创建数以千计的 Goroutine,而不担心系统资源的耗尽。
    • Goroutine 通过关键字 go 来创建,它运行在 Go 运行时的调度器之上。
    go func() {
        // Goroutine 的代码逻辑
    }()
    
  2. M(Machine):

    • M 是 Go 语言中的操作系统线程(Machine),用于执行 Goroutine。每个 M 都关联一个操作系统线程,而 Goroutine 则由 M 来执行。
    • M 的数量由 Go 运行时根据系统的 CPU 数量动态调整。M 与真正的操作系统线程有一定的复用关系,可以在多个 Goroutine 之间切换。
  3. P(Processor):

    • P 是 Go 语言中的处理器(Processor),它负责管理和调度 Goroutine。P 的数量也由 Go 运行时动态调整,每个 P 可以同时运行多个 Goroutine。
    • P 的主要任务是从全局的 Goroutine 队列中获取 Goroutine,并将其分配给某个 M 执行。当某个 M 上的 Goroutine 阻塞时,P 负责将其换到其他可执行的 M 上执行。

GMP 模型的关键是实现了高效的 Goroutine 调度和管理机制。它使得开发者可以使用轻量级的 Goroutine 来实现并发,而不必过多关心线程的创建和销毁的开销。通过 GMP 模型,Go 语言能够在多核系统上充分利用硬件资源,实现高性能的并发编程。

go中 channel的数据结构是什么

在 Go 语言中,channel 是一种用于在 Goroutine 之间传递数据的并发安全的数据结构。channel 的底层数据结构并没有直接暴露给开发者,但是我们可以了解一些大致的工作原理。

channel 的数据结构主要包括两个部分:一个指向数据的指针和两个指向等待发送和接收 Goroutine 的队列。以下是一个简单的描述:

  1. 指向数据的指针:
    • 当有数据被发送到 channel 时,channel 内部会保存一个指向数据的指针。这个指针指向被发送的数据在内存中的位置。
  2. 发送 Goroutine 队列:
    • 当一个 Goroutine 尝试发送数据到一个已满的 channel 时,这个 Goroutine 将会被阻塞,并被加入到 channel 的发送 Goroutine 队列中。一旦 channel 可以接收数据,其中的一个 Goroutine 将会被唤醒并执行发送操作。
  3. 接收 Goroutine 队列:
    • 当一个 Goroutine 尝试从一个空的 channel 中接收数据时,这个 Goroutine 会被阻塞并加入到 channel 的接收 Goroutine 队列中。一旦 channel 中有数据可用,其中的一个 Goroutine 将会被唤醒并执行接收操作。

通过这种方式,channel 的数据结构保证了并发的安全性,多个 Goroutine 可以在不使用显式锁的情况下安全地进行数据传递。channel 内部的调度和同步工作由 Go 运行时(runtime)负责,使得开发者可以更方便地进行并发编程。

对已经关闭的channel分别读写会发生什么

对于已经关闭的 channel,Go 语言规定了一些行为:

  1. 读取已关闭的 channel

    • 如果一个 channel 已经关闭,那么对于该 channel 的后续读取操作将不会阻塞,并且会立即返回该类型的零值。即使 channel 中还有未读取的数据,读取已关闭的 channel 也不会导致数据被丢弃,而是会将已经在 channel 中的数据读取出来。
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    close(ch)
    
    // 读取已关闭的 channel,会返回 channel 中的数据和一个表示 channel 是否已关闭的标志
    for i := 0; i < cap(ch)+1; i++ {
        data, ok := <-ch
        fmt.Println(data, ok)
    }
    
  2. 写入已关闭的 channel

    • 如果一个 channel 已经关闭,再向该 channel 进行写入操作会导致 panic。这是因为关闭后的 channel 不再允许写入数据,试图写入会触发运行时异常。
    ch := make(chan int, 2)
    close(ch)
    
    // 向已关闭的 channel 写入数据,会导致 panic
    ch <- 3
    

在使用 channel 时,通常建议在写入前先检查 channel 是否已关闭,可以使用可选的第二个返回值(ok)来判断 channel 的状态。例如:

// 示例:在写入前检查 channel 是否已关闭
ch := make(chan int, 2)
close(ch)

select {
case ch <- 3:
    fmt.Println("写入成功")
default:
    fmt.Println("channel 已关闭")
}

这样可以避免在已关闭的 channel 上进行写入而导致 panic。

标签 :
share(TODO):