代码题
以n个协程,去输出[]string{“a”,“b”,“c”,“d”,“e”, “f”, “g”}。 代码题真得很简单,自己手生的很,竟然没写出来。
|
|
八股文
-
go的map可不可以一边遍历,一边delete。为什么?怎么解决 在Go语言中,不能够同时遍历和修改(包括删除)map,因为在遍历过程中如果对map进行了修改或删除,会导致遍历结果不确定性,甚至会导致程序崩溃。
如果一边遍历一边删除map中的元素,会导致遍历器遍历到的元素和map中实际存在的元素不一致,可能会漏掉一些元素或者重复遍历某些元素,从而影响程序的正确性。 为了解决这个问题,可以采用以下方案:
遍历map时,将需要删除的key保存到一个slice中,然后遍历slice进行删除操作。
|
|
使用并发安全的map,如sync.Map。sync.Map使用了类似于读写锁的机制,可以在不影响读操作的情况下进行删除操作。
|
|
- go里面的context有没有用过?有什么用处 Go语言中的context包提供了一种可以跨API处理请求范围数据、取消信号和截止日期的机制。你可以将一个Context对象作为参数进行传递。所有的goroutine都可以从Context对象中读取参数、取消请求或者完成请求。
以下是context包的常见用途:
-
传递请求范围的值 很多时候,我们需要在请求处理过程中传递一些值,比如请求ID、认证信息等等。使用Context将这些值绑定到一个请求链中,所有处理这个请求的函数或者方法都可以访问这些值,可以避免需要手动将这些值传递给每一个函数的不必要麻烦,并减少因为全局变量引入的副作用。
-
取消处理请求 有时候,当一个请求正在处理时,用户提交了一个取消请求。这时候,我们可以使用Context机制来实现优雅地取消请求。Context被严格限制时间、处理过程,如果当前请求超时、终止、禁止会导致其对Context对应的goroutine发送信号,以支持适当的终止。这种机制可以避免不必要的等待,也可以是你的程序具备可撤销属性,保证系统更加健壮。
|
|
- channel的底层实现? 里面有锁么? 在 Go 语言中,channel 是一种用来在 goroutine 之间进行通信的方法,是非常重要的并发编程工具。本质上,channel 是一种阻塞式的队列,底层是通过一种称为 Wait-based 机制的方式实现的。具体来说,当一个 goroutine 向 channel 发送消息时,它会被阻塞,直到另一个 goroutine 从 channel 中取出了这条消息。同样地,当一个 goroutine 从 channel 中接收消息时,如果此时 channel 中没有消息可接收,该 goroutine 也会被阻塞,直到另一个 goroutine 向 channel 中发送了一条消息。
对于 channel 的底层实现,可以简单概括为:channel 底层是一个带锁的环形队列。
具体来说,当我们使用 make(chan T) 或者 make(chan T, n) 创建一个 channel 时,Go 语言运行时会为它分配内存,并在内存中创建一个 channel 结构体,该结构体里包含了以下几个元素:
buf:一个长度为 n 的 T 类型数组,这是 channel 在底层实现中所使用的环形队列; elemSize:元素大小(即 T 类型的长度); closed:一个 flag,表示 channel 是否已经被关闭; qcount/closed:一个 uint32 类型的变量,表示队列中已有的元素个数或者 channel 是否已经被关闭; dataqsiz:队列的容量,即 channel 可以容纳的元素个数。 在操作 channel 时,编译器会将对 channel 的读写操作转换为对 Go 语言运行时的函数调用。具体来说,发送操作会转换为 runtime.chansend 函数的调用,接收操作会转换为 runtime.chanrecv 函数的调用。
runtime.chansend 函数负责向 channel 中发送元素,其内部逻辑简述如下:
锁住 channel 结构体中的 Mutex; 判断 channel 是否已经被关闭或队列已满,如果是则解锁 channel 并直接返回一个 false,否则继续; 将元素写入队列中,并修改 qcount 变量的值,表示队列中已有的元素个数增加了一个; 解锁 channel,并返回一个 true。 runtime.chanrecv 函数负责从 channel 中接收元素,其内部逻辑简述如下:
锁住 channel 结构体中的 Mutex; 判断 channel 是否已经被关闭或队列已空,如果是则解锁 channel 并直接返回一个 nil 进行流程; 从队列中取出一个元素,并修改 qcount 变量的值,表示队列中已有的元素个数减少了一个; 解锁 channel,并将取出的元素返回。 需要注意的是,在 Go 1.5 之前的版本中,channel 底层是由单向链表实现的,在最新版本中已经变为环形队列。另外,当前 Go 语言运行库中使用了多种加锁机制来保证 channel 的并发安全,包括 Spin Lock、Semaphores 等。 4.协程与线程的区别 6. panic后defer会不会执行 在 Go 语言中,当程序执行到一个 panic 语句时,程序就会停止当前执行流程,并进入到 panic 所在的 goroutine 的 defer 执行阶段。这时,所有在该 goroutine 中延迟执行的 defer 语句都会被执行。
需要注意的是,在执行完 defer 语句后,当前 goroutine 的执行不会继续进行,而是结束掉。这时,运行时会将控制权交给上一层调用栈中的代码,并在那里进行恐慌处理。
下面是一个示例程序的代码,展示了在执行过程中当 panic 被触发的时候 defer 是否会执行。
|
|
|
|
这段代码主要利用了 defer 和 panic/recover 两个关键字。 在 main 函数中,首先定义了一个 defer 函数,用于在 main 函数结束前打印一句话。然后调用了 innerFunc 函数。 在 innerFunc 函数中,也定义了一个 defer 函数,用于在 innerFunc 函数结束前打印一句话。接着,通过 panic 抛出了一个异常。 由于 innerFunc 函数没有进行异常处理,因此该异常会被传递到调用该函数的地方,也就是 main 函数中。在 main 函数的 defer 函数中,首先打印了一句话,然后通过 recover 恢复了异常,并打印了异常信息。 7. zset使用过么?主要用在什么地方 zset 是 Redis 中的一种数据结构,也称为有序集合(Sorted Set)。和 set 类型一样,zset 也是一个字符串集合,不同的是每个字符串都有一个分数(score),表示该字符串的排序权重。 zset 常用于需要排序的场景,比如排行榜、热门文章列表等。通过设置不同字符串的分数,可以实现按照一定规则排序。在 Redis 中,zset 内部使用跳跃表(Skip List)实现,查找、插入、删除的时间复杂度都是 O(log N),性能较高。 zset 不仅支持基本的集合操作,比如添加、删除、查找某个元素,还能根据分数范围查找元素,获取排名、分数等信息。此外,zset 还支持多个元素之间的交集、并集、差集等操作。 8. redis为什么运行的那么快 Redis 之所以运行那么快,主要是因为以下几个原因:
纯内存操作: Redis 是一个基于内存的数据存储系统。相对于传统的关系型数据库,Redis不需要进行磁盘I/O操作,因而具有很高的读写速度。
异步式非阻塞I/O:Redis 使用 I/O 多路复用技术来处理多个客户端请求,并在内部利用单线程的方式来处理这些请求。这种方式可以保证在大量请求时的高性能,避免了同步阻塞I/O所引起的性能瓶颈。
单线程处理: Redis 是单线程的数据处理引擎,这意味着 Redis 不会在并发环境中出现任何锁问题,从而尽可能地避免了锁竞争的问题。
持久化的灵活配置: Redis 提供两种持久化方式,即RDB(快照方式)和AOF(追加文件方式),并支持对不同场景进行不同的配置。
总的来说,Redis在设计的时候,考虑了内存操作的高效,I/O多路复用的高并发,单线程无锁处理的高效性等多个方面,从而可以达到高性能的运行效果。 9. 缓存穿透与缓存雪崩,怎么解决?如果时间不太好控制, 怎么处理 缓存穿透和缓存雪崩是 Redis 中常见的问题,下面是针对这两个问题的解决方案:
缓存穿透的解决方案:
设置布隆过滤器:在缓存层之前增加一个布隆过滤器,用于快速判断请求的 key 是否存在于缓存中,如果不存在,则直接返回,避免了对数据库的查询操作。
缓存空对象:如果查询数据库结果为空,也可以将结果缓存起来,但是缓存时间要短,避免浪费内存。
对于恶意攻击,可设置 IP 黑名单或者限流措施,限制访问频率。
缓存雪崩的解决方案:
设置不同的过期时间:将缓存的过期时间设置为不同的时间,避免同时失效导致的请求全部落到数据库上。
限流措施:对于流量突然增大的情况,可以采用限流措施,例如设置并发访问的最大数量,超过限制则直接返回错误信息。
数据预热:在系统低峰期,可以预先将热门数据加载到缓存中,避免在高峰期缓存失效导致的请求全部落到数据库上。 如果时间无法控制,则可以考虑采用缓存自动刷新机制,例如在缓存过期时自动刷新缓存,避免缓存失效导致的请求全部落到数据库上。此外,还可以适当增加缓存的过期时间,避免缓存过早失效。 10. innodb的存储引擎是什么?相比其他的树有什么优点 11. 什么是复合索引?用的时候该怎么,abc三个字段,只查bc,索引下推 12. kafka如何保证消息的顺序一致性 13. es,如果操作10000条,出现深分页,怎么解决。 使用游标分页:Elasticsearch 支持使用游标分页(Cursor-Based Pagination)的方式进行分页处理,这种分页方式不仅可以提高查询性能,同时还可以避免深分页的问题。游标分页可以利用 Livy API 或 Elasticsearch Scroll API 来实现。
优化查询条件:深度分页的根本原因是要遍历较多的文档来满足查询的结果,因此优化查询条件可以减少文档数量,从而避免深分页的问题。可以尽量缩小查询范围,更精确地定位查询的文档。
使用分页的深度限制:Elasticsearch 默认不允许从搜索上下文(Scroll API)中请求超过10000个文档,这是基于性能的考虑。如果您一定要处理这么多文档,你可以使用 scroll_size 参数进行配置,从而控制它处理的文档数,这样可以避免深分页的风险。
使用 Doc Values 进行排序:使用 Doc Values 进行排序能够加速排序操作,从而避免深分页的问题。通常深度分页是由于大量排序操作导致的,如果使用 DocValues,可以获得更快的排序速度,从而减少深度分页。
总的来说,在 Elasticsearch 中,要避免深分页问题可以通过使用游标分页、优化查询条件、使用分页深度限制和使用 Doc Values 进行排序等方法来解决。 14. mysql事务的隔离级别是什么。