网易八股面试

八股文

golang:

1,不同包的多个init函数的运行时机(答得不好 没看过这里)

2,init和main函数的执行顺序 (init函数在main函数开始前执行)

3,如何删除切片中的某一个元素 (就说了一个通过append(a[:2],a[3:]…))

4,sync.Map{}怎么使用 (使用Store存)

5,怎么控制去并发操作map(加锁)

6,go的读写锁怎么工作 (一大段支支吾吾后说了读写互斥)

7,如何保存程序崩溃时的数据 (不会,说了日志,在项目中聊到recover我就没想到这)

8,多个并发操作对map进行读写,程序会发生什么(给了两个选项,a:会继续执行 b:程序会崩溃,选了a,结果现在一查发现是b)

9,超时处理 (在网上查到用context.WithTimeout更好,我说的time.After) 去监听 channel的一个超时事件

  1. context deadline 超时控制
  2. 通过 time.After

10,对一个已经关闭的channel读写会发生什么 (如果不空的话,先读数据,空的话读对应类型的零值, 写的话会出现panic)

11,开五个协程 全部执行一个函数,怎么保证协程执行完全部打印(使用sync.WaitGroup ) 1. 简单点用 WaitGroup 阻塞 5个协程 2. 基于channel, 开大小为5的channel, 然后 每个携程执行完成之后,发送一个事件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import (
    "sync"
)
var wg sync.WaitGroup 

func main2() {
    // wg.Add(5)
    ch := make(chan struct{},5)
    for i:=1;i<=5;i++ {
        go func(j int) {
            fmt.Println("",j)
            ch <- struct{}{}
        }(i)
    }
    for v := range ch {
        
    }
    wg.Wait()
}
func main1() {
    // wg.Add(5)
    for i:=1;i<=5;i++ {
        wg.Add(1)
        go func(j int) {
            fmt.Println("--",j)
            wg.Done()
        }(i)
    }
    wg.Wait()
}

linux:

1,怎么去查询某一行的数据 (grep)

2,怎么汇总url,统计每个url (不会)

1
2
3
4
5
6
7
8
9
#!/bin/bash  
  
log_file="access.log"  # 日志文件路径  
result_file="top_urls.txt"  # 结果文件路径  
  
# 提取URL并计数  
awk -F " " '{print $7}' "$log_file" | sort | uniq -c | sort -nr | head -10 > "$result_file"  
  
echo "脚本执行完成,结果已保存至当前目录的 $result_file"
1
grep -o 'http[s]?://[^[:space:]]*' access.log | uniq -c | wc -l

3,怎么查找进程 (ps)

4,怎么查找有go语言的进程 (pgrep go)

mysql:

1,对abc建立联合索引,他们的顺序重要么(重要,应把使用频率高的放在前面)

作者:牛客128067218号 链接: https://www.nowcoder.com/discuss/499307188142223360?sourceSSR=search 来源:牛客网

解题

GO基础

定义:Go 的每个包可以有多个 init 函数(甚至在一个文件内),用于初始化代码。 运行时机: 先初始化被依赖的包:如果包 A 依赖包 B,则 B 的 init 函数会在 A 的 init 函数前运行。 同一包内多个文件:按照编译器决定的文件顺序依次执行。 单文件多个 init 函数:按照代码顺序执行。

MVCC

MVCC(Multi-Version Concurrency Control,多版本并发控制) 是 MySQL InnoDB 存储引擎中实现高并发读写性能的关键技术。它通过为每一行数据保存多个版本,允许读操作和写操作并行进行,减少了锁的使用。

在 MySQL InnoDB 中,即使没有使用 SELECT … FOR UPDATE,默认的事务隔离级别 可重复读(REPEATABLE READ) 也能够解决幻读问题。这是由于 InnoDB 的 MVCC 和 Next-Key Lock 机制 的配合工作,即使在没有显式加锁的情况下,依然能够避免幻读的发生。

MVCC 的工作机制 隐藏的两个字段: 每行记录会存储两个额外的隐藏字段:

DB_TRX_ID:最近一次对该行修改的事务 ID。 DB_ROLL_PTR:回滚指针,指向 Undo Log 中的旧版本数据。 Undo Log:

当事务对数据进行更新时,旧版本会存储在 Undo Log 中,用于支持 MVCC 的一致性读。 事务 ID:

