教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 go list指针_2020 年字节跳动 Go 面试总结

go list指针_2020 年字节跳动 Go 面试总结

发布时间:2021-12-12   编辑:jiaochengji.com
教程集为您提供go list指针,2020 年字节跳动 Go 面试总结等资源,欢迎您收藏本站,我们将为您提供最新的go list指针,2020 年字节跳动 Go 面试总结资源

我是一只可爱的土拨鼠,专注于分享 Go 职场、招聘和求职,解 Gopher 之忧!欢迎关注我。

土拨鼠注:虽然是 Go 面试,但完全是 Go 的内容真不多。可见大家准备时,一定不能只盯着 Go 语言本身。


应朋友之邀,今天下午去字节送了颗人头,最后不负众望,被面试官撵出来了……

<h2><span style="font-weight:bold;"/><span style="font-weight:bold;">一面</span><span style="font-weight:bold;"> </span></h2> <h3><span style="font-weight:bold;"/>谈一下之前重构百度账号中心的方案<span style="font-weight:bold;"/></h3>

吹了一波之前在百度改造 restful 接口的方案,但面试官并不感冒,提了一个显示文章的列表的场景,但感觉没有理解面试官的意思,没有提出面试官满意的 restful 解决方案,刚开始就得了个负分,这块得抽空找大佬再探讨探讨,等后面有什么心得再补充吧

<h3><span style="font-weight:bold;"/>mysql 索引快的原理<span style="font-weight:bold;"/></h3>

回答这个问题需要先看一下数据库的存储结构

<figure style="text-align:center;"><figcaption> img </figcaption></figure>

页结构

<h4><span style="font-weight:bold;"/>页和页之间的关系<span style="font-weight:bold;"/></h4> <figure style="text-align:center;"><figcaption> img </figcaption></figure>

页和页之间的关系

<blockquote>

有个知识,之前不知道的 聚集索引:以主键创建的索引,叶子节点存储的是表中的数据 非聚集索引:非主键创建的索引,叶子节点中存储的是主键和索引列,使用非聚集索引查询数据,会查询到叶子上的主键,再根据主键查到数据(这个过程叫做回表)

</blockquote>

没有用索引的时候,需要遍历双向链表来定位对应的页,有了索引,可以用二分查找,这么弱智的答案,我当时居然没想到,这也是后面面试官问为什么主键建议用自增字段的答案

<h3><span style="font-weight:bold;"/>页码跳页性能(即 sql offset 会不会影响性能)<span style="font-weight:bold;"/></h3>

mysql 查询时,offset 过大影响性能的原因是多次通过主键索引访问数据块的 I/O 操作 InnoDB 会,MyISAM 不会,如图所示

<figure style="text-align:center;"><figcaption> img </figcaption></figure>

InnoDB 和 MyISAM 对比图

InnoDB 的二级索引对应的是主键,mysql 查询的时候会根据主键将数据块查出来,然后执行 offset 丢弃,如果只查主键就不会有性能问题。MyISAM 的主键索引和二级索引都指向数据块,因此没有这方面的问题

<h4><span style="font-weight:bold;"/>优化措施<span style="font-weight:bold;"/></h4>

先查询偏移后的主键,再查询数据块

<pre class="has"><code>select a.* from member as a inner join (select id from member where gender=1 limit 300000,1) as b on a.id=b.id
</code></pre> <h3><span style="font-weight:bold;"/>golang 中 new 和 make 的区别<span style="font-weight:bold;"/></h3> <ol><li>make 仅用来分配及初始化类型为 slice、map、chan 的数据。new 可分配任意类型的数据.</li><li>new 分配返回的是指针,即类型 *Type。make 返回引用,即 Type.</li><li>new 分配的空间被清零, make 分配空间后,会进行初始化.</li></ol><h3><span style="font-weight:bold;"/>有一个字母翻译对照表,1 代表 A,2 代表 B,以此类推至 26 代表 Z,现给一个整形数组,例如[1,2,3,4,5,6,7,8,9],求共有多少种翻译方式<span style="font-weight:bold;"/></h3> <pre class="has"><code>package main

import (
    "fmt"
)

func main() {
    s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
    dict := make(map[int]string)
    for i := 1; i 27; i  { //初始化字典
        dict[i] = string('A'   (i - 1))
    }
    count := 0
    for i := 0; i len(s); i  {
        if i == 0 {
            count
        } else if s[i-1]*10 s[i] 27 && s[i-1]*10 s[i] > 0 {
            count
        }
    }
    fmt.Println(count)
}
</code></pre> <h3><span style="font-weight:bold;"/>redis zset 的数据结构<span style="font-weight:bold;"/></h3> <figure style="text-align:center;"><figcaption> img </figcaption></figure>

