惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
阮一峰的网络日志
阮一峰的网络日志
S
Secure Thoughts
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threat Research - Cisco Blogs
P
Privacy & Cybersecurity Law Blog
The Hacker News
The Hacker News
H
Heimdal Security Blog
W
WeLiveSecurity
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
The Last Watchdog
The Last Watchdog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
D
DataBreaches.Net
I
Intezer
Webroot Blog
Webroot Blog
C
Cisco Blogs
AWS News Blog
AWS News Blog
博客园 - 聂微东
T
The Blog of Author Tim Ferriss
V
Vulnerabilities – Threatpost
罗磊的独立博客
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
Schneier on Security
Schneier on Security
宝玉的分享
宝玉的分享
博客园 - 叶小钗
PCI Perspectives
PCI Perspectives
D
Docker
Scott Helme
Scott Helme
NISL@THU
NISL@THU
J
Java Code Geeks
B
Blog RSS Feed
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
AI
AI
美团技术团队
Cloudbric
Cloudbric
月光博客
月光博客
P
Proofpoint News Feed
T
Tailwind CSS Blog
Google DeepMind News
Google DeepMind News
小众软件
小众软件
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Cloudflare Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 Golang CAS 操作是怎么实现的 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例) 读《为什么学生不喜欢上学》 OpenAI Prompt Engineering 摘录和总结 读《打造真正的新产品》 VueJS 总结 Linux 自动挂载 alist 提供的webdav FreeBSD 使用 vm-bhyve 安装Debian虚拟机 FreeBSD 和 Linux 网卡聚合实现提速 GPT 帮我搞定了时区转换问题 长任务系统如何处理? macOS/Linux 编译 InputLeap 使用开源软KVM - synergy-core 解决 macOS 终端hostname一直变化问题 KVM 共享 Intel 集成显卡 PromQL 备忘 读《格鲁夫给经理人的第一课》 读《打开心智》 为什么要把复杂的联表操作拆成多个单表查询? 红包系统的设计 MySQL Index Condition Pushdown Optimization Go mod 简明教程 OpenWRT 使用 Android/iOS USB 网络 搭建旁路由 Golang gRPC 错误处理 编写可维护的单元测试代码 OAuth 2 详解(六):Authorization Code Flow with PKCE OAuth 2 详解(五):Device Authorization Flow OAuth 2 详解(三):Resource Owner Password Credentials Grant OAuth 2 详解(四):Client Credentials Flow OAuth 2 详解(二):Implict Grant Flow OAuth 2 详解(一):简介及 Authorization Code 模式 ElasticSearch 学习笔记 三种git流程以及发版模型 错误处理实践 权限模型(RBAC/ABAC) OIDC(OpenID Connect) 简介 任务队列简介 PostgreSQL 操作笔记 使用Drone CI构建CI/CD系统 Golang migrate 做数据库变更管理 使用PostgreSQL做搜索引擎 Nginx 源码阅读(三): 连接池、内存池 Nginx 源码阅读(二): 请求处理 Nginx 源码阅读(一): 启动流程 Go 泛型简明教程 KVM 显卡穿透给 Windows 使用 HTTP Router 处理 Telegram Bot 按钮回调 使用反射(reflect)对结构体赋值 GIN 是如何绑定参数的 你好 2022(2021 年终总结) 用Go导入大型CSV到PostgreSQL 使用 OpenWRT 搭建软路由 使用软KVM切换器 barrier 共享键鼠 SQL 防注入及原理 使用 gomock 测试 Go 代码 gevent不是黑魔法(二): gevent 实现 gevent不是黑魔法(一): greenlet 实现 用 entgo 替代 gorm 应用内使用crontab不是那么方便 单测时要不要 mock 数据库? Sentry 自建指南 用selenium完成自动化任务 用闲置的安卓手机做垃圾电话短信过滤 推荐三个时间管理工具 一次事故反思 当JS遇到uint64:JS整数溢出问题 SQLite3 存储以及ACID原理 Redis源码阅读:pub/sub实现 Redis源码阅读:zset实现 Redis源码阅读:bitmap 位图的运算 Redis源码阅读:set是怎么做交并集运算的?
计算机中的权衡(trade-off)
Jiajun Huang · 2020-01-11 · via Jiajun的技术笔记

太极

计算机中一切都是权衡(trade-off)。所谓权衡,即在两个极端中取一个相对中间的位置,不是非黑即白,而是根据实际情况作出取舍, 也许偏向A多一点,从而偏向B少一点,也许是反过来。

A <------------------------> B
   \                      /
          trade-off

我们来聊几个常见的例子。

(关系型)数据库范式和反范式

而常见的两个极端,就是范式和反范式。所谓范式,就是一定的套路,是一种大家都遵循的行为准则,有时候业界也会称之为最佳实践。 反范式,就是不按套路出牌,反其道行之。如果你是工作了几年的人,细心回想一下,就会发现日常工作中到处都是范式和反范式, 同时到处都是权衡(trade-off)。

数据库设计中的范式和反范式我想大家都应该有所耳闻(可以参考此处)。

