面试官超级nice,不懂的问题都告诉了我答案,是一家非常喜欢的公司
线程安全map
- 使用 sync.map
- 使用 sync.RWMutex 手动加锁
defer 对程序性能的影响
一般场景下,defer 的性能开销微不足道,但在循环中,手动管理资源释放可能是更优的选择
gc
三色渲染法,写屏障
进程、线程、协程
- 进程: 进程是具有一定独立功能的程序,进程是系统资源分配和调度的最小单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
- 线程: 线程是进程的一个实体,线程是内核态,而且是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
- 协程: 协程是一种用户态的轻量级线程,协程的调度完全是由用户来控制的。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
协程如何释放CPU
- 协程在执行 I/O 操作、阻塞调用、同步操作(如互斥锁、channel 等)时会自动释放 CPU。
- 可以使用 runtime.Gosched() 主动让出 CPU,让 Go 调度器去调度其他 goroutine。
- 从 Go 1.14 开始,Go 支持抢占式调度,即使没有显式释放 CPU,Go 运行时也能强制调度其他 goroutine
内存泄露分析工具
可以通过Go自带的工具pprof或者使用Gops去检测诊断当前在系统上运行的Go进程的占用的资源。
B 树和 B+树的区别
- 在B+树中,叶子节点包含了所有的数据记录,而B树的数据记录分布在所有节点上(包括非叶子节点)。
- B+树的叶子节点之间有链表结构,便于顺序遍历,而B树没有这样的结构。
聚簇索引和非聚簇索引
- 聚簇索引将数据的物理存储顺序与索引顺序一致。也就是说,表中的数据按索引列的顺序进行存储,聚簇索引不仅存储索引,还决定了表中数据的物理排列方式。
- 聚簇索引不需要回表
- 非聚簇索引是将索引与数据分开存储,索引的结构中保存了指向实际数据的指针(即物理地址),通过索引查找需要先查索引,再根据指针去访问实际的数据行。
- 非聚簇索引需要回表
mysql默认隔离级别
可重复读
redis大key会有什么影响
大key(即占用内存较多的键值对)可能会对系统性能、内存使用和管理带来一些负面影响。
大key在Redis中会导致内存消耗增加、操作阻塞、网络瓶颈、持久化和主从复制延迟等问题。为了避免这些问题,最好在设计时避免创建大key,定期监控并优化大key的使用。同时可以通过分片、TTL等策略,降低大key对系统的负面影响。
后续又投了一波,居然又给了一次面试的机会,可惜没把握住
波场的带宽和能量
在波场(TRON)网络中,带宽(Bandwidth)和能量(Energy)是两种资源,用于不同类型的操作和消耗,分别处理交易费用和智能合约执行的成本。它们的设计目的是为用户提供灵活的资源使用模式,避免单一的交易费用模式。
1. 带宽(Bandwidth)
- 用途:用于支付普通的转账交易,例如 TRX 代币的转移等。
-
获取方式:
- 每个账户每天会自动获得一定的免费带宽(通常是 1500 带宽),但这些免费额度可能不足以支付较频繁的交易。
- 额外的带宽可以通过冻结 TRX 获得,用户在冻结 TRX 后可以按比例获得更多的带宽资源。
- 若带宽不足,也可以支付 TRX 手续费来完成交易。
- 计算消耗:每次交易会消耗带宽,消耗量取决于交易的大小(以字节为单位),一般为每字节 1 带宽。
2. 能量(Energy)
- 用途:用于支付智能合约的执行费用,包括合约的创建、调用和运算等操作。
-
获取方式:
- 与带宽类似,用户可以通过冻结 TRX 获得能量,且冻结的 TRX 不同于获得带宽的部分。
- 用户也可以选择直接支付 TRX 来支付能量消耗的费用。
- 计算消耗:每次调用智能合约都会消耗能量,消耗量取决于合约的复杂性和计算需求。消耗的能量越多,合约执行的成本也越高。
带宽与能量的差异
资源类型 | 用途 | 获取方式 | 计算方式 |
---|---|---|---|
带宽 | 普通交易的手续费 | 每日免费带宽、冻结 TRX 获取、直接支付 TRX | 根据交易大小消耗带宽 |
能量 | 智能合约的执行费用 | 冻结 TRX 获取、直接支付 TRX | 根据合约复杂度和运算需求消耗能量 |
3. 使用场景和优势
- 免费交易:通过冻结 TRX,用户可以减少支付 TRX 手续费的需求,尤其适合频繁交易和合约操作的用户。
- 智能合约:能量专门用于合约,适合合约调用量大的用户,通过冻结 TRX 获取能量可以有效减少直接支付的 TRX 成本。
B+树的高度是什么决定的
B+ 树的高度主要由树的阶数(degree)和存储的数据量(节点数)决定。B+ 树是一种自平衡的多路查找树,通常用于数据库和文件系统中,因为它能高效地进行大量数据的插入、删除和查询操作。
1. 树的阶数(degree)
-
阶数定义:B+ 树的阶数(
m
)指每个内部节点最多可以有m
个子节点,或者说每个节点中可以存储最多m-1
个键。 - 对高度的影响:B+ 树的阶数越高,单个节点可以存储的键越多,意味着每次查询时可以“分流”到更多的子节点。这样树的分叉系数更高,树的深度就会更小。因此,树的阶数越大,B+ 树的高度越低。
- 实际应用:在数据库和文件系统中,阶数通常设得较大(例如 100 以上),以减少树的高度,从而减少磁盘 I/O 操作,提升查询性能。
2. 数据量(节点数)
- 影响高度的原因:B+ 树是平衡树,随着存储数据量的增加,树会自动保持平衡,通过增加树的高度来适应更多的数据。
-
高度关系:假设 B+ 树的阶数为
m
,树的高度为h
,则 B+ 树在最坏情况下存储的数据量N
可以达到:
[
N \leq m^h
]
因此,树的高度h
与数据量N
的关系为:
[
h \approx \log_m(N)
] - 数据量越大,树越高:在阶数固定的情况下,数据量越大,树的高度就越高,但由于 B+ 树的分支系数大,高度增加相对较慢。
总结
B+ 树的高度主要由树的阶数和数据量共同决定。较大的阶数可以显著减少树的高度,而随着数据量的增加,树的高度会逐渐增加。因此,实际应用中通过选择合适的阶数,可以在存储容量和查询效率之间取得平衡。
三色渲染的根节点
在三色标记算法中,根节点指的是从哪里开始遍历和标记的起点。通常,根节点包含了程序中的所有活跃变量和全局引用,即那些能够直接或间接引用到堆内存中对象的变量。三色标记法主要用于垃圾回收,通过从根节点开始遍历来确定哪些对象是可达的,哪些对象是垃圾。
根节点的来源
根节点的典型来源包括:
- 全局变量和静态变量:这些变量在程序生命周期内存在,始终处于可达状态。
- 栈中的活动变量:当前函数调用栈上的变量,包括局部变量、参数等。
- 寄存器中的变量:在处理器寄存器中存储的指针或变量值。
5个协程并发,发送随机数量的随机数给另外一个协程,求和并打印结果,要求空间复杂度为O(1)。
func main() {
ch := make(chan int)
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
j := rand.Intn(10)
for k := 0; k < j; k++ {
ch <- rand.Intn(10)
}
}()
}
go func() {
wg.Wait()
close(ch)
}()
sum := 0
for num := range ch {
sum += num
}
fmt.Println(sum)
}
滑点
在 Uniswap 等去中心化交易所(DEX)中,滑点(Slippage)指的是交易执行时的价格与预期价格之间的差异。滑点通常发生在市场波动较大或交易量较高时,由于流动性和价格波动的影响,实际成交价格可能与预期不同。
滑点的原因
滑点主要由以下因素引起:
- 价格波动:加密市场波动性较大,价格在提交交易与执行交易的时间差内可能发生显著变化。
- 流动性池深度:在 AMM(自动化做市商)机制中,Uniswap 的价格由流动性池中的两种代币的比例决定,交易会改变池中代币的比例,影响价格。
- 交易规模:大额交易会显著影响流动性池的代币比例,导致更大的价格变动,即更高的滑点。
如何应对滑点
Uniswap 允许用户设置“滑点容忍度”,即用户可以接受的滑点范围(通常为 0.1% - 1%)。如果滑点超过设定值,交易会自动取消,以避免过度滑点带来的不利价格。
滑点计算
滑点通常表示为百分比,计算方式如下:
[
\text{滑点} = \frac{\text{期望价格} - \text{实际成交价格}}{\text{期望价格}} \times 100%
]
例如:
- 如果用户希望以 100 USDT 购买某代币,但实际成交价是 98 USDT,则滑点为 2%。