【每周小结】2023-Week3
本周,我的工作模式正式从远程办公回到了现场办公。恰逢过年,整体工作节奏放缓,切换的过程很顺畅。
虽然我十分期待远程办公成为常态,但不得不承认,这种模式在中国落地,还有一段路要走。
Go技巧 - 提高ORM使用体验的三个要点
ORM是一个非常高频使用的开发工具。以下图为例,Go程序内与MySQL中,数据存储是异构的 ,这就导致传统开发方式会分成两步:
- 将Go程序中的数据转换成MySQL的
SQL
语句 - 解析MySQL 返回的数据到具体结构体中
这部分的开发有大量重复性的代码,如拼接SQL、数据解析,所以就有了ORM这个概念 - 将内存中的数据结构(对象)与数据库中的表对应起来。一旦映射关系建立,那就可以调用ORM里的CRUD完成日常开发。在Go语言程序中,最常见的就是gorm。
示例
我们以Book作为对象为例,它在Go程序中的定义是:
type Book struct {
Id int64 `gorm:"column:id"`
BookName string `gorm:"column:book_name"`
UpdateTime time.Time `gorm:"column:update_time"`
Status int `gorm:"column:status"`
}
对应MySQL中的建表语句为:
CREATE TABLE `books`
(
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
`book_name` varchar(255) NOT NULL DEFAULT '' COMMENT '书名',
`update_time` datetime NOT NULL DEFAULT NOW() COMMENT '更新时间',
`status` tinyint(3) NOT NULL DEFAULT 0 COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
CHARSET = utf8mb4
COLLATE = utf8mb4_bin COMMENT '书';
完成定义后,我们可以使用gorm
库实现CRUD了。但基于ORM库,开发中还是会高频出现一些奇怪的问题:明明程序没有bug,ORM的操作结果却没有达到预期。例如插入时status
字段是0,没有报错,但查询时缺变成了100。
这类问题,往往是开发者在设计时没有注重 用户认知 导致的,也就是说 现象反直觉、所见非所得。我们今天的话题,将基于此展开:
要点一:程序侧 - 节制地使用ORM能力
ORM往往扩展了很多能力,但大幅度地增加了用户的学习成本与排查问题时的成本。以GORM字段权限控制为例:
type User struct {
Name string `gorm:"<-:create"` // 允许读和创建
Name string `gorm:"<-:update"` // 允许读和更新
Name string `gorm:"<-"` // 允许读和写(创建和更新)
Name string `gorm:"<-:false"` // 允许读,禁止写
Name string `gorm:"->"` // 只读(除非有自定义配置,否则禁止写)
Name string `gorm:"->;<-:create"` // 允许读和写
Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
}
看起来特性很酷,但如果你作为读代码的人,你愿意去读一个结构体中每个Field的tag
详情吗?而且,这种限制藏得很隐蔽,发生问题后排查起来很累。
因此,程序侧的ORM定义,最重要的是能保证程序数据结构与数据库存储结构一一映射,其余特性需要慎用。
慎用不代表不用。 如果能在团队内部形成规范,一方面这个规范能落地到代码里,另一方面也能宣传到各个成员、让大家形成共识,那就能用这些特性提升开发效率。
要点二:数据库侧 - 最简化设计
程序侧的代码对开发者可见,排查问题相对清晰。而如果问题最终是在数据库侧导致的,那么就变得复杂了:
- 技术领域不同 - 数据库存在一定的专业性,经验尚浅的开发者需要一定的经验积累
- 访问权限 - 角色、环境等问题,可能导致排查困难
- 滞后性 - 出现问题的优先排查对象往往是代码,数据库往往会被我们“默认”认为没问题
所以,我们在前期设计数据库侧的内容时,要尽可能地保证简单。我个人的评判标准是:让Go结构体的数据,和MySQL表中的一行数据完全对应,不做额外的工作。
我举两个反例:
- 字段默认值有特殊的含义,如建表时
status
的默认值设置为100- 改进方案:如果100这个值有业务含义,应在Go程序中设置
- 表中增加Trigger,如
status
字段修改为某个值后,自动触发另一个字段的修改- 改进方案:在Go程序中实现这块逻辑
要点三:ORM能力与数据库特性的综合考量
第三个要点最为复杂,它需要结合ORM库的具体能力以及数据库的自身特性来综合考量:ORM的有些特性并不完善,具体在哪实现?
依旧以gorm为例,在用Book
结构体进行多列更新时,无法更新其中的默认值,如
// 官方示例
// 代码原理:Active字段是默认值false,所以不会更新
// 用户认知:因为惯性思维,往往认为这个值会被设置为false
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
我们先不考虑具体解决方案,而是希望大家能认识到ORM的局限性 - 想用一个结构体完全覆盖所有的增删改查场景,是不现实的。选择方案,其实是trade-off
,选择一个团队更能快速理解的策略。
想了解方案的同学,可以参考我之前的博客。
第三点是进阶性质的能力,需要大量ORM与数据库侧的开发经验,今天不作展开。
小结
ORM的使用体验会大幅提升CRUD的开发与维护效率。我比较提倡 在设计时,最简化ORM与数据库侧的特性,只采用其核心的映射能力。
而当简化到一定程度后,我们可以打通两侧的数据结构,如示例中的Book
结构体与books
建表语句。由于MySQL中的数据类型更为复杂,可以维护一个从 解析建表语句,自动生成Go中ORM结构体 的代码生成工具。
实现可以参考博客
编程思考 - 开发者的coding经验
如今的应届生在校或实习时就具备了颇为深厚的编程经验,参加工作后能快速地胜任日常需求,这就引起了老一批工程师的焦虑,不禁怀疑:我们的coding经验究竟有什么价值?
下面,我分享一下个人的思考,会从低到高三个维度进行讲述:
- 代码维度 - 写得好,读得懂:看过、写过的代码多,一方面让自己写代码时可读性提高,另一方面也能适应五花八门的项目风格。
- 功能维度 - 懂需求,善取舍:代码所实现的功能,往往和最终预期有出入,如沟通损耗、认知差异等;而功能实现的过程中往往需要取舍,要理清主次先后。
- 系统维度 - 识风险,促迭代:开发的代码从来就不是孤立的,需要识别出它对系统其余功能是否会产生风险;同时,本次开发也是一个迭代的机会,例如建设更通用的模块、修复一些历史包袱等。
以上三点维度不同,但很难从价值维度区分高低。从这三点来看,一个资深coder对团队的价值非常重要。
工作生活 - 焦虑感的缓解
这几年,我的焦虑感与日俱增,尤其是近两年的行业低谷。面对焦虑,专家们有很多思路,这里分享三个对我帮助最大的方法:
- 多锻炼,既能保证身体能量充沛,又可以释放很多负能量
- 多读书(尤其是心理学),提升心智成熟,坦然地面对不确定性
- 多沟通,与同事、领导、朋友等多种角色,进行真诚的交流
Github: https://github.com/Junedayday/code_reading Blog: http://junes.tech/ Bilibili: https://space.bilibili.com/293775192 公众号: golangcoding
相关文章
- 数据孤岛是业务效率的无声杀手
- 2023展望:新的一年将给大数据分析领域带来什么?
- 阿里云ADB基于Hudi构建Lakehouse的实践
- 大数据在医疗保健领域的使用案例
- 微软增加说明:KB5021751 更新扫描已经 / 即将过时 Office 过程中不会触碰用户隐私
- 2022 Gartner全球云数据库管理系统魔力象限发布 腾讯云数据库入选
- 场景化、重实操,分享一个实时数仓实践案例
- Arctic的湖仓一体践行之路
- 分布式计算MapReduce究竟是怎么一回事?
- 淘系数据模型治理优秀实践
- 大数据分析对医疗保健的影响
- 当我们说大数据Hadoop,究竟在说什么?
- 2022年及以后大数据的五个发展趋势
- 网易严选离线数仓治理实践
- 2023 年数据治理趋势
- 一份“靠谱”的年度经营计划,你学会了吗?
- 漫谈对大数据的思考
- 测试一下,读懂数据的能力,你有吗?
- 用艺术的眼光探索数据之美
- 聊聊数据分析成果如何落地