zset 结构

如图所示,L0 层存储所有的数据,L1 层随机抽取几个组成一个系数索引,L2 层进一步抽取 L1 层,从而组成一个多层稀疏索引,这样就可以用二分法快速的找出所需要的数据了

<h3><span style="font-weight:bold;"/>利用 redis 做一个延时事件执行系统(设计)<span style="font-weight:bold;"/></h3> <figure style="text-align:center;"><figcaption> img </figcaption></figure>

延时执行设计

当时想的设计跟盒子科技的这张 PPT 高度类似,以时间为 score,以事件数组为 value,存储成一个 zset,然后定期去取一定数量的事件进行执行。如果延时执行事件比较稀疏,就设定一个值,比如每次取的事件必须是一分钟内的,一分钟内没有时间则等待下次再取。

<h2><span style="font-weight:bold;"/><span style="font-weight:bold;">二面</span><span style="font-weight:bold;"> </span></h2> <h3><span style="font-weight:bold;"/>PHP 代码 sleep 的时候,进程状态是怎样的,进程都有哪几种状态<span style="font-weight:bold;"/></h3> <ol><li>创建状态</li><li>就绪状态</li><li>运行状态</li><li>阻塞状态</li><li>终止状态</li></ol>

当代码执行 sleep 的时候进程处于阻塞状态

<h3><span style="font-weight:bold;"/>linux 系统中如何查看进程状态<span style="font-weight:bold;"/></h3>

使用命令 <code>ps -aux</code>,STAT 列即为进程状态

<figure style="text-align:center;"><figcaption> img </figcaption></figure>

进程示例

<h4><span style="font-weight:bold;"/>linux 上进程有五种状态<span style="font-weight:bold;"/></h4> <ol><li>R——Runnable(运行):正在运行或在运行队列中等待</li><li>S——sleeping(中断):休眠中,受阻,在等待某个条件的形成或接收到信号</li><li>D——uninterruptible sleep(不可中断):收到信号不唤醒和不可运行,进程必须等待直到有中断发生</li><li>Z——zombie(僵死):进程已终止,但进程描述还在,直到父进程调用 wait4()系统调用后释放</li><li>T——traced or stoppd(停止):进程收到 SiGSTOP,SIGSTP,SIGTOU 信号后停止运行</li></ol><h3><span style="font-weight:bold;"/>状态后缀表示:<span style="font-weight:bold;"/></h3> <h3><span style="font-weight:bold;"/>数据库事务的实现原理<span style="font-weight:bold;"/></h3>

数据库不同的存储引擎可能会有一些区别。这里拿常用的 InnerDB 存储引擎举例:

<h4><span style="font-weight:bold;"/>原子性实现原理:<span style="font-weight:bold;"/></h4>

通过数据库 Undo Log 实现的。事务中在操作任何数据之前,首先将原数据备份到 Undo Log 然后进行数据的修改。如果事务中有任意操作发生异常或用户执行了 Rollback 语句,那么数据库就会使用 Undo Log 中的备份将数据恢复到事务开始之前的状态。

<h4><span style="font-weight:bold;"/>一致性实现原理:<span style="font-weight:bold;"/></h4>

与原子性实现原理一样也是利用 Undo Log

<h4><span style="font-weight:bold;"/>持久性实现原理:<span style="font-weight:bold;"/></h4>

通过数据库 Redo Log 实现的,Redo Log 与 Undo Log 相反,Redo Log 记录的是新数据的备份,事务提交之前,会把数据备份到 Redo Log 中并持久化。当系统崩溃时,虽然数据没有持久化到数据库中,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到最新的

<h4><span style="font-weight:bold;"/>隔离性实现原理:<span style="font-weight:bold;"/></h4>

隔离性的实现原理比较特殊,是通过数据库锁的机制实现的。隔离性分四个级别:读未提交(Read uncommitted)、读已提交(Read committed)、可重复读(Repeatable reads)、可序列化(Serializable) MySQL 的默认隔离级别就是 Repeatable,Oracle 默认 Read committed

<h5><span style="font-weight:bold;"/>读未提交:一个事务可以读到另外一个事务未提交的数据。脏读<span style="font-weight:bold;"/></h5>

实现:事务在读数据的时候并未对数据进行加锁。事务在发生更新数据的瞬间,必须先对其加 行级共享锁,直到事务结束才释放。举例:事务 A 读取某行记录时(没有加锁),事务 2 也能对这行记录进行读取、更新。当事务 B 对该记录进行更新时,事务 A 读取该记录,能读到事务 B 对该记录的修改版本,即使该修改尚未被提交。事务 A 更新某行记录时,事务 B 不能对这行记录做更新,直到事务 A 结束。

<h5><span style="font-weight:bold;"/>读已提交:一个事务可以读到另外一个事务提交的数据。不可重复读<span style="font-weight:bold;"/></h5>

实现:事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。原理:事务 A 读取某行记录时,事务 B 也能对这行记录进行读取、更新;当事务 B 对该记录进行更新时,事务 A 再次读取该记录,读到的只能是事务 B 对其更新前的版本,或者事务 B 提交后的版本。事务 A 更新某行记录时,事务 B 不能对这行记录做更新,直到事务 1 结束。流程描述:事务 A 读操作会加上共享锁,事务 B 写操作时会加上排他锁,当事务 B 正在写操作时,事务 A 要读操作,发现有排他锁,事务 A 就会阻塞,等待排他锁释放(事务 B 写操作提交才会释放),才能进行读操作。

<h5><span style="font-weight:bold;"/>可重复读<span style="font-weight:bold;"/></h5>

实现:事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。举例:事务 A 读取某行记录时,事务 B 也能对这行记录进行读取、更新;当事务 B 对该记录进行更新时,事务 A 再次读取该记录,读到的仍然是第一次读取的那个版本。事务 A 更新某行记录时,事务 B 不能对这行记录做更新,直到事务 1 结束。

<h5><span style="font-weight:bold;"/>可序列化(Serializable) 写操作串联执行<span style="font-weight:bold;"/></h5>

实现:事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。举例:事务 A 正在读取 A 表中的记录时,则事务 B 也能读取 A 表,但不能对 A 表做更新、新增、删除,直到事务 A 结束。事务 A 正在更新 A 表中的记录时,则事务 B 不能读取 A 表的任意记录,更不可能对 A 表做更新、新增、删除,直到事务 A 结束。原理:在读操作时,加表级共享锁,事务结束时释放;写操作时候,加表级独占锁,事务结束时释放。

<h3><span style="font-weight:bold;"/>聚簇索引<span style="font-weight:bold;"/></h3>

跟聚集索引是一个东西,参见上面的聚集索引

<h3><span style="font-weight:bold;"/>反转一个链表,如 1->2->3->4->5->6,转为 1->5->4->3->2->6<span style="font-weight:bold;"/></h3> <pre class="has"><code>package main

import "fmt"

// ListNode 链表节点
type ListNode struct {
    Val  int
    Next *ListNode
}

//反转链表的实现
func reversrList(head *ListNode) *ListNode {
    cur := head
    var pre *ListNode = nil
    for cur != nil {
        pre, cur, cur.Next = cur, cur.Next, pre //这句话最重要
    }
    return pre
}

// Print 打印链表
func (l *ListNode) Print() {
    for l != nil {
        if l.Val > 0 {
            fmt.Println(l.Val)
        }
        l = l.Next
    }
}

func main() {
    m := 1
    n := 4
    root := new(ListNode)//头结点
    p := root
    for i := 1; i 7; i  {//初始化链表
        node := new(ListNode)
        node.Val = i
        p.Next = node
        p = p.Next
    }
    p.Next = new(ListNode)//尾节点
    l1 := root
    var l2, l3 *ListNode
    p = root
    index := 0
    for l3 == nil {//截成三段
        if index == m {
            l2 = p.Next
            p.Next = nil
            p = l2
        }
        if index == n {
            l3 = p.Next
            p.Next = nil
        }
        p = p.Next
        index
    }
    l2 = reversrList(l2)// 反转l2
    list := []*ListNode{l2, l3}
    p = l1
    index = 0
    for index len(list) {//拼接起来
        for p.Next != nil {
            p = p.Next
        }
        p.Next = list[index]
        index
    }
    l1.Print()
}
</code></pre> <blockquote>

作者:血之君殇

链接:https://www.jianshu.com/p/70571ce961b7

来源:简书 

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

</blockquote>

推荐阅读

<ul><li>

2020 腾讯社招 Golang 后端面试题

</li></ul>

欢迎搜索或扫码关注我!

到此这篇关于“go list指针_2020 年字节跳动 Go 面试总结”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
go list指针_2020 年字节跳动 Go 面试总结
Go 开发关键技术指南 | 为什么你要选择 Go?(内含超全知识大图)
go 语言学习历程
Go语言发展历史、核心、特性及学习路线
[GO语言基础] 一.为什么我要学习Golang以及GO语言入门普及
Go语言爱好者周刊:第 78 期 — 这道关于 goroutine 的题
剖析使Go语言高效的5个特性(1/5): 变量的处理和存储
Go 语言十年而立,Go2 蓄势待发
从零开始学习GO语言-搭建Go语言开发环境-快速开发入门第一个小程序
golang静态代码检查_Golang面试题41道

[关闭]
~ ~