Flink keyBy 为什么要多做一次 MurmurHash?

在 Flink 中做 keyBy 的时候,内部根据 key 选择下游节点时,有这么一段逻辑,代码在 KeyGroupRangeAssignment.java#L75:

int computeKeyGroupForKeyHash(int keyHash, int maxParallelism) {
        return MathUtils.murmurHash(keyHash) % maxParallelism;
}

简单来说,就是根据 key 的 hash 值,计算出 key 的分组。这里需要注意的是,并非直接简单地根据 hash 值取模,而是经过了一次 MurmurHash。

那为什么要这么做,解决了什么问题呢?

问题简单来说就是:keyBy 之后的分组要足够均匀,而用户定义的 keyHash 未必均匀。

举个最极端的例子:M % 10,但 M 的取值全都是 10 的倍数,比如 {10, 20, 30} 等。那无论 M 的取值有多分散和随机,M % 10 都等于 0,那么最终只有分组 0 里面有数据,将会发生严重的数据倾斜。

为什么 MurmurHash 能解决这个问题?

Continue reading

Awesome C++ Blogs

类似于必读书目的书单,那些值得反复阅读、常读常新的技术博客,也应该被系统地整理成一份精选集合。

先开个坑,后续逐步填充内容~

2025

C++ 如何计算普通类型的 Hash 值:基于 gcc/clang 源码分析

当 int/long/float/指针/std::string 作为 std::unordered_map 的 key 时,C++ 底层是如何计算 hash 值的?

gcc/clang 作为使用最多的两种编译器和标准库,它们在这个问题的实现上略有差异。本文将基于二者的源码进行对比分析。

Continue reading

std::any 的性能开销:基于 libstd++ 源码分析

C++17 中引入了 std::any,可以非常方便地将任意类型的变量放到其中,做到安全的类型擦除。然而万物皆有代价,这种灵活性背后必然伴随着性能取舍。

std::any 的实现本身也并不复杂,本文将基于 libstd++ 标准库源码 深入解析其实现机制与性能开销。

Continue reading

从源码角度解读 enable_shared_from_this

我们在使用 C++ 的时候,有时会需要在类的内部获取自身的 shared_ptr,这就会用到 std::enable_shared_from_this。在实际使用过程中,std::enable_shared_from_this 有三个陷阱需要注意:

Continue reading
2024

Context 的错误使用引发 Panic 的问题复盘

我们有这么一段业务代码,在 Gin 的 API Handler 中,开了一个子 goroutine 写 DB,代码大概是这样:

Continue reading
2023

Go 1.22 可能将改变 for 循环变量的语义

几乎世界上每个 Golang 程序员都踩过一遍 for 循环变量的坑,而这个坑的解决方案已经作为实验特性加入到了 Go 1.21 中,并且有望在 Go 1.22 中完全开放。

Continue reading

剖析 Golang Bigcache 的极致性能优化

Bigcache 是用 Golang 实现的本地内存缓存的开源库,主打的就是可缓存数据量大,查询速度快。 在其官方的介绍文章 《Writing a very fast cache service with millions of entries in Go》 一文中,明确提出了 bigcache 的设计目标:

Continue reading

解读 Golang 标准库里的 varint 实现

最近发现 Golang 标准库竟然自带了 varint 的实现,代码位置在 encoding/binary/varint.go,这个跟protobuf里面的varint实现基本是一致的。刚好借助 golang 标准库的 varint 源码,我们来系统地学习和梳理下 varint。

Continue reading
2021

深度分析 Golang sync.Pool 底层原理

sync.Pool 是 Golang 内置的对象池技术,可用于缓存临时对象,以缓解因频繁建立临时对象带来的性能损耗以及对 GC 带来的压力。

Continue reading

os.Chmod 时到底用 777 还是 0777?

问题是这样的:我在代码里面调用了 os.Chmod("test.txt", 777),希望把该文件的读写及执行权限对所有用户开放。

Continue reading

一个 Gin 缓存中间件的设计与实现

我们在开发 HTTP Server 的时候,经常有对接口内容做缓存的需求。例如,对于某些热点内容,我们希望做 1 分钟内的缓存。短期内缓存相同内容不会对业务造成实质影响,同时也会降低系统的整体负载。

Continue reading

高性能服务之优雅终止

「优雅终止」指的是当服务需要下线或者重启时,通过一些措施和手段,一方面能够让其他服务尽快的感知到当前服务的下线,另一方面也尽量减小对当前正在处理请求的影响。优雅终止可提升服务的高可用,减少下线造成的服务抖动,提升服务稳定性和用户体验。

Continue reading

一致性 Hash 原理及 GroupCache 源码分析

一致性 Hash 常用于缓解分布式缓存系统扩缩容节点时造成的缓存大量失效的问题。一致性 Hash 与其说是一种 Hash 算法,其实更像是一种负载均衡策略。

Continue reading

Golang sync.Cond 条件变量源码分析

sync.Cond 条件变量是 Golang 标准库 sync 包中的一个常用类。sync.Cond 往往被用在一个或一组 goroutine 等待某个条件成立后唤醒这样的场景,例如常见的生产者消费者场景。

Continue reading