zclll's blog - zclll
type: channel
link: https://zclll.cn/index.php/author/1
description: zclll
items: [翻译] 入土为安——jemalloc生平
items: 如何在 Mac 下 PostgreSQL@16 中启用 PostGIS？
items: 作为新手，如何参与 Apache Doris 项目
items: 借助 Windsurf Sonnet Debug 过程一例
items: Authentication plugin 'mysql_native_password' cannot be loaded 解决方案
items: 使用Certbot在CentOS上更新和续签Nginx SSL证书
items: 德州扑克新手进阶指南
items: Linux更新时区文件，解决夏令时和Africa/Casablanca问题
items: 2023开发者生态，一些有趣的事实（偏C++向）
items: 新年贺词20年

[翻译] 入土为安——jemalloc生平
type: item
date: 2025-07-05T18:26:00+00:00
description: 本文翻译自 jemalloc Postmortem，原作者 Jason Evansjemalloc 内存分配器 最早构思于 2004 年初，并且已经公开使用了大约 20 年。由于开源软件许可的性质，jemalloc 将会永久保持公开可用，但是上游的活跃开发已经结束。本文简要描述 jemalloc 的开发阶段，每个阶段都有些成功或失败的亮点，最后附有一些回顾性的评论。Phase 0: 源于 Lyken2004 年的时候，我开始在科学计算的背景下开发 Lyken 编程语言。这门语言最终挂了，但是它的手动内存分配器到 2005 年 5 月已经功能完整。（不过，原本打算在其上实现的垃圾收集器从未完成）2005 年 9 月，我开始将分配器集成到 FreeBSD 中，到 2006 年 3 月，我移除了 Lyken 中的分配器，转而使用系统分配器的一个简单封装版本。为什么在投入了这么多努力之后，还要移除从 Lyken 中集成的内存分配器？其实我们刚把这个分配器集成到 FreeBSD 中，就发现其实系统分配器所唯一缺少的就是一个统计、跟踪内存分配量的功能，目的是能够触发线程级别垃圾回收。而这可以通过使用线程特定数据和 dlsym(3) 的简单包装来实现。有趣的是，许多年后 jemalloc 添加了 Lyken 当初缺少的统计收集功能。Phase 1: 进入 FreeBSD时间回到 05 年，FreeBSD 当时拥有 Poul-Henning Kamp 设计的卓越的 phkmalloc，但是这个分配器没有提供多线程环境的合适规定，而 Lyken 的分配器看起来是一个明显的改进，并且颇具可扩展性。在朋友和同事的鼓励下，我把它集成了过来——很快，人们开始把它称作 jemalloc。但这进展有点儿太快了——集成后不久，我们就明显发现 jemalloc 在一些负载下存在严重的碎片化问题，尤其是由 KDE 应用程序引起的那些。就在我以为 jemalloc 的迁移差不多胜利了的时候，这个现实场景中的失败让 jemalloc 的可行性受到了质疑。简要来说，碎片化问题源于使用统一的范围分配方法（即没有大小类分离）。我从 Doug Lea 的 dlmalloc 中获得了基本灵感，但没有采用那些经过实战检验的交织启发式算法，这些算法避免了许多最严重的碎片化问题。随后我们在这方面疯狂搞研究和实验。到 jemalloc 成为 FreeBSD 版本的一部分时，其布局算法已经完全改变，改为使用大小分离的区域，如 2006 年 BSDCan jemalloc 论文中所述。Phase 1.5: 整合 Firefox到了 07 年11月，Mozilla Firefox 3即将发布，高度碎片化是一个未解决的问题，尤其是在微软的 Windows 上。因此，我们花了一年时间与 Mozilla 合作处理内存分配问题。将 jemalloc 移植到 Linux 很简单，但 Windows 就是另一码事了。jemalloc 的官方源代码在 FreeBSD libc 库中，所以我们基本上分叉了 jemalloc 并添加了可移植性代码，将任何与 FreeBSD 相关的代码都推送到上游。整个实现仍然在一个文件中，这减少了分叉维护的困难，但肯定是在这个阶段中的某个时间，整个实现的复杂性已经超出了单文件所能承载的范围了。多年后，Mozilla 开发者对上游 jemalloc 做出了重大贡献，以摆脱他们需要自己维护一个专门分支的处境。但不幸的是，Mozilla 的基准测试一直显示，他们的分支版本优于上游版本。我不知道这是否是过拟合的问题，还是说性能上存在实际的差异，但这仍然是我对 jemalloc 留有的最大遗憾之一。Phase 2: 在 Facebook在 2009 年加入 Facebook 工作后，我惊讶地发现阻碍 Facebook 基础设施中广泛使用 jemalloc 的最大障碍是监控。关键的内部服务处于一种尴尬的境地：我们依赖 jemalloc 来控制内存碎片，但工程师们需要使用 tcmalloc 和 gperftools 中的pprof堆分析工具来调试内存泄漏。因此兼容pprof的堆分析功能就成了 jemalloc 1.0.0 版本的重中之重。jemalloc 开发迁移到了GitHub，接下来几年中，随着不断出现的问题和机遇，开发断断续续地进行着。其他开发者开始贡献重要的功能。我们在 3.0.0 版本引入了广泛的测试基础设施，以及对 Valgrind 的支持。4.x 系列引入了基于衰减的清除机制和 JSON 格式的可观测性能力。5.x 系列为了更好地与 2MiB 的巨页交互，我们从块（chunk）迁移到了块范围（extent）。在某种程度上更具争议的是，我在 5.0.0 中移除了对 Valgr…

如何在 Mac 下 PostgreSQL@16 中启用 PostGIS？
type: item
date: 2025-04-10T13:22:00+00:00
description: 答案是：不太行。（必须得手动编译，参考enable-postgis-extension-for-postgresql-16-on-mac-m2）所以本文是标题党。其实我要说的是如何切换到 pg14 并让 postgis 正常跑起来。首先我们在 pg16 中会遇到如下问题：postgres=# CREATE EXTENSION postgis; ERROR: extension "postgis" is not available 描述: Could not open extension control file "/opt/homebrew/opt/postgresql@16/share/postgresql@16/extension/postgis.control": No such file or directory. 提示: The extension must first be installed on the system where PostgreSQL is running.这个问题的原因是 postgis 没有把插件所需的内容正确放到 pg16 所需的地方，那么我们需要手动 copy 一下：cp /opt/homebrew/Cellar/postgis/3.4.3/share/postgresql@14/extension/* /opt/homebrew/opt/postgresql@16/share/postgresql@16/extension/然后再启动就会发现版本不适配：postgres=# create extension postgis; ERROR: PostGIS built for PostgreSQL 14.0 cannot be loaded in PostgreSQL 16.4 背景: PL/pgSQL function inline_code_block line 15 at RAISE这个时候我们重新下载一个 pg14 就可以了，不同版本的软件在 homebrew 中可以同时存在：brew install postgresql@14 brew services stop postgresql@16 brew services start postgresql@14这样就可以正常在 pg14 当中使用了：postgres=# create extension postgis; CREATE EXTENSION postgres=# SELECT ST_Touches( ST_GeomFromText('LINESTRING(0 0, 1 1)'), ST_GeomFromText('LINESTRING(1 1, 2 2)') ); st_touches ------------ t (1 行记录)

作为新手，如何参与 Apache Doris 项目
type: item
date: 2025-02-18T13:53:00+00:00
description: 全职做 Apache Doris 开发的第三年，也是带社区同学参与 Apache Doris 开源项目的第三年。过去一年里我带了大约十多位同学参与了开源项目，获得了实际的产出，也有两位同学进入了 SelectDB 全职进行 Apache Doris 的项目开发。这件事情老实讲对我个人并没有什么裨益——它不构成我的工作产出，项目也不归我所有。但是在有限的时间内，我还是希望能够回馈到社区。通过 good first issue 的方式来链接在校生的一小部分，和业界的一小部分。你的大部分疑问会在下面得到解答。做什么首先需要介绍一下什么是 Apache Doris，以及社区的运行机制。Apache Doris 是一个分布式的实时 OLAP 数仓，关于它的详细介绍你可以在网上随处找到。简单来说，Doris 是目前国内性能最佳的 All-in-One 数仓和 OLAP 引擎。除此之外，和你比较相关的是，作为一个开源商业化项目，它有着现在罕见的社区活跃度。这指的是它不仅接受几个核心贡献组织的开发，而且对于自发参与的个人开发者非常友好。作为 Apache 的开源社区，项目不从属于商业公司，而是 Apache 基金会，它依基金会的规章而行事。但是实际的维护基本上依赖一些背后的商业公司。作为开源社区，我们将几乎所有开发内容公开贡献出来，与很多你耳熟能详的国内大公司一起共建，包括腾讯、百度、京东、美团等。这其中也不乏个人开发者。而 good first issue 就是个人开发者参与开源的良好第一步。什么是 Good first issue？这指的是一部分较为简单，且涉及模块较少，比较独立的开发任务。对于新手来说，完成这种任务的开发不需要太多知识和复杂的 debug 过程。对于社区来说，这种较为简单的工作交给同学们可以有助于我们专注于更重要且复杂的主线任务。这样同学们可以从中掌握经验、快速成长，社区也不必花费过多精力解决一些琐碎问题，这是一个绝对的双赢。于是现在许多开源社区都有了 Good first issue。这些任务都会有专门的 mentor 带领你入门。为什么做这个问题换句话说就是，我能从中开源开发中得到什么。参与开源项目，构建软件世界的梦想和成就感——如果你认同这一点，那么这就是你的答案了。但是对大多数同学来说这可能比较虚，所以我们需要说下面几点：一个超过绝大多数“项目经历”的“项目经历”。现在 CS 越来越卷，就我个人看到的（过去一年我看了50-100份 DB 相关的校招简历），什么 15445 6.s081，以往的闪光点近乎成为标配。但开源至今为止还没有。几乎所有简历上的项目都不如你实际参与过大型开源项目的经历。这是一个绝对的闪光点大型项目协同开发的经历。在你完成代码的过程中，你会经历很多非常实际的问题，例如 IDE 的、git 的。这些东西是你个人开发几乎遇不到的角落，但它们很重要。如果你没有相关经历，那么在下一段工作中你需要在这上面花费不少时间。当你提交 PR 之后，你会接受社区其他成员的 review，以我的经验，代码至少需要修改 4-5 次。这个过程中你会意识到，很多理所当然的东西，其实需要反复推敲斟酌。当你解决掉这一切问题，你的能力已经到了一个新的台阶。谁适合参加首先，good first issue 绝不是什么困难的东西，但对于全职员工的产出来说 ROI 可能偏低，但对于没有相关经验的同学来说它又能极大地丰富自己的简历和就业条件，所以从这个角度来说，应当是：在校生等缺乏大型项目实际经验的人——这对各位来说有很大帮助另外对于项目本身来说，我们需要：能快速上手的人。这些任务都比较简单，有大量可以参考的例子，需要你有充足的时间，能快速去做，然后解决实际的问题并达成目标。需要什么对于 good first issue 来说，基本上不需要什么技能。常见的编程语言如 C++ 和 Java，熟悉它的基本语法即可。如果有什么不会的，你可以在使用中学到它们。但有一些技能是你需要掌握的：学会借助 GPT 解决问题。你会遇到很多和命令行、git 等有关的问题，其中绝大多数都可以在正确描述的前提下用 GPT 解决，省时省力。参考例子。你参与的绝大多数任务都会有极其相似的其他例子，请先参考并尝试理解他们。沟通。如果遇到问题，首先尝试自己解决，如果确实无法解决，feel free to communicate。准确描述你的问题上下文，会更容易得到解答。在整个开发的过程中，你需要时不时和 mentor 同步你的进展。动手能力。快速去做，而不是说。好了，以上问题都是针对参与开源的泛泛之谈，接下来是你如何参与到 Doris 开发之中的步骤，也基本上是我写这篇文章的原因——这样大家就可以来这里看到一些必须的内容，节省了我复制粘贴的时间。环境搭建首先我们需要一…

借助 Windsurf Sonnet Debug 过程一例
type: item
date: 2025-01-25T14:36:00+00:00
description: 背景近日笔者在为 Apache Doris 支持字典表功能，主要专注于 FE 的管理模块和优化器相关的查询计划生成。期间有一个 bug 由于笔者对 GSON 的机制毫无了解，花费很长时间未解决，遂尝试使用 Windsurf(调用模型为 Sonnet)进行分析。结果令笔者大为震撼，现记录如下，或许对当前大模型在大型 codebase 下能力边界的认知，有一定帮助。问题这里有一个核心类 Dictionary，它继承自 Table，who has 成员 rwLock。这个 Dictionary 和基类在现版本都是通过 GSON 序列化完成持久化的。也就是为名字添加了 @SerializedName 注解，然后利用GsonUtils 完成 read 和 write 对象。基类 Table 中， rwLock 对象在每一个构造函数中都进行了初始化。然而问题来了，我的 FE 在重启之后，Dictionary 对象通过基类拥有的 rwLock 却总是失效的。也就是说序列化与反序列化出了问题。public class Dictionary extends Table { @SerializedName(value = "dbName") private final String dbName; @SerializedName(value = "sourceCtlName") private final String sourceCtlName; @SerializedName(value = "sourceDbName") private final String sourceDbName; @SerializedName(value = "sourceTableName") private final String sourceTableName; ...... @Override public void write(DataOutput out) throws IOException { String json = GsonUtils.GSON.toJson(this); Text.writeString(out, json); } public static Dictionary read(DataInput in) throws IOException { String json = Text.readString(in); return GsonUtils.GSON.fromJson(json, Dictionary.class); } } public abstract class Table extends MetaObject implements Writable, TableIf, GsonPostProcessable { protected MonitoredReentrantReadWriteLock rwLock; public Table(TableType type) { this.rwLock = new MonitoredReentrantReadWriteLock(true); ... } public Table(long id, String tableName, TableType type, List<Column> fullSchema) { this.rwLock = new MonitoredReentrantReadWriteLock(true); ... } ...... }Sonnet Works接下来通过多轮问答，Sonnet 阅读代码库并不断产生猜测，并由我来验证。让我们看看它做得如何。Question 1我的 @Dictionary 继承自 @Table.java ，在Table的每一个构造函数中rwLock都会被赋值，但是我在将一个Dictionary作为TableIf执行.readLock()却报错rwLock为null，请浏览上下文以及任何可能的有关文件，帮我尝试找到这是为什么Answer 1让我帮你调查这个问题。首先，我需要查看Dictionary类和Table类的实现来了解继承关系和rwLock的初始化情况。首先让我查看Dictionary类：Analyzed code item Dictionary.java:Dictionary现在让我查看Table类：Analyzed Table.java:(1-101)让我继续查看Table类的其他部分：Analyzed Table.java:(101-201)从代码中我发现了一些重要信息：Table类中的rwLock是在构造函数中初始化的，有两个构造函数：Table(TableType type)Table(long id, …

Authentication plugin 'mysql_native_password' cannot be loaded 解决方案
type: item
date: 2025-01-19T07:54:30+00:00
description: 使用 mysql 客户端登录时候报错，问题发生实际原因是 mysql9.0 不再默认包含 mysql_native_password 插件了。它的密码哈希存储方式被证明是不可靠的。采用更新的可靠验证方式，如 caching_sha2_password，需要连接的对端进行支持。如果对端支持，那么 mysql 协议握手时可以自行确认。但是如果你要连接的服务端不是 mysql 且不支持新的认证协议，那么就需要回退 mysql 版本。在 Linux 下安装一个老版本客户端即可。如果是 MacOS 那么可以这样方便地执行：brew install mysql-client@8.4 brew unlink mysql brew link mysql-client@8.4

使用Certbot在CentOS上更新和续签Nginx SSL证书
type: item
date: 2024-12-15T10:43:55+00:00
description: Let's encrypt 官网)并未提供 CentOS 的傻瓜式安装步骤，这里我们主要参考这一篇安装之后使用这样的命令生成证书给 Nginx 使用：sudo certbot certonly -d yourdomain.com -d *.yourdomain.com --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory中间每一步按提示操作即可。会需要为 DNS 配置 TXT 记录。成功以后 Certbot 会提示证书的生成目录，查看一下：[root@xxx nginx]# tree xxx/zclll.com/ xxx/zclll.com/ ├── cert.pem -> xxx ├── chain.pem -> xxx ├── fullchain.pem -> xxx ├── privkey.pem -> xxx └── README其中 cert.pem 为公钥，chain.pem 为中间链证书，fullchain.pem 为二者组合。privkey.pem 为私钥证书。因此在 Nginx 的配置文件 nginx.conf 中，为对应 server 做如下配置即可：ssl_certificate = fullchain.pemssl_certificate_key = privkey.pem然后重启服务即可正常运行。续签证书通过 --manual 方式生成的证书是无法通过 certbot renew 自动更新的（因为中间需要手动完成一些操作，就像我们刚完成过的一样），除非通过 --manual-auth-hook 提供一个交互式的脚本。简单起见我们可以选择通过certbot certonly --standalone命令手动更新。选择 renew 即可。

德州扑克新手进阶指南
type: item
date: 2024-08-11T16:11:00+00:00
description: 最近几年德州扑克（Texas Hold'em）在国内流行度有显著上升，尤其是在IT和金融行业。很多人在玩了一些手牌之后，会逐渐思考自己的打法，尝试从一名纯娱乐玩家转变为一种尝试真正理解这个游戏的业余玩家。那么这篇文章就是为你而准备的。德州扑克当中有两个核心的概念，或者说，仅有两个核心的概念。一切的手牌发展和玩家博弈都是围绕着“位置”和“范围”进行的。接下来我们依次讲解：位置位置指的是你在当前轮次的座位，假设我们有一个七人桌，那么位置从小盲起依次是SB、BB、UTG、MP、CutOff、Hijack、Button。当你刚开始接触德扑，你可能关注自己手中的牌有多好（绝对牌力），以及在翻牌后有没有中到牌。但当你进行了足够多的游戏，你必须明白——在不同的位置进行游戏，你可以玩儿的手牌，以及下注的方式都是完全不同的。1如果你在前位，比如说，枪口位，拿到了一对口袋2，你是否应该入池呢？你会觉得都中了对子，无论如何也不是翻前应该放弃的牌，所以你选择入池。那么当你平跟入池（limp）以后，你期望后位的对手们会如何行动呢？比如说，他们有人做了很大的开池（open-raise），然后还有人做了3-bet，现在轮到你了，你会怎么做呢？这些主动操作的对手之中，是否有人会拿着比你大的口袋对呢？你不知道，但这很有可能，对吧？在绝大多数的情况下，你的这手牌带来的唯一效果就是，直接损失了自己limp的筹码。因为你完全无法反击后位玩家们的操作。他们可能有更大的口袋对，然后推回来3-bet甚至4-bet，你傻眼了。现在你需要选择扔进去更多筹码面对自己几乎不可能有利的翻牌，或者接受自己刚刚的limp打水漂。如果你硬着头皮跟注了，那么让我们看看翻牌之后会发生什么，假设是AKQ这种牌面，那些open和跟注很大尺度的人，他们会一个人都没有中牌吗？这显然不太可能。即使是78J这种排面，你也不知道会不会有人中牌了，他们只要有人中牌，就会比你大。那么接下来的故事就是，你会弃给对手的任意一个操作。你看，你的一个平跟入池，在完全不知道后位玩家会怎么做的情况下，把自己陷入了一种非常尴尬的境地。大多数时候，面对会玩的对手，你只能被对手的加注剥削，白白损失自己的跟注（相比于上头去继续跟注看翻牌，这反而算是好事儿了）。[scode type="blue" size=""]简单的思考：既然这种操作会让人如此难受，那我在后位的时候是否应该多多益善地加注或诈唬，让前位对手陷入尴尬境地？[/scode]2那么你同样拿着一对口袋2，但你是Button位，前面的玩家要么过牌，要么弃牌，现在到你行动，你可以如何操作？显然你知道前面的玩家没有足够大的牌，那么你的对2是不是已经是最强牌力了？如果你希望更少的人看到翻牌以便赢下这手牌，你可以轻松的下足够大的注，在必要的时候，甚至ALL IN也是一种选择。[scode type="yellow" size=""]为什么Allin都可能是一种选择，但limp仍然不是？[/scode]又如果前面的玩家操作的非常激烈，打出了3bet、4bet，仍然很多人跟注，你该怎么办？这个时候，我相信是一个标准的easy fold。因为你知道了比在前位所能知道更多的信息，你猜到了对手可能是些什么牌，所以你的决定总是“更正确的”。这就是位置的重要性。你现在知道了，在不同的位置，即使是相同的牌，采取的行动和获胜的机会也是截然不同的。所以，在前位频繁地平跟入池，也绝对不是一个好的策略。这恰恰是新手最常犯的错误，他们总是想要看一眼翻牌，然后再根据是否中牌决定是否下注。但是后位有经验的玩家会告诉你——看翻牌是要有代价的。通过上面的例子很容易理解，先于对手做决策，和后于对手做决策，会面临完全、完全不同的处境。这本质上是“信息”的重要性。除非你拿着那些超强的牌，例如AA、KK，否则在翻前的位置，比你手里的牌，更有资格决定你应该如何行动。范围德州扑克是一种概率游戏。所谓概率，指的并不是运气。运气是指，这个游戏我有30%、50%或者70%的概率赢下来，但我归根结底得听天由命，就像彩票一样。而概率是指，任何玩家在任何一手牌上都有可能输或者赢，但如果你打得比桌子上其他玩家好，那么当手牌数足够多，你就会长期较稳定地必然盈利。（也就是我们所说的期望收益，EV）这种概率，本质上就是来自于德州扑克的核心——“范围”。什么是范围，事实上当你打过几手牌，你就已经意识到了——如果对方在翻前领打，在翻后继续c-bet，每一条街上都领打，那么不需要别人告诉你，你就知道，对面要么是真的有很强的牌，要么至少是在扮演很强的牌（bluff，诈唬）。你对这名玩家“要么牌力很强，要么正在尝试诈唬”的认知，就是我们所谓的“范围”。我们前面所说的，在更后面的位置行动，相比于前位行动的优势，就是在于对对手范围的掌握。在前位下注，你面对的…

Linux更新时区文件，解决夏令时和Africa/Casablanca问题
type: item
date: 2024-05-25T17:29:45+00:00
description: 这篇文章用来解决两个问题：如何更新操作系统的时区文件，让它得知目前最新的夏令时信息（这会依当局的行政命令而发生改变）如果遇到了时区信息错误，如何解决第一个需求最为常见，然而第二个问题对于 CentOS 用户来说同样有一定意义。下次有其他倒霉蛋遇到这个问题的时候，就能从本文确定问题和修复方式了。问题当你使用的应用不自己维护时区信息时，他们会从 Linux 系统的 tzdata，也就是系统的时区数据库读取这些信息。这一般依赖$TZDIR指示的路径（因此你可以通过这个环境变量修改它——如果你的应用程序足够聪明，知道先看看这个变量的话），如果没有指示，那么默认是/usr/share/zoneinfo目录。如果你的场景强依赖正确的夏令时数据，那么你必须经常更新这个数据库。因为很多主体的夏令时每年并不一致，而这依赖于当局的行政命令，因此你需要根据新的公告数据去更新，以保证你的软件和现实世界表现一致。如果你使用 CentOS，在某些涉及到时区的场景中，发现涉及 Africa/Casablanca（卡萨布兰卡）这个时区的数据总是不对，而其他绝大多数情况都是正常的，那么大概率是因为：这儿的 tzdata 本来就是坏的。如何解决你需要做的是更新系统的时区文件到一个正确/最新的版本（取决于你的需求）。接下来是过程：首先你需要下载 IANA 时区数据库，这里我们就用最新的，其他也很容易在 ICANN 找到：wget https://www.iana.org/time-zones/repository/tzdb-latest.tar.lz将其解压，然后完成 build 过程，这可能需要 C 编译环境：tar -xf tzdb-latest.tar.lz cd tzdb-2024a/ make TOPDIR="$HOME/tzdir" install # 决定编译到哪里 "$HOME/tzdir/usr/bin/zdump" -v America/Los_Angeles # 读一个文件验证一下然后你在TOPDIR给定的路径就得到了一份新的 tzdata 及相关文件。接下来需要用它们去覆盖本系统中的文件（如果需要，记得备份）：sudo cp -r $YOUR_PATH/usr/share/zoneinfo /usr/share这就足够更新时区数据了。当然如果你需要更全面的更新，包括一些时区相关工具，那么你可以把TOPDIR所有的文件拷贝并覆盖到/目录对应位置下。参考资料Africa/Casablanca timezone missing #10211Africa/Casablanca timezone issue #1442作者在 SelectDB 的内部文档

2023开发者生态，一些有趣的事实（偏C++向）
type: item
date: 2024-01-08T04:13:44+00:00
description: [scode type="blue" size=""]开拓视听，颇具参考意义。[/scode]语言——JS，yyds！（Rust长势喜人——越难找的员工，越值钱啊——Rust，唯一原神——这么多的人在用C++做游戏？这么少的人用C++写数据库吗。。职业生涯——学历？——内生的兴趣，是计算机行业的独到优势啊——好吧，这个比例远超我的想象AI——毕竟，这是一个需要知识和智力的行业——确实，谁又爱写测试呢数据库——interesting~ redis已经作为八股融入了cn后端人的血液了。另外，在这里好像大家不那么愿意接受新鲜事物（指pgsql——Navicat，CN特供！（破解版）开发者——嗯。。入职即高级！——码农是青春饭（——看起来很有信服力——呃。。。——青春饭和老龄化社会——实际润人的数量开发工作——woo，相当高的比例C++——比我们想象的要快，不是吗？——哈哈，大家都很想要modules，但是谁又能完整支持呢？——好吧，这么多人不写单测——我们需要（而且没有）一个合理的解决方式——GCC远胜于CLANGRust——新兴力量，不是吗？参考文献以上全部数据，来自JetBrains每年进行的开发者生态调查，原文刊于https://www.jetbrains.com/zh-cn/lp/devecosystem-2023/

新年贺词20年
type: item
date: 2024-01-01T15:13:00+00:00
description: [scode type="blue" size=""]以下内容均来自南方周末编辑部[/scode]20年变迁，不言自明。2024，新年贺词——守住不惑的底线，选择做最值得的自己2024年的第一束阳光正在深处积蓄，我们——生于1984年的南方周末也即将迎来创刊四十周年。按照最新一次人口普查的结果，中国人的平均年龄也大约四十岁。 “四十”而“不惑”，前者是这份新闻纸与吾国吾民的微妙同频，后者是我们念兹在兹的自我期许。 我们诞生于改革开放初启鹏程的春天里，现实百废初兴、百舸争流，未来充满未知、诸多困惑。也正因如此，穿越惑与不惑的边界，求索东方古国现代文明进步的答案，内蕴于我们的基因与使命。 四十年来，我们寻刻着一个民族走向复兴的经纬坐标，记录着无数个体命运的跌宕起伏；我们追求真、歌颂善、呈现美，我们抗拒假、斗争恶、暴露丑；我们持续见证、诉说并努力参与那场壮丽的春天的故事，又从那个故事中汲取无穷力量，让我们在面对未知与困惑时，绝不背离自我的基因与使命：在这里，读懂中国。 读懂永无止境，不惑则是一种状态。本应不惑的年岁，依然会强烈感受到各种困惑的推背感。 百年变局仍在高速演变，一个2023年就足以令人头晕目眩： 暴雨一路向北，杜苏芮狂卷乱奔，气象气候变幻莫测；支原体肺炎上热搜，流感成热词，南北儿科问者如云；网约车多地饱和，全职儿女等待“上岸”；谁记得住3000点保卫多少回？谁忘得了朱令与琴相伴又与病魔抗争的故事？摘下口罩的那一刻，你是否感到寒气？ 中美元首在大国关系的十字路口晤谈“正确相处之道”，俄乌冲突未了，巴以厮杀又起，中东局中局风云变幻，福岛核污水每一秒钟都在随洋流扩散，这个地球，能否和合与共？ ChatGPT取代的是马还是马车夫？算法会控制你还是解放你？人类的未来是不是马斯克的星辰大海？新技术带来的是元宇宙的美梦还是潘多拉的魔咒？ 惑与不惑永远在刷新边界，行动是穿越的唯一途径。越是目眩神迷，越要抱朴守一，在沧海桑田中守护方寸安宁殊为珍贵。 同在2023年，人们走向烧烤摊，走进演唱会，来一场“特种兵”式的旅行，在City Walk中与远方破镜重圆；与逝者道别，向生者微笑，为村超欢呼，为电竞喝彩，自己出演短剧的主角，让曾长出荒草的街道变成cos的魔法舞台。 这是变局中人的日常，毕竟唯一不变的只有变化本身。而在这其中，守住自己的内心，守住自己的生活，就是在守住不惑的底线，守护人生的大盘。 这也是似曾相识的过往。四十年来，我们看到无数这样的你，我们不停为你加油，因为我们就是你们的一部分。 我们感受着你的困惑，更感佩于你的行动。我们知道你生于平凡，也明白你心有不甘；我们看到你徘徊踯躅，也欣赏你迈开脚步；我们明白你犹豫不决，也理解你做出抉择；我们目睹你只有单枪匹马，也见证你汇成千军万马。 每一次何去何从的困惑，都可能通向一场毅然决然的醒悟。即使不知道答案，即使不清楚前路，仍可选择做最值得的自己：去思考、去行动，去迎接、去探索。无论游刃有余还是头破血流，每一次闪展腾挪都是一次饱含希望的奋起，仿佛每一团在冬夜升起的篝火，都在为迎接春日而燃烧。 不惑是有进无退的前行，是心怀牵挂的闯荡，是背着大山的跳舞。是上有老下有小肩上扛着整个世界却无可依靠的“必须坚强”。那就为自己点个赞吧，然后咬着牙继续走。 不惑是不确定中的结构涌现，是危机中萌发的生机。是在陷入迷茫时坚信自己的珍贵，在遭受厄运时坚持自己的志趣，经过光阴淘洗的种子，破土就是风华。 不惑是从心底生出光与热。即便历经创伤，也以明媚暖意，驱散焦灼与阴翳，随身携带一个宇宙，把爱的能量场安放在自我和他人之间。 不惑是既直击本质，又不失本真。是看清真相后的依然热爱，是洞穿世事后的心怀慈悲，是人生智慧的集中绽放，是“勿意、勿必、勿固、勿我”的笑对。 不惑是哪怕见惯山川湖海，仍能在意一粒沙、一滴水。是千疮百孔里依然藏着的倔强与柔软，是星夜兼程时对一轮朗月不知所起但一往情深的眷念。 不惑是时刻关照弱者的声音。我们每个人都可能是弱者，而社会的安全感与幸福感，也来源于让每个自强的人，能有示弱的机会。不惑的灵魂是夜空中最亮的星，既照亮自己的前路，也成为别人的明灯。 不惑是坚定那些被历史验证的共识。直面复杂与跌宕，始终执手相行，始终心怀敬畏，始终敢于创造。 不惑是坚守“正义、良知、爱心、理性”，是“让无力者有力，让悲观者前行”，是“没有一个冬天不可逾越”，是“总有一种力量让我们泪流满面”，是“每一个这样的你都是英雄”。 而不惑的本源，终将回到真实。每一道困惑的目光里，都闪烁着一个真实的梦想，那可能是大城市里的一张床，是地震后的一碗牛肉面，是父母的养老钱，是孩子的课间十分钟，是一则没有“仅限35岁以下”的招聘启事。每一次踏实的求解，都源自一个不被遮蔽的问题，敲开真相上的坚壳，方能触摸答案。 亲爱的读者，新年的第一束阳光正…