news 2026/10/18 4:58:48

Canal入门教程:基于MySQL Binlog的实时数据同步与缓存一致性实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Canal入门教程:基于MySQL Binlog的实时数据同步与缓存一致性实践

说实话,我第一次听说 Canal 这个名字的时候,第一反应是“这又是什么中间件?”。但真正花了一个下午把它跑通之后,才发现这玩意儿对于搞后端、搞数据同步的人来说,简直是解放生产力的利器。如果你正在做缓存和数据库的一致性同步、要把 MySQL 的数据实时同步到 Elasticsearch、或者想把数据库的每次增删改都推送给下游系统,那 Canal 基本是你绕不开的一个工具。这篇东西就是给完全没用过 Canal 的兄弟准备的,从零开始,把它是什么、怎么跑起来、会遇到什么坑,一次说清楚。

1. 别被名字吓到,Canal 到底是个啥

1.1 从“缓存和数据库不一致”这个经典问题说起

我先举个很常见的场景。你的业务里有张订单表,用户下单之后订单状态不断变化,从“待支付”到“已支付”再到“已发货”。前端为了查询快,会把订单信息丢一份到 Redis 缓存里。但问题来了:数据库的订单状态变了,缓存里的那份怎么同步?

第一种方案是业务代码里双写,先更新数据库,再删缓存或者更新缓存。这么做简单,但问题是代码侵入性太强。你每个写操作的地方都得写一套同步逻辑,一旦漏掉一个,缓存和数据库就永久不一致了。而且如果你们是微服务架构,订单服务和优惠券服务都要改订单状态,那双写逻辑散落在各个服务里,迟早维护到崩溃。

第二种方案是定时任务扫表,每隔几秒查一下有没有变化的记录。这个方案也有毛病,实时性差是一方面,更重要的是你得给表加一个 update_time 字段,靠它来捞增量。如果数据结构复杂一点,或者有分库分表,扫起来就很痛苦。

第三种方案,也是我目前见过最优雅的,就是通过 Canal 监听 MySQL 的 binlog,数据库里任何一行数据发生变化,Canal 都能第一时间感知到,然后推给你。你只需要关心“收到变更之后要干嘛”,根本不用管“怎么发现变更”。说白了,Canal 就是 MySQL 变更的“传声筒”和“广播站”。

1.2 Canal 在中间件家族里的角色定位

Canal 的全称很长,我也不背了,你只需要记住它诞生于阿里,最开始是为了解决异地机房数据同步的问题。它的核心能力就是:把自己伪装成一个 MySQL 的从库,向主库请求 binlog,拿到 binlog 之后解析成你能看懂的结构化数据,再通过各种方式吐给你。

注意它的位置——它不替代消息队列,不是数据库,也不是 ETL 工具。它是一个管道。它做的事情非常单一,就是“监听 + 解析 + 分发”。正因为专注,所以它在数据同步这条链路上非常可靠。生产环境里,有人拿它同步数据到 Elasticsearch,有人拿它同步到 Redis,有人拿它同步到 HBase,还有人拿它对接 Kafka 做数据总线。无论下游是谁,Canal 的那一段逻辑都是一样的,它只负责告诉你:“哪张表的哪一行,发生了什么操作,前后的值是什么。”

如果你现在看到这里还是有点抽象,那你就把 Canal 想象成一个坐在数据库旁边的“记事员”。数据库每做一次增删改,它就在小本本上记录下来,然后按照你的要求,把这条记录念给你听。你不用趴在数据库上一直盯着,只需要等它主动告诉你。

1.3 什么人需要认真学 Canal

我总结下来,下面这几类人是最该学 Canal 的:

  • 后端开发:尤其是做了几年业务、开始接触缓存、消息队列、搜索服务的人。
  • 数据研发和数据运维:需要从业务库同步数据到数据仓库或做实时计算的人。
  • 架构师:在设计数据一致性方案、异构数据同步方案时,Canal 几乎是必选项。
  • 刚入行的初中级程序员:如果你想在简历上写一个有含金量的亮点,把 Canal 的原理吃透、亲手搭一个 Demo,面试时够你和面试官聊很久了。

反过来说,如果你只是写简单的 CRUD,不涉及跨系统数据交互,那 Canal 对你暂时没什么用。但只要你负责的系统开始变复杂,数据开始需要被多个系统消费,你就绕不开它。

