- 博客(45)
- 收藏
- 关注
原创 深入理解高性能字节池 bytebufferpool
工程上有很多需要用到[]byte的场景,例如:1. 当处理 HTTP 请求时,需要创建一个字节切片 []byte 去读取请求体:先把请求数据读到[]byte中,再从[]byte反序列化成请求对象2. 在打日志时,需要先将日志encode成[]byte,再将[]byte输出到writer中在高并发场景中,需要频繁的进行[]byte内存的申请和释放,增大了 GC 的压力,这时候需要采用 “字节池” 来优化
2025-07-15 22:40:20
702
原创 深入剖析 go-zero 分布式缓存
摘要 go-zero缓存框架通过多种机制保障缓存系统的稳定性和可靠性:采用Cache Aside模式处理缓存一致性,权衡了实现复杂性与概率极小的不一致情况;通过设置短暂占位符防止缓存穿透,利用SharedCalls机制避免缓存击穿,并添加随机扰动缓解缓存雪崩;同时提供可观测性支持,通过定时日志统计缓存命中率。源码分析显示其核心逻辑集中在doTake方法中,结合单飞机制控制并发访问,并采用随机过期时间分散缓存失效压力。整体设计体现了工程实践中对性能、稳定性和可维护性的平衡考量。
2025-07-07 00:16:46
824
3
原创 详解分布式事务框架DTM:子事务屏障
DTM分布式事务框架通过子事务屏障技术巧妙解决了空补偿、悬挂和幂等性问题。其核心是利用数据库唯一索引实现"以改代查":在本地建立分支操作状态表,通过插入操作检测事务状态。对于cancel操作,先尝试插入try记录,成功则判定为空补偿;对于try操作,若cancel已插入try记录则判定为悬挂。同时唯一索引约束保证了幂等性。该方案通过数据库事务和锁机制有效处理了并发场景下的竞态条件,避免了传统"先查后改"方案可能导致的悬挂问题,为分布式事务提供了可靠保障。
2025-06-23 00:08:56
836
原创 深入浅出Redis持久化
redis作为内存数据库,数据都在内存里,如果突然宕机,则数据都会丢失(这里假设不使用非易失性内存),redis提供了持久化机制来防止这种情况发生如果将redis仅作为缓存使用,且不需要宕机后快速生成缓存数据,可以不使用持久化机制,还能提升性能RDB(redis database):定时将某一时刻内存中的数据保存到磁盘上AOF(append only file)
2025-01-16 20:52:55
1075
原创 gorm源码解析(三):增删改查流程
前两篇文章介绍了ORM框架的必要性,以及gorm框架的整体设计。本文将深入增删改查流程,看gorm具体是怎么将对象转化为sql,以及将结果解析成对象构造clause构造sql执行sql处理结果
2024-12-18 08:33:30
1023
原创 gorm源码解析(一):ORM概览
ORM(Object-Relational Mapping)是对象关系映射的框架开发者可以使用熟悉的面向对象方式来操作数据库,而无需直接编写繁琐的SQL语句执行增删改时完成从对象到sql的转换。例如当用户create一个对象时,ORM框架需要根据该对象,生成一条INSERT的sql解析查询结果时完成从结果到对象的转换。当从mysql server接收到查询结果时,ORM框架需要将其转换为业务model对象
2024-12-15 18:24:57
794
原创 分布式唯一ID生成(四):tinyid
tinyid的主要特性有:- 生成全局唯一的64位数字ID- 趋势递增的id:趋势递增的意思是,id是递增但不一定是连续的- 支持生成1,3,5,7,9…序列的ID- 支持配置多个db,每次随机从一个db获取号段,提高可用性- 支持client获取一批ID,然后本地发号,提升性能
2024-11-10 18:07:06
1144
原创 分布式唯一ID生成(三):uid-generator
UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器以组件形式工作在应用项目中,和leaf 不同,leaf是提供server集群,client调server获取ID通过特殊的workerId分配策略,解决workerId不好手动指定,以及唯一性问题通过时钟不回退解决时钟回拨导致的ID重复问题通过借用未来时间来解决雪花算法天然存在的并发限制下面依次分析这些特性的实现原理
2024-11-10 11:18:18
1609
原创 分布式唯一ID生成(二): leaf
本文将介绍leaf号段模式和雪花算法模式的设计原理,并走读源码leaf提供了,业务只管调leaf server的接口获取ID,leaf serve内部根据号段或雪花算法生成ID,而不是业务服务自己去请求数据库生成id,或自己根据雪花算法生成idleaf提供两种分布式ID生成策略:每次从db获取一批ID,而不是一个ID,减少调DB的频率用双buffer解决TP999耗时高的问题在内存判断参数biz是否合法,提高校验性能使用动态step,解决突发流量造成对db压力仍然大的问题。
2024-11-09 20:06:34
1522
原创 漫谈分布式唯一ID
在大多数业务场景中,需要对每条数据分配一个唯一ID作为标识。大部分关系型数据库提供了自增主键功能来支持该需求但若数据量较大,需要分库分表时,就不能使用每个数据库实例提供的自增键功能,因为不能保证在所有表中唯一,分布式全局唯一ID的需求应运而生全局唯一性:在某个业务场景下唯一,避免数据冲突,这是最基本的要求高性能:生成速度快,不能阻塞业务流程趋势递增:通常会将该ID作为数据库主键,由于mysql innoDB采用聚集索引,若新增的记录主键无序,可能造成 叶分裂和 空间利用率不高。
2024-11-09 13:22:22
1382
原创 深入理解对象池 sync.Pool
当多个 goroutine 都需要创建同⼀种对象的时候,如果 goroutine 数量过多,导致对象的创建剧增,进⽽导致 GC 压⼒增大。并发⼤-->占⽤内存⼤-->增加GC频率-->限制程序执行效率-->处理并发能⼒降低-->并发更⼤此时就需要有⼀个对象池,每个 goroutine 不⾃⼰调mallocgc从runtime分配对象,⽽是从对象池中获取⼀个对象,用完后再把对象放回池里。sync.Pool就是这样一个对象池,可以减少GC频率和内存分配减少GC频率:内存分配少了,出发GC的频率就会降低。
2024-11-05 22:31:07
1627
原创 本地缓存库分析(五):groupcache
groupcache是一个分布式本地缓存库,根据一致性hash算法决定某个key由自己负责,还是别的节点负责存在本地的缓存用LRU管理内存淘汰这个库只暴露了get接口(传一个fn进去,缓存中Get不到时调fn从其他地方获取数据,放入缓存),而不支持set/update/delete操作。因此适用场景有限,适合那种对于给定key来说,value不会变的数据例如静态文件,拿md5当key,value是文件内容这么做的好处是不会有更新导致的缓存一致性问题,因为就更新不了下面对一些关键设计进行分析
2024-11-05 11:57:22
1111
1
原创 本地缓存库分析(四):fastcache
什么是fastcache?支持高并发支持存储大量数据,但对GC压力小严格内存限制代码非常简单,易于理解本文将剖析其设计及代码实现,版本:v1.12.2问题解决锁竞争严重解决,用了多个segment大量缓存写入,导致gc标记阶段占用cpu多解决,指针数量约等于总数据量/64KB 个内存占用不可控解决,底层数组占用空间是固定的。索引占用的不固定,但索引占用空间小不支持缓存按时效性淘汰按FIFO淘汰不支持缓存过期不支持缓存数据可以被污染解决,使用序列化后的字节数组。
2024-11-04 21:52:19
1199
原创 本地缓存库分析(三):freecache
什么是freecache?0 GC开销支持高并发支持过期时间近似LRU缓存淘汰严格限制内存使用量可以说是功能特性非常全面的一款本地缓存库了,非常适合在工程中使用本文先介绍 freecache 中值得学习的设计细节,然后简要分析主要流程 get 和 set的源码,版本:v1.2.4问题解决锁竞争严重解决,用了多个segment大量缓存写入,导致gc标记阶段占用cpu多解决,不管多大数据量,一共只有512个指针内存占用不可控解决,底层数组占用空间是固定的。
2024-11-04 12:31:02
1156
原创 详解varint,zigzag编码, 以及在Go标准库中的实现
当我们用定长数字类型int32来表示整数时,为了传输一个整数1,我们需要传输00000000 00000000 00000000 00000001 32 个 bits,而有价值的数据只有 1 位。这就导致了大量的空间浪费,因为大部分字节并没有实际存储有效的信息
2024-10-27 23:46:27
1554
原创 本地缓存库分析(二):bigcache
一个本地缓存必然要支持并发访问,如果用一个sync.Mutex锁住整个缓存,会导致整个缓存的读写串行化,性能很低。于是BigCache使用了分片机制表面上bigcache中所有的数据是存在一个大cache里面,但实际上底层数据分成了N个不互重合的部分,每一个部分称为一个shard
2024-10-27 12:54:50
1482
原创 本地缓存库分析(一):golang-lru
如果用go自带的map实现本地缓存,大概有两种实现方式:1. sync.Map2. map + mutex.RWLock但有以下缺点:1. 锁竞争严重2. 大量缓存写入,导致gc标记阶段占用cpu多3. 内存占用不可控4. 不支持缓存按时效性淘汰5. 不支持缓存过期6. 缓存数据可以被污染:如果缓存的V是指针,那么业务修改了V的某个值为当前请求用户自己的值,在缓存中的V就被污染了
2024-10-26 12:10:56
1383
原创 Golang bufio包源码分析
在上一篇文章中提到了用bufio来高效处理输入输出,本文来分析bufio高效的原理主要看bufio.Reader和bufio.Writer如何包装缓冲区和真正的reader,writer,实现高效IO阅读的go版本:1.22.6为啥要用bufio?提高性能:通过开辟一块缓冲区,减少系统调用的次数bufio.Reader:先调一次真正的reader读取一批数据到缓冲区,接下来的read就直接从缓冲区读,无需再调真正的reader发起系统调用。
2024-10-25 19:55:43
1201
原创 Golang 怎么高效处理ACM模式输入输出
最近在练习牛客上单调栈题目时,要求自己处理出入输出,也就是读题库要求的输入,计算最终结果,并打印输出当我用处理输入,用处理输出时:在数据量较大时超时了。。。算法本身已经是最优复杂度了,那问题只可能在输入输出上于是研究了下用go写算法笔试题时,怎么高效处理输入输出读取输入:需要用带缓冲的包装这里用到方法读取每一行,表示一直读,直到遇到换行符为止读取到每一行数据就好办了,按照空格分隔,转换成数字,调算法处理如果在自己的windows电脑写代码,但提交到OJ平台(一般在linux环境执行)时,需要注意两个平
2024-10-22 23:36:44
1121
原创 开源限流组件分析(三):golang-time/rate
根据的分析,令牌桶限流的实现没必要真的准备一个桶,定时往里塞令牌,然后每次获取令牌时从桶中弹出,但这样做,需要开辟桶最大容量大小的空间最佳做法是利用的原理,只用维护一个桶中当前令牌数的变量,本文将分析go官方库提供的限流工具的使用及实现细节,也是令牌桶算法。并对下面这个问题给出合理的解释:在CancelAt方法中,为啥归还令牌时不是归还所有令牌,而是要扣减一个值,版本:v0.7.0。
2024-10-22 12:48:13
1534
原创 开源限流组件分析(一):juju/ratelimit
这篇文章分析下go开源限流组件juju-ratelimit的使用方式和源码实现细节版本:v1.0.2其提供了一种高效的令牌桶限流实现令牌桶相比于其他限流算法(如漏桶算法)的一个显著优势,就是在突发流量到来时,可以短时间内提供更多的处理能力,以应对这些额外的请求直观上来说,令牌桶算法可以实现为:桶用channel实现在后台每隔一段固定的时间向桶中发放令牌要获取令牌时,从channel取数据// 每隔interval时间往channel中塞一个令牌select {// 放令牌。
2024-10-18 08:41:33
1447
原创 kratos源码分析:滑动窗口
在限流器、熔断器中,需要根据过去一段时间内的请求数据来判断本次请求是否可以放行此时就需要一个滑动窗口,用于平滑收集并统计这些数据。对于在窗口时间范围外的数据自动丢弃。
2024-10-15 20:53:00
721
原创 深入理解 sync.RWMutex 实现原理
读写锁sync.RWMutex,是互斥锁Mutex的一个改进版,适用于读取数据频率远大于写数据频率的场景例如,程序中写操作少而读操作多,如果执行过程是1次写然后N次读的话,使用Mutex这个过程将是串行的,带来的问题是:即便N次读操作互相之间并不影响,也需要获取到Mutex后才可以操作。如果使用读写锁,多个读操作可以同时持有锁,将大大提升并发能力一个协程拥有写锁时,其他协程获取写锁需要阻塞一个协程拥有写锁时,其他协程获取读锁需要阻塞一个协程拥有读锁时,其他协程获取写锁需要阻塞。
2023-06-02 22:32:44
1216
原创 Go 连接池的设计与实现
如果不用连接池,而是每次请求都创建一个连接是比较昂贵的,因此需要完成3次tcp握手同时在高并发场景下,由于没有连接池的最大连接数限制,可以创建无数个连接,耗尽文件描述符连接池就是为了复用这些创建好的连接
2023-04-08 13:18:17
1326
原创 go并发编程 —— singleflight设计模式
singleflight是一种并发编程设计模式,将的多个并发请求成一个请求,以减少对下游服务的压力。
2023-04-04 00:23:32
536
原创 深入理解 Go slice 扩容机制
容量小于1024时为2倍扩容,大于等于1024时为1.25倍扩容小于256时为2倍扩容,大于等于256时的扩容因子逐渐从2减低为1.25不管1.18前后,最终需要按照内存分配块向上修正
2023-03-18 14:43:35
1508
原创 深入理解Go函数调用原理
在程序运行的过程中,会涉及到对函数的调用,调用时IP寄存器会指向被调用函数的地址,函数返回后继续执行本函数剩下的代码程序执行单元(线程或者协程)在执行过程中需要记录程序上下文的数据结构,包括局部变量,BP,参数,返回值等。程序用栈帧这种结构来记录这些信息这两个信息存在使用BP,SP和IP中:BP:存储函数调用堆栈基址指针的寄存器,即SP:存储当前函数栈顶的位置,也就是IP
2023-03-12 14:16:08
461
原创 Go开发者常犯的错误,及使用技巧 (1)
变量名要有意义,不能随便取a,b,c如果for语句块简短还好,如果很长,顺着读下去在后面可能都不记得v代表什么,如果将v换成user,会减少很多理解成本
2023-02-18 21:02:13
781
原创 go如何实现服务优雅关闭
什么叫优雅关闭?先说不优雅关闭,就是什么都不管,强制关闭进程,这会导致有些正在处理中的请求被强行中断这样做有什么问题?
2023-02-04 21:59:06
1610
原创 一文搞懂go并发编程设计原理
主要学习其设计原则,大体流程,权衡利弊不要纠结于部分难懂的实现细节,因为不同的人对相同接口的实现细节不一样,就算是相同的人实现两次也可能不一样。
2023-01-22 12:13:39
1071
原创 Gin框架源码分析(1)—— 整体介绍
既然go原生提供了http功能,为啥还需要gin等第三方框架呢?主要还是原生的http不满足实际业务场景的需求,gin主要提供了以下额外的功能
2022-11-05 18:54:46
837
原创 Redis高可用实现原理——集群
redis高可用有3种方式:主从,哨兵,集群集群模式通过分片来解决写热点和数据容量问题,同时支持主从复制功能,解决读热点问题,并提供故障转移功能,实现高可用
2022-10-24 08:48:37
590
原创 redis高可用实现原理——主从,哨兵
单节点系统有明显的缺点,一旦发生故障会导致服务不可用。而且,单个节点处理所有的请求,吞吐量有限,容量也有限Redis实现高可用,通常有三种部署模式:主从,哨兵,集群
2022-10-21 12:18:20
499
原创 详解Go slice底层原理
切片是go中非常重要的一种基础结构,一个切片由3部分组成:指针、长度和容量。指针指向底层数组,长度代表slice当前的长度,容量代表底层数组的长度
2022-05-05 18:02:20
2057
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