这些思路说只是想过,但是并没有落地
优化的点就是:批量取、提前取、singleflight 取、局部分发。注意,批量取和提前取对应的还有批量生成和提前生成,思路是一样的。万一面试官问到了如何设计一个高性能的发号器,你也可以回答这些点。
发号器是一种在分布式系统中用于生成唯一ID的工具或服务。它可以确保在多个并发请求或业务流程中生成的ID是全局唯一的,通常用于数据库的主键生成、事务标识符、分布式锁等场景。
批量取
业务方每次跟发号器打交道,不是只拿走一个ID,而是拿走一批,比如拿100个,拿到之后业务方内部自己慢慢消耗,消耗完了再去取下一批。
优点:极大地减轻发号器地并发压力,比如一批是100个,那么并发数就降低为原来的1%了。
缺点:可能破坏递增的趋势。比如说一个业务方 A 先取走了 100 个 ID,然后业务方 B 又取走 100 个,结果业务方 B 先用完了自己取的 ID,插到了数据库里面;然后业务方 A 才用完自己的 100 个
提前取
业务方提前取到ID,这样就不需要真的等待需要ID的时候再临时取。
提前取可以和批量取结合在一起,即提前取一批,然后内部慢慢使用。在快用完的时候再取一批。
同时也要设计一个兜底措施,如果要是用完了,新的一批还没取过来,要让业务方停下来等待。
优点: 提高业务方性能
缺点: 可能破坏递增的趋势
singleflight取
类似于在缓存中应用singleflight模式。假如说业务方A有几十个线程或协程需要ID,那么没有必要让这些线程都去取ID,而是派一个代表去取。这个代表取到之后再分发给需要的线程,这也能降低发号器的并发负载。
思路可以进一步优化,每次取得时候多取一点,后续还有别的线程需要,也不用自己去取了。
局部分发
假如说现在整个实例上有 1000 个 ID,这些 ID 是批量获取的。那么一个线程需要 ID 的时候,它就不再是只拿一个,而是拿 20 个,然后存在自己的 TLB(thread-local-buffer) 里面,以后这个线程需要 ID 的时候,就先从自己的 TLB 里面拿,避开了全局竞争,减轻了并发压力。