如果你仔细读一下 3NF 就会发现,它是冲着数据的低冗余去的,首先通过1NF把数据拆散成最小可拆分粒度,然后通过2NF确定唯一性, 通过3NF消除冗余。加入我们遵守3NF,实际设计中,就会发现为了消除冗余,数据库需要大量使用外键,而外键有这么一个特点,每次 修改数据时,都会去检查另一张表中的数据,从而确保外键的约束。对于传统的数据库使用来说,这没有什么太大的问题,然而放在 互联网中,一旦并发上上来(虽然大部分所谓的互联网公司仍然达不到这个量),外键将迅速成为数据库性能瓶颈。因此,常见的互联网 公司设计使用中,一般都会使用反范式,即主动冗余一份数据到所需要的,并发非常高的表中(更常见的做法是加缓存,但是不在此文 范畴内)。

而至于实际用途中,遵守多少范式,又遵守多少反范式,是一个度的问题,我们需要根据实际情况进行权衡取舍。

常量

经常在代码中看到这么两种设计:

return c.JSON(http.StatusOK, {})

或者是

return c.JSON(200, {})

我们大家都知道,RESTful中会使用HTTP状态码来表示一些返回状态,例如200就是成功,400就是参数错误等等,而我们几乎所有人都 听说过,代码需要高内聚,低耦合,不要使用魔数(magic number)等等,因此很多情况下我们会使用 http.StatusOK 来表示200, 仔细想想,其实这样做有两个坏处,那就是所有地方都要导入定义 StatusOK 这个常量的模块;阅读并理解 StatusOK 的速度没有 200快,而且实际情况中我们常用的80%状态码就那么几个:200, 301, 302, 400, 401, 404, 405等。

从另一方面,当你遇到不熟悉的状态码时,文字的含义和可读性比数字高,例如 402 到底是什么意思?如果使用 StatusPaymentRequired 就会好很多。不过由于团队中并不是所有人都能记住,所以综合下来使用 http.StatusOK 是一个更好的 选择,如果团队能够达成一致,那么无论哪个选择都是OK的。

再例如之前我读Go的源码时,发现源码中到处都是使用 “linux”, “windows”, “drawin” 三个字符串,而不是抽出来成为const, 我就去提了个 issue 问作者们为啥不抽成常量然后导入。

作者的回答很有道理,因为大家都懂这几个词的意思,他们其实也是常量。而导入一个新的模块更加麻烦,这也是一种取舍。

抽象泄漏(abstraction leaky)

抽象,这是CS的代名词。无论是语言,还是数据,这些年来,我们都是冲着抽象一步一步发展。正所谓,计算机中没有什么事情是加一个 中间层解决不了的,如果有,那就加两个。举个例子,我们从最开始的汇编语言,为了屏蔽各种汇编语言的细节,我们提供了高级语言 这层抽象。在高级语言中,为了屏蔽多个语句的实现细节,我们提供了函数这层抽象。

完全不抽象(暴露所有实现细节)和过度抽象正是我们最上面所画的A和B两个极端。完全不抽象的代码,会导致非常低的可读性,以为 它会把我们带入细节的深渊,试想,你无法了解一个系统的全局,怎么可能全面掌握它呢?

而过度抽象则是另外一个反面,每一次抽象都意味着细节的丢失,过度抽象之后,你只知道一个全貌,却无法了解细节,会导致自己 陷入一个似懂非懂的状态,例如当你阅读Go代码时,碰到一个接口(interface),但是你无法找到具体是哪一个实现,这里你就丢失了 很大一块细节,为了掌握整个系统,你需要了解实现细节,但是现在细节却丢失了。

从细节到抽象,和从抽象到细节,其实就是思维过程中,自底向上和自顶向下的两种方式,我们要掌握这两种方式,并且来回切换,才能 更快更好的了解一个东西,自顶向下帮助你掌握轮廓,自底向上帮助你掌握细节,同时运用两种方式才能让你真正的掌握一个系统。

说回正题,抽象泄漏,就是想要做到完美的抽象,却发现做不到,比如ORM,想要屏蔽所有数据库的实现细节,但却发现为了使用数据库 独有的功能,又必须在公有组件上加上每种数据库独有的功能支持。

函数到底拆多细

这是最近和群友们讨论的一个话题(进技术交流群见右侧二维码),函数到底拆多细?如果你感受过一个函数几百行,翻几屏都翻不完的 那种恐惧,大概率以后你自己再也不会写那样的代码了。

而当你见过另一种极端,几乎每一行代码都被拆散成一个函数时,你会了解到另一种痛苦。

人类的思维过程是比较适合一溜顺的,也就是说,你的思维能从头到尾不打断,而不是在各种环境中切换来切换去跳来跳去的。一个 函数中有很多代码时,符合这一点,你可以从头看到尾,然而它有一个很严重的问题,可维护性非常低,并且当长度达到一定程度时, 你需要记忆的上下文(例如前面定义的变量、内部函数等等)变得越来越多,会达到一个点,超过这个点之后,非常难理解这份代码。

拆的过细的函数会导致另一个问题,每次遇到一个函数时,相当于在你的思维里打一个断点,告诉自己,这里需要执行另一个分支。 同样当函数多到一定程度时,就会达到你的人脑上下文内存上限。

所以函数到底拆多细,怎么拆,这个问题是非常主观的一个问题,但是可以肯定的是,它是一种取舍。

这里得提一句,一个好的函数名可以减少这种痛苦。

总结

计算机中,到处都是权衡取舍。