每个事务启动时,InnoDB 分配一个递增的事务 ID(TRX_ID),用来标识事务的版本。 两种一致性读:

当前读(Current Read): 当前读会读取最新版本的数据,并对数据加锁(如 SELECT … FOR UPDATE、UPDATE)。 快照读(Snapshot Read): 读取数据时基于事务开始时的快照视图,避免锁定数据(如普通的 SELECT)。

详细示例:幻读解决机制 假设有以下场景:

初始表数据:

sql

id | value 1 | 100 3 | 300 事务 1:

1
2
3

BEGIN; 
SELECT * FROM table WHERE id >= 2 FOR UPDATE;

锁定了 id >= 2 的范围,包括 id=3 的行和 2~3 之间的间隙。 事务 2:

1
2

INSERT INTO table (id, value) VALUES (2, 200); -- 被阻塞

由于间隙锁,事务 2 在尝试插入 id=2 时会被阻塞,直到事务 1 提交。 总结:

通过 Next-Key Lock,MySQL 不仅锁定了现有行,还锁定了满足查询条件的间隙,从而防止了幻读。

案例

1
2
SELECT * FROM table WHERE id >= 2 
-- 是快照读,  这个时候插入了 id=3 的数据,下次查询 id>=2 的时候还是查询的快照,这么解释

是的,在 MySQL InnoDB 中,如果事务的隔离级别是 可重复读(REPEATABLE READ) 或更低,SELECT * FROM table WHERE id >= 2 执行的确是 快照读(Snapshot Read)。这种情况下,即使在事务运行期间有新的数据插入,事务中的后续查询仍然基于事务开始时的快照视图,无法看到新插入的数据。


快照读的关键机制:MVCC

  1. 事务开始时创建快照

    • 当事务启动时,InnoDB 会为该事务生成一个 一致性视图(Consistent Read View),记录当前能看到的数据版本。
    • 事务中的所有普通 SELECT 查询都会基于这个快照执行。
  2. 版本控制(MVCC 原理)

    • 每条记录在 InnoDB 中都存储了多个版本(通过 Undo Log 实现)。
    • 快照读查询根据事务的快照时间戳(事务 ID),仅读取那些在快照时间点已经提交的数据版本。
    • 未提交的、更改中的数据对快照读不可见。

示例解释

假设表 table 中初始有以下数据:

1
2
3
id | value
1  | 100
3  | 300

操作过程:

  1. 事务 1 启动并执行第一次查询:

    1
    2
    
    BEGIN;
    SELECT * FROM table WHERE id >= 2; -- 返回 id=3 的行
    
    • 在事务开始时,InnoDB 创建了一个一致性快照。
    • 查询结果:id=3
  2. 事务 2 插入新记录并提交:

    1
    2
    3
    
    BEGIN;
    INSERT INTO table (id, value) VALUES (2, 200);
    COMMIT;
    
    • id=2 的新记录已经被提交,但对事务 1 来说不可见,因为事务 1 的快照视图没有包含这条记录。
  3. 事务 1 再次查询:

    1
    
    SELECT * FROM table WHERE id >= 2; -- 返回的仍然是快照数据
    
    • 查询结果依旧是 id=3,而不会包括 id=2
    • 原因是事务 1 的查询仍然基于事务开始时的快照。

为什么快照读查询不到新插入的数据?

  1. 快照视图的隔离性

    • 当事务 1 开始时,它只看到在快照创建之前提交的所有数据版本。
    • 新插入的数据即使提交了,也不在快照视图内。
  2. 避免“幻读”

    • 虽然没有加锁,快照读通过 MVCC 的机制实现了事务间的隔离性,保证了同一个事务的查询结果一致。

快照读 VS 当前读

类型 特点 是否加锁
快照读 基于事务开始时的快照,读取的是固定的历史版本数据,查询结果一致。
当前读 读取最新版本的数据,可能会阻塞或被阻塞。例如 SELECT ... FOR UPDATEUPDATE 等。

总结

  • 当事务使用普通的 SELECT 查询(快照读)时,查询结果是基于事务开始时的快照视图。
  • 即使在事务运行期间有其他事务插入或修改了数据,快照读查询的结果也不会变化,确保了事务的一致性。
  • 下次查询 id >= 2 的结果仍然是最初快照中的内容,不会包含新插入的数据。这正是快照读的核心特性。