GoFrame 框架 ORM 与 Redis Cache 的简单使用
写在前面
嘛,好久没有写文章了,今年还是第二篇文章,已经五月份了捏,时间过得真快。
我呢,好不容易有了自己的时间,开始写我自己的项目,之前都在弄学校的东西或者在比赛,忙得要死。
现在这个项目用 GoFrame 写的,我已经写了近万行有关 ORM 和 Redis 的代码,结果发现有尼玛更简单的写法(我直接摆烂,焯)。
其实吧,GoFrame 配合 Redis 缓存这个事儿,说简单也简单,说复杂也复杂。主要还是我当时太懒了,文档都没好好看(典型的程序员通病,谁让我们都喜欢直接上手呢 🤷♂️)。
结果就把缓存这块写得跟裹脚布一样,又臭又长。
友情提示:本文默认你已经会 GoFrame 的基础操作了,不会的话…先去补课吧(手动狗头)
项目准备
开始搞事情之前,你需要准备两样东西:数据库配置(废话)和用 gf gen
生成的 ORM 代码(再废话一次)。
数据库配置连接
这玩意儿放在 manifest/config/config.yaml
文件里,就像这样:
1 | database: |
(看到这个配置是不是很眼熟?对,就是最基础的那种,没有花里胡哨的东西)
配置代码生成
这个配置文件藏在 hark/config.yaml
里:
1 | gfcli: |
配置完就可以愉快地 gf gen dao
了,生成一堆代码,爽歪歪~
简单使用
按照 GoFrame 官方文档(我这次真的看了,不骗你),最基础的 ORM 操作长这样:
1 | var userEntity *entity.User |
而单纯使用 Redis 的话,用 g.Redis()
就行:
1 | cacheData, redisErr := g.Redis().GetEX(ctx, fmt.Sprintf("role:name:%s", roleName)) |
看起来很简单对吧?分开写确实没什么难度。
问题来了(重点来了)
但是!(这里要大声说)如果我想要第一次从数据库拿数据,后面都从缓存拿,减少数据库的 I/O 压力呢?
这时候代码就开始变得"有趣"了…我当时写出来的代码,现在看看都想给自己一拳 😅
下面是一个反面教材,展示如何通过 UUID 获取用户信息,缓存不存在就查数据库:
注意哦,这段代码是写在 dao 层的
user.go
文件里的,就是用gf gen dao
生成的那堆代码里。
不要写到 interface 文件里去了(血的教训,别问我怎么知道的)
1 | // MapToStruct 数据转换工具(反正就是个工具函数) |
写完这段代码,我当时还挺得意的(现在想想真是年轻啊 🤦♂️)
更优雅的解决方案(终于来了)
后来我重新翻了翻文档 ORM链式操作-查询缓存,发现自己就是个…算了,不骂自己了。
GoFrame 人家早就给你准备好了缓存功能,我却在这里重复造轮子 🤡
启用缓存(重要步骤)
在你的 main.go
文件里,加上这几行代码就行了:
1 | func main() { |
就这样,缓存就启用了!是不是简单到让人想哭?
使用缓存(爽到飞起)
为了测试效果,我写了个简单的 Debug 接口:
1 | func (c *ControllerV1) BaseDebug(ctx context.Context, req *v1.BaseDebugReq) (res *v1.BaseDebugRes, err error) { |
测试效果(见证奇迹的时刻)
打开浏览器访问 http://localhost:8080/api/v1/debug
,效果立竿见影!
第一次访问:慢吞吞的,因为要查数据库
第二次访问:飞一般的感觉,直接从缓存拿
对缓存参数的深度解剖(技术宅时间)
首先,我们来看看 GoFrame 给我们生成的默认缓存键:SelectCache:fy_system@default#bamboo-service:5828117350005325686
这个缓存键看起来就像是程序员的身份证号码一样复杂,让我们来"解剖"一下这个神秘的字符串:
SelectCache
- 这是在说"我是个查询缓存"(就像在自我介绍)fy_system
- 表名,告诉我们这是哪张表的数据default
- 数据库连接名,就像是说"我来自默认连接"bamboo-service
- 数据库名称,这是我们的"家"5828117350005325686
- 查询条件的哈希值(我猜测的,反正是个很大的数字,像是彩票号码)
当然,如果你觉得这个默认的缓存键太"随意",你也可以给它起个更有意义的名字:
1 | dao.System.Ctx(ctx).Cache(gdb.CacheOption{Duration: 1 * time.Minute, Name: "custom_cache_key"}).Where(&do.System{Key: "author_name"}).Scan(&systemEntity) |
这样缓存键就变成了 SelectCache:custom_cache_key
,简洁多了。
但是等等!这样写有个问题 - 所有使用这个缓存键的查询都会被缓存到同一个地方,就像所有人都用同一个储物柜,容易拿错东西。
所以更好的做法是这样:
1 | var userUUID string |
这样缓存键就是 SelectCache:user:uuid:具体的UUID值
,每个用户都有自己专属的缓存"储物柜"。
说实话,GoFrame 默认生成的缓存键其实挺聪明的,它会根据查询条件自动生成唯一的键,就像是给每个查询都分配了一个专属的"身份证"。
不过,如果你的项目比较复杂,比如:
- 多个系统共用一个 Redis(就像多个室友共用一个冰箱)
- 不同语言的项目混合开发(Java 的 SpringBoot 和 Go 的 GoFrame 在一起工作)
这时候自定义缓存键就很有必要了,否则可能会出现"张三的数据被李四拿走了"的尴尬情况。毕竟 SpringBoot 可不认识 GoFrame 的缓存键命名规则!
总结(终于到总结了)
写完这篇文章,我的内心是崩溃的…之前那些复杂的缓存代码,原来可以这么简单就搞定!
通过本次"踩坑"之旅,我们学到了:
- RTFM 很重要:Read The F***ing Manual,文档真的要好好看(血泪教训)
- 框架缓存香得很:GoFrame 内置的缓存功能简单粗暴,两行代码搞定
- 配置超级简单:
gcache.NewAdapterRedis(g.Redis())
设置缓存适配器.Cache(gdb.CacheOption{Duration: 1 * time.Minute})
启用查询缓存- GoFrame 自动处理所有缓存逻辑
- 性能提升显著:缓存命中后,查询速度飞起来了
最重要的一点:别像我一样,遇到问题就开始重复造轮子。先看看官方文档,说不定人家早就给你准备好了更优雅的解决方案 😂
这种内置缓存的方式,既简单又高效,避免了手动编写缓存逻辑的各种坑。以后用 GoFrame 开发,这就是我推荐的缓存使用姿势!