腾讯面经

233 阅读3分钟

一面

1.自我介绍

2.mysql

  • mysql索引

正常八股

  • mysql主从同步机制

从节点上执行start slave命令之后,从节点会创建一个I/O线程用来连接主节点,请求主库中更新的binlog,I/O线程接收到主节点binlog之后,保存在本地relaylog中,从库线程读取relaylog中的内容,解析成具体的操作并执行。

  • mysql如何保证主从同步的数据一致性

1.异步复制
mysql默认的复制机制,事务提交并写入主库后就会返回成功给客户端,binlog更新后如果从库还没有更新会引起数据不一致的问题
2.全同步
事务提交后,必须等待全部从库全部执行相同事务后才会返回成功。缺点是会影响性能。
3.半同步
事务提交后,只需要等待其中一个从库记录relaylog的响应即可,不需要等待从库执行事务。
总结
异步复制会有数据不一致的风险,半同步和全同步可以保证数据一致性,但是全同步会影响数据库性能,所以一般生产环境会使用半同步的方式。

  • 慢sql如何处理

使用explain关键字分析sql,如果没有索引建索引,没走索引则分析没走索引的原因。一般有以下几种情况:
1.索引命中的数据太大
2.对字段进行计算
3.不符合最左前缀匹配原则
还有尽量减少多表join,因为会产生笛卡尔积;查询字段尽量写出来,不要select *,有些情况下可以直接走索引返回,避免回表。

3.kafka

  • kafka和rabbitmq对比

  • kafka重复消费问题

发生的场景:
1.还没有提交offset时线程挂掉。
2.线程消费了,消费时间过长,超过session timeout时间,导致partition断开连接。此时还没提交offset,但是kafka会reblance,就会导致重复消费。
3.消费者重新分配partition的时候,可能出现从头开始消费的情况。
4.消费速度很慢,导致在一个session周期内心跳检测机制出问题,也会导致offset没提交从而导致重复消费。

  • kafka结构

生产者,消费者,borker,topic,partition
一个topic为一类消息,每条消息必须指定一个topic。物理上,一个topic分成一个或多个partition,每个partition有多个副本分布在不同的broker中。每个partition都是一个append log文件,新增消息时会顺序写入append log,每条消息在log文件中的位置称为offset。消费时只需要修改offset的位置即可。

  • kafka内存管理策略

1.基于时间:每隔一段时间删除部分数据
2.基础存储:partition大小超过指定大小时删除部分数据

4.go

  • GMP模型
  • 优势
  • grpc框架,rpc协议和http协议的区别

5.系统架构

  • 如何保证系统高并发和高可用

可以参考这位大佬的文章: 字节三面:如何设计一个高并发系统

6.k8s

  • k8s如何实现事务的注册与发现

7.笔试题

  • 两个协程交替输出字母和数字
    要求格式:12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728
package main

import (
	"fmt"
	"sync"
)

var number = make(chan int)
var letter = make(chan int)
var wg = sync.WaitGroup{}

func printNumber() {
    n := 0
    for {
        select {
        case signal := <-number:
            for i := 0; i < 2; i++ {
                n += 1
                fmt.Print(n)
            }
            if signal == 2 {
                wg.Done()
                return
            }
            letter <- 1
        }
    }
}

func printLetter() {
    l := 'A'
    for {
        select {
        case <-letter:
            for i := 0; i < 2; i++ {
                fmt.Print(string(l))
                l += 1
            }
            if l >= 'Z' {
                number <- 2
                return
            }
            number <- 1
        }
    }
}

func main() {
    wg.Add(1)
    go printNumber()
    go printLetter()
    number <- 1
    wg.Wait()
}

  • 给定一个数组,找到和为target的最短子序列的长度,如果找不到返回0
    例:nums = [2,1,2,6,3,4], target=7,应返回2,因为最短子序列为[3,4]
package main

import "fmt"

func getMinSplice(target int, nums []int) int {
    // 滑动窗口,当sum值大于等于target时移动左指针,记录最小长度
    if nums == nil || len(nums) == 0 {
        return 0
    }
    left, right := 0, 0
    sum := 0
    minLen := len(nums) + 1
    for right < len(nums) {
        sum += nums[right]
        for sum >= target {
            if sum == target && right-left+1 < minLen {
                minLen = right - left + 1
            }
            sum -= nums[left]
            left += 1
        }
        right += 1
    }
    if minLen > len(nums) {
        return 0
    }
    return minLen
}

func main() {
    result := getMinSplice(7, []int{2, 1, 2, 6, 3, 4})
    fmt.Print(result)  // 2
}