2. 快速上手前必须搞懂的原理:它一直在装“从库”

2.1 先花五分钟回忆一下 MySQL 主从同步

Canal 的原理要说透,绕不开 MySQL 的主从复制机制。主从复制很简单,主库把数据变更记录写到 binlog 日志文件里,从库用一个 I/O 线程把主库的 binlog 拉过来,写入自己的 relay log,再由 SQL 线程把 relay log 里的变更重放到自己身上。这样主库和从库的数据就保持一致了。

很多第一次接触 Canal 的人会问:“那我是不是直接搭一个从库然后读数据就行了?”理论上可以,但代价太大了。为了拿到变更数据,你得维护一整套从库环境,存储成本、运维成本都上去了。而且从库的数据是全量的,你其实只想知道“谁变了、变了什么”,并不需要存一份完整副本。

Canal 的精明之处就在这,它把自己伪装成一个从库,去主库注册,主库就真的把它当成自己的小弟,兢兢业业地给它推送 binlog。但 Canal 拿到 binlog 之后,并不去“重放”,而是直接解析,把里面的事件提取出来,格式化之后再交给下游。主库一根线程推送,Canal 解析完之后丢了就行,不需要落地存储,成本极低。

2.2 一个“假的从库”是怎么骗过主库的

你可能会问:“Canal 又不是真正的 MySQL 实例,它怎么跟主库通信?”

答案是:Canal 自己实现了一套 MySQL 主从交互协议。它跟主库建立 TCP 连接之后,发送一个类似“我要当你的从库”的握手请求,主库验证完账号权限,就会从这个连接上源源不断地把 binlog 推给 Canal。Canal 再把字节流按照 binlog 的格式去解析,还原出原始的表结构信息和数据变更内容。

这里有一个特别容易踩的坑:主库推送 binlog 的格式有三种,STATEMENT、ROW、MIXED。Canal 强制要求 ROW 模式。为什么?因为 STATEMENT 模式记录的是 SQL 语句本身,比如 “UPDATE user SET name='张三' WHERE id=1”,这种格式下 Canal 没法得知修改后的整行长什么样,只有 ROW 模式会记录“哪一行、哪些列、旧值是什么、新值是什么”。所以你在配置 MySQL 的时候,binlog_format 必须设成 ROW,最好连带 binlog_row_image 也设成 FULL,否则某些场景下拿不到完整的镜像数据。

2.3 Canal 的关键组件和部署形态

Canal 的部署形态有一个很清晰的层次。最底层是 server,它对应一个 Canal 进程,里面可以配置多个 instance。每个 instance 对应一个独立的“数据同步任务”,包含一个完整的数据管道。

你可以这样理解:server 是车间,instance 是车间里的一条条流水线。每条流水线都有自己独立的配置,监听不同的 MySQL 实例,互不干扰。生产环境里,常见做法是一台服务器上跑一个 Canal server,里面根据业务拆成多个 instance,每一个 instance 负责同步某几个库表。

另外,Canal 还分为单机模式和集群模式。单机模式就是一个 server 负责所有 instance,配置简单,适合学习和中小业务。集群模式引入了 ZooKeeper,由它来协调多个 Canal server,实现 instance 的 failover。这里我先不多展开,后面讲生产实践时再细说。新手入门阶段,你只需要先把单机模式跑通,原理理解了,往集群扩展就是水到渠成的事。

3. 手把手环境准备:从 MySQL 开启 binlog 到 Canal 跑起来

3.1 MySQL 侧配置:开启 binlog 并确认参数

在你动手下载 Canal 之前,先确认你的 MySQL 版本。Canal 对 MySQL 版本的兼容性整体不错,我实测过 5.7 和 8.0 都能正常跑。但 8.0 以上需要注意加密认证插件的问题,稍后我会在常见问题里专门说。

首先编辑 MySQL 的配置文件,在 [mysqld] 段下加入以下几行:

[mysqld] log-bin=mysql-bin binlog-format=ROW binlog-row-image=FULL server-id=100

这里说明一下每个参数的意义:

  • log-bin 开启 binlog 日志,文件名前缀随便起,我用的是 mysql-bin。
  • binlog-format 必须设置为 ROW,这个前面已经解释过了。
  • binlog-row-image 设置为 FULL,这样 binlog 里会记录所有列的旧值和新值,方便 Canal 解析。
  • server-id 绝对不能省略,主从复制协议里,每个参与复制的节点都需要一个唯一的 ID,Canal 伪装从库时也需要一个不同的 server-id。

改完配置之后重启 MySQL 让它生效。然后登录 MySQL,执行下面这条命令确认一下:

SHOW VARIABLES LIKE 'binlog_format';

看到结果是 ROW,就说明配置生效了。

3.2 创建专用账号并授权

这一步很多人会忽略,图省事直接用了 root 账号。我不建议这么干,因为 Canal 要的操作权限其实非常小,给它一个专用账号反而更容易排查问题。执行下面的 SQL:

CREATE USER 'canal'@'%' IDENTIFIED BY 'canal'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES;
  • SELECT 权限是让 Canal 在解析 binlog 时,如果需要获取表结构元数据,可以查表。
  • REPLICATION SLAVE 权限是让它能够伪装成从库,请求 binlog。
  • REPLICATION CLIENT 权限是让它能够执行 SHOW MASTER STATUS 之类的命令,获取当前 binlog 的位置。

如果你用的是 MySQL 8.0,创建用户之后大概率还要再授权一次,因为 8.0 的默认认证插件是 caching_sha2_password,Canal 对它的支持在低版本上不够友好。最稳妥的办法是在创建用户时指定认证插件:

CREATE USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';

如果你是在 MySQL 8.0 里已经创建好了用户,也可以修改:

ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';

这样能省掉一堆客户端的握手兼容问题。

3.3 下载 Canal 服务端部署包

环境准备完毕,接下来去下载 Canal 的部署包。你不需要自己编译源码,官方直接提供了编译好的 release 包。下载的时候注意版本选择,目前比较主流的是 1.1.x 系列,对新手来说,直接挑最新的稳定版本就行。

下载完解压到你喜欢的目录,我习惯放在 /opt/canal 下,目录结构大概是这样的:

bin/ # 启动/停止脚本 conf/ # 全局配置和实例配置 lib/ # Canal 依赖的 jar 包 logs/ # 日志输出目录

conf 目录下有一个 canal.properties,这是全局配置;还有一个 example 目录,这是一个叫 example 的实例模板,里面放着 instance.properties,这个文件决定了 Canal 到底监听哪个 MySQL。后面要新增同步任务,就是复制 example 目录改名字再改配置。

解压完成之后先别急着启动,还有两个配置文件需要根据你的环境修改。

3.4 修改 canal.properties 和 instance.properties

先打开 conf/canal.properties,里面有很多参数,新手阶段只要关注这几个关键项。

# 当前 Canal server 的 ID,多实例时不能重复 canal.id=1 # 是否开启内置的 admin 服务,新手可关掉 canal.admin.manager=false # 端口号,Canal server 暴露给客户端的 TCP 端口,默认 11111 canal.port=11111 # 注册到 ZooKeeper 的配置,单机模式不需要改 canal.zkServers=

然后是 conf/example/instance.properties 这个文件,它才是真正决定连哪个 MySQL 的关键。需要改的核心项如下:

# MySQL 主库地址和端口 canal.instance.master.address=127.0.0.1:3306 # 前面创建好的账号和密码 canal.instance.dbUsername=canal canal.instance.dbPassword=canal # 需要监听的库表,注意它只接受正则表达式 # 下面这行表示监听所有库的所有表 canal.instance.filter.regex=.*\\..*

这里有个细节,filter.regex 的正则分隔符是\\..,就是库名加一个点加表名。比如只想监听 test 库下的 user 表,就写成:

canal.instance.filter.regex=test\\.user.*

如果想监听 test 库下所有表,就写成:

canal.instance.filter.regex=test\\..*

这个正则语法有点绕,我在第一次配置时在这里卡了一段时间。后来索性先配了监听所有表,跑通了再去收紧范围。

3.5 首次启动和日志观察

配置完成后,就可以启动了。在 bin 目录下执行:

sh startup.sh

启动后第一件事不是去看日志,而是先确认进程起来了没有:

ps -ef | grep canal

然后去看 logs/canal.log 和 logs/example/example.log。新手第一次跑,大概率会在这两个日志里发现一些报错。常见的无非是账号密码不对、权限不足、连接超时这几类。

如果看到类似start successful的字样,就说明 Canal 已经成功连接上 MySQL,伪装成从库开始等待 binlog 推送了。

4. 一个 5 分钟跑通的最小 Demo:从 MySQL 到终端

4.1 目标:监听一张表的变更并在终端打印

环境跑通了,我们来做一次完整的端到端实验。实验目标很简单:对某张表执行 UPDATE 操作,在 Canal 收到变更后,用一段客户端程序把变更内容打印出来。

在 MySQL 里先建一张简单的表:

CREATE DATABASE IF NOT EXISTS canal_demo DEFAULT CHARSET utf8mb4; USE canal_demo; CREATE TABLE user_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64), age INT, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );

然后确认一下 instance.properties 里的过滤规则能匹配上这张表。按照前面的配置监听所有库,就不用额外改了。如果你改了过滤规则,记得重启 Canal。

4.2 客户端环境准备:Python 还是 Java 你选一个

Canal 官方提供了 Java 客户端依赖,这是最正统的方式。但很多非 Java 体系的人不熟悉 Maven 工程,这里我推荐用 Python 的 canal 客户端包,它足够轻量,适合快速验证。

pip install canal-python

这个包的使用方式非常直观,核心代码是:连接 Canal server 的地址和端口,订阅你想要的 instance,然后循环拉取消息,解析出每一条数据变更。

我们写一个最简单的脚本:

from canal.client import CanalClient from canal.protocol import EntryType def parse(entry): # 只处理行数据变更 if entry.entry_type != EntryType.ROWDATA: return store = entry.store_value for row_change in store.row_changes: row = row_change.row for column in row.after_columns: print(f"列名={column.name}, 新值={column.value}, 是否主键={column.is_key}") client = CanalClient(host='127.0.0.1', port=11111, username='', password='') client.subscribe(client_id='1001', destination='example', filter='.*\\..*') client.start() while True: message = client.get(1000) if message is None: continue entries = message.entries for entry in entries: parse(entry)

这段脚本的逻辑很直白。Canal 服务端把变更事件封装成 Entry 对象,我们取出来之后,遍历里面每一行变更,再把变更后的字段打印出来。

4.3 在 MySQL 里执行一次 UPDATE 并观察结果

脚本跑起来之后,回到 MySQL 执行:

INSERT INTO user_info (name, age) VALUES ('张三', 25); UPDATE user_info SET age = 26 WHERE name = '张三';

再去终端看,你应该能看到类似这样的输出:

列名=id, 新值=1, 是否主键=True 列名=name, 新值=张三, 是否主键=False 列名=age, 新值=26, 是否主键=False 列名=update_time, 新值=2025-01-01 12:00:00, 是否主键=False

到这里,你的 Canal 已经成功把 MySQL 的变更实时拿到手里了。整个链路是:MySQL 产生的 binlog 被 Canal 伪装从库接收,解析成结构化字段,再通过 TCP 推送到客户端程序。

4.4 数据变更的完整形态:before 和 after

这里我要强调一个知识点。上面打印的只是变更后的值,在真实的 Canal 数据里,每一条行变更都包含 before 和 after 两组字段。before 是这行数据变更前的样子,after 是变更后的样子。

如果你拿 Canal 做数据同步,这个 before 和 after 的对比非常关键。举个例子,如果你要把订单状态变更推送给短信服务,你需要先判断 after.state 是不是“已发货”,然后才能决定要不要发短信。如果你要把变更同步到 Elasticsearch,绝大多数情况下只需要 after 这一组数据,直接覆盖写文档即可。

看 canal-python 这个包的源码,你会发现它每个 row 下面有 before_columns 和 after_columns 两个列表。如果你只需要 after,就跟我上面写的那样遍历 after_columns 就对了。有些同事会疑惑:“我明明更新了一行,为什么收到的消息里没有旧值?”这就要回去检查 binlog_row_image 配置了,如果设置成了 MINIMAL,binlog 里只记录被修改的列和主键列,旧值是拿不到的。所以生产环境同步场景,强烈建议 FULL。

5. 我踩过的坑和排查思路,建议先收藏

5.1 binlog 格式没生效,数据一直收不到

这是新手最容易翻车的问题。MySQL 的配置文件改了,但你在配置文件的写法有误,或者改错了实例,导致实际运行的 MySQL 并没有加载到 ROW 格式。你可以在 MySQL 里执行:

SHOW VARIABLES LIKE 'binlog_format';

如果结果是 STATEMENT 而不是 ROW,别怀疑,就是配置没生效。检查一下配置文件路径,到底是 /etc/my.cnf、/etc/mysql/my.cnf 还是 /data/mysql/my.cnf,不同安装方式路径不一样。

5.2 收到消息一直报 Insert 而不是 Update

有位朋友跑通之后跑来问我:“我明明执行的是 UPDATE,为什么 Canal 一直给我 INSERT?”这个问题很有意思,排查到最后发现,他更新语句执行时,一张表被清空了又重新插入,在 binlog 里就呈现为 DELETE + INSERT。Canal 是按 binlog 事件来解析的,它不管你在 SQL 层面写了什么,只看 binlog 里实际记录了什么。所以如果你要做精确的 CUD 分类,不要看 SQL 语句,要以 binlog 事件为准。

5.3 客户端连接收不到数据,但 Canal 日志显示已经推送了

这个情况我也遇到过。Canal server 的日志显示有客户端连接成功,也能正常发送数据,但客户端脚本就是干等着不打印。

排查方法是先检查客户端的 filter 正则是否跟服务端的配置冲突。当时我是把服务端 filter 设置了只监听某个库,客户端却用.*\..*去订阅,结果根本匹配不上。这个 filter 的概念很像消息队列里的 topic 订阅关系,服务端和客户端两侧要一致才行。

5.4 消费端太慢导致 Canal 延迟增大

如果你接的下游处理比较慢,而 MySQL 的变更又很频繁,你可能会发现数据同步延迟越来越大。这里要明白 Canal 的机制:它从 binlog 增量拉取数据之后,会先放到一个内存缓冲区里,客户端消费走后才会从缓冲区删除。如果客户端消费跟不上,缓冲区满了,Canal 就会停止拉取新数据,MySQL 的 binlog 就会积压。

解决办法不是去调大缓冲区,而是优化消费逻辑。比如批量拉取,而不是一条条处理。canal-python 的get(num)方法,你可以一次性地把参数调大,比如get(2000),把拉取和处理改成批量模式,吞吐量会有明显提升。

5.5 MySQL 主从切换后 Canal 失联

一旦你的 MySQL 做了主从切换,Canal 在配置里写死的 master.address 就失效了。它会疯狂报连接失败的错。处理方案有两个:要么在 Canal 前面再挂一层域名或负载均衡,切换时只改域名指向;要么用 Canal 的集群模式配合 ZooKeeper,让 Canal 自己感知主从变化。新手单机实验阶段,遇到这个问题直接改配置重启就完事了。

6. 从 Demo 走向生产:Canal 能玩出的花活

6.1 把消息扔进 Kafka 或 RocketMQ,实现解耦

Demo 版本是把数据直接推给客户端程序,生产环境很少这么干。更合理的架构是:Canal 接入消息队列,业务系统从消息队列消费。

举个例子,你有一个搜索服务,它要的数据来源于订单库。传统做法是下单服务在写完数据库之后,主动调搜索服务的接口同步索引。这个方案耦合太重,订单服务一旦异常,搜索数据就断了。引入 Canal 之后,订单服务的代码不用任何改动,Canal 检测到订单表变化,把变更消息丢进 Kafka,搜索服务以消费者的身份读 Kafka,更新自己的索引。订单服务、Canal、搜索服务三者互相不知晓对方的存在,整个数据链路完全解耦。

配置 Kafka 客户端其实不难,Canal 的 protocol 模块提供了 CanalMQProducer,在 canal.properties 里指定 producer 类型为 kafka,配置好 Kafka 的地址,然后在 instance 里开启 MQ 模式,消息就会自动进入指定的 topic。

6.2 缓存一致性:用 binlog 驱动缓存刷新

前面说到的缓存一致性问题,Canal 提供了一个非常优雅的解法。你只需要在缓存更新的频道上做文章:Canal 监听电商系统的商品表变更,一旦商品价格发生变化,消息消费端收到之后把 Redis 里对应的商品缓存删掉。用户下一次访问时缓存未命中,再从数据库读一次并回填。

注意这里用的是“删缓存”而不是“更新缓存”。为什么?因为并发场景下,更新缓存容易出现旧值覆盖新值的竞态问题,而删缓存天然免疫这个问题:读线程发现缓存没有,就去数据库拿最新值然后回填,所有线程拿到的都是最新数据。Canal 加缓存删除这套组合,在业界已经是很成熟的做法了。

6.3 异构数据同步:MySQL 到 Elasticsearch 的实时管道

现在很多系统的搜索功能都依赖 Elasticsearch,但数据库是 MySQL。业务数据被写入 MySQL 之后,必须尽快同步到 ES 里才能被搜到。如果靠业务代码里双写 ES,等于给每个写接口都绑上一个重活,接口响应时间直接恶化。

用 Canal 之后,数据同步对业务代码完全透明。ES 的索引结构跟 MySQL 的表结构可以不一样,比如 MySQL 里用户表存的是 user_id、user_name、tag_ids,ES 里需要的是一个包含 tag_names 的扁平文档。那消息消费端就做一次数据转换,把 Canal 传来的行数据改造成 ES 需要的文档,再用 Bulk API 批量写入。这套方案我在模拟项目 X 里完整走过一遍,实测下来几十万行数据能在秒级完成同步。

6.4 进阶学习路径:从会用到底层原理

最后给想深入研究的人指个方向。第一步,把 Canal 自身带的可观测性指标用起来,比如通过 JMX 暴露的延迟指标、消费速率指标。第二步,去看 Canal 的源码,重点看 binlog 解析模块和分析模块,理解它如何把 MySQL 的二进制协议翻译成一系列事件对象。第三步,把 Canal 和 ZooKeeper 的集群模式搭一遍,了解实例在节点故障时如何自动迁移。走到这一步,你对分布式系统里“主从选举 + 故障转移”的套路也会理解得更深。

另外,如果你用的是阿里云等托管数据库,建议先用控制台确认是否已经开放了 binlog 相关参数。某些托管数据库默认是不给开 binlog 的,需要工单申请或者白名单配置。我最早差点在这上面栽了跟头,还好提前确认了一下。

写在最后的几句体会

从配置 MySQL 到跑通第一个 Demo,整个过程不会花你太多时间,但后面做排查和调优时,你一定会回来反复琢磨原理。我个人的感觉是,Canal 属于那种“配置看起来很简单,但真正理解它需要一点耐心”的工具。它不像 Web 框架那样写几行代码就能看到页面,它工作在数据链路的底层,一旦接通,你就能真切感受到数据像水一样在整个系统里流动起来。

还有一个小建议:第一次实验时,别一上来就监听所有库所有表。找个小库、建两张表、跑一个最小同步链路,先让整条路走通,再慢慢加规则。数据同步这行,断断续续最坑人,缓步推进才是最快的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 4:57:27

RWKV-7 “Goose“ with Expressive Dynamic State Evolution

RWKV-7 “Goose” 论文总结与翻译 主要内容与创新点 核心架构 动态状态演化:通过推广增量规则(delta rule),引入向量值门控和上下文学习率,实现了更灵活的状态更新机制。 常数复杂度:保持线性时间和常数内存复杂度,适用于长序列任务。 并行训练:支持高效的并行化训练…

作者头像 李华
网站建设 2026/10/18 4:56:36

artcraft 类创意项目从零搭建:渲染选型、状态管理与插件机制实战

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个词,我脑子里蹦出来的不是某个具体工具,而是一种很朴素的组合感——art(艺术、审美、表达)加上 craft(手艺、工艺、打磨&…

作者头像 李华
网站建设 2026/10/18 4:53:51

高并发场景下订单与库存的最终一致性方案:Redis预扣与异步下单实践

只要做过电商后端的,基本都绕不开“订单和库存”这一对冤家。平时流量低的时候,方案怎么简单怎么来,查库存、扣库存、下单三步走,基本不出问题。可一旦碰上秒杀、大促这类高并发场景,流量翻几十倍,同样的代…

作者头像 李华
网站建设 2026/10/18 4:53:47

Executors 线程池无界队列隐患审计,附 ThreadPoolExecutor 正确配置方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/18 4:52:57

Claude Code入门指南:终端AI编程助手从安装到实战

最近总有朋友问我,Claude Code到底是个什么东西,怎么装、怎么用、值不值得折腾。我研究了一下,发现抛开那些高大上的宣传,它本质就是一个跑在终端里的AI编程助手,通过命令行对话的方式帮你读写代码、执行命令、排查问题…

作者头像 李华