Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >实现悲观协议,除了锁还能咋办?

实现悲观协议,除了锁还能咋办?

作者头像
JavaEdge
发布于 2024-05-26 06:09:21
发布于 2024-05-26 06:09:21
1730
举报
文章被收录于专栏:JavaEdgeJavaEdge

相对乐观和局部悲观是一体两面的关系,识别它的要点就在于是否有全局有效性验证,这也和分布式数据库的架构特点息息相关。但是关于悲观协议,还有很多内容没有提及,下面我们就来填补这一大块空白。

悲观协议的分类

要先跳出来,从并发控制技术整体的分类体系看。

并发控制的分类体系,学术界标准也不一:

狭义乐观协议和其他悲观协议这种分类方式更清晰些,所以就选择“ Transactional Information Systems : Theory, Algorithms, and the Practice of Concurrency Control and Recovery”中的划分体系。书中一幅图,梳理不同的并发控制协议。

先分为悲观、乐观。因为这里的乐观协议是指狭义乐观并发控制,所以包含内容较少,只有前向乐观并发控制和后向乐观并发控制;而悲观协议又分为基于锁和非锁两大类,其中基于锁的协议是数量最多的。

两阶段封锁(Two-Phase Locking,2PL)

基于锁的协议显然不只是2PL,还包括有序共享(Ordered Sharing 2PL, O2PL)、利他锁(Altruistic Locking, AL)、只写封锁树(Write-only Tree Locking, WTL)和读写封锁树(Read/Write Tree Locking, RWTL)。但这几种协议在真正的数据库系统中很少使用,所以就不过多介绍了,我们还是把重点放在数据库系统主要使用的2PL上。

2PL就是事务具备两阶段特点的并发控制协议,两阶段指:

  • 加锁阶段
  • 释放锁阶段

加锁阶段严格区别于紧接着的释放锁阶段。

在t1时刻之前是加锁阶段,在t1之后则是释放锁阶段,我们可以从时间上明确地把事务执行过程划分为两个阶段。2PL的关键点就是释放锁之后不能再加锁。而根据加锁和释放锁时机的不同,2PL又有一些变体。

保守两阶段封锁协议(Conservative 2PL,C2PL),事务在开始时设置它需要的所有锁。

img
img

严格两阶段封锁协议(Strict 2PL,S2PL),事务一直持有已经获得的所有写锁,直到事务终止。

强两阶段封锁协议(Strong Strict 2PL,SS2PL),事务一直持有已经获得的所有锁,包括写锁和读锁,直到事务终止。SS2PL与S2PL差别只在于一直持有的锁的类型,所以它们的图形是相同的。

理解这几种2PL变体,回想13的Percolator模型。当主锁(Primary Lock)没有释放前,所有的记录上的从锁(Secondary Lock)实质上都没有释放,在主锁释放后,所有从锁自然释放。所以,Percolator也属于S2PL。TiDB的乐观锁机制是基于Percolator的,那么TiDB就也是S2PL。

事实上,S2PL可能是使用最广泛的悲观协议,几乎所有单体数据都依赖S2PL实现可串行化。而在分布式数据库中,甚至需要使用SS2PL来保证可串行化执行,典型的例子是TDSQL。但S2PL模式下,事务持有锁的时间过长,导致系统并发性能较差,所以实际使用中往往不会配置到可串行化级别。这就意味着我们还是没有生产级技术方案,只能期望出现新的方式,既达到可串行化隔离级别,又能有更好的性能。最终,我们等到了一种可能是性能更优的工程化实现,这就是CockroachDB的串行化快照隔离(SSI)。而SSI的核心,就是串行化图检测(SGT)。

串行化图检测(SGT)

SSI是一种隔离级别的命名,最早来自PostgreSQL,CockroachDB沿用了这个名称。它是在SI基础上实现的可串行化隔离。同样,作为SSI核心的SGT也不是CockroachDB首创,学术界早就提出了这个理论,但真正的工程化实现要晚得多。

理论来源:PostgreSQL

PostgreSQL在论文“Serializable Snapshot Isolation in PostgreSQL”中最早提出了SSI的工程实现方案,这篇论文也被VLDB2012收录。

串行化理论的核心是串行化图(Serializable Graph,SG)。这图用来分析数据库事务操作的冲突情况。每个事务是一个节点,事务之间的关系则表示为一条有向边。啥关系可表示为边呢?

串行化图的构建规则是这样的,事务作为节点,当一个操作与另一个操作冲突时,在两个事务节点之间就可以画上一条有向边。事务之间的边的分类:

  • 写读依赖(WR-Dependencies),第二个操作读取了第一个操作写入的值
  • 写写依赖(WW-Dependencies),第二个操作覆盖了第一个操作写入的值
  • 读写反依赖(RW-Antidependencies),第二个操作覆盖了第一个操作读取的值,可能导致读取值过期

案例看咋用这几条规则构建一个简单的串行化图:

图中共三个事务先后执行:

  • 事务T1先执行W(A),T2再执行R(A),所以T1、T2之间存在WR依赖,形成一条T1指向T2的边
  • T2的W(B)与T3的R(B)也是WR依赖
  • T1的W(A)与T3的R(A)之间也是WR依赖

最终产生一个DAG,能构建出DAG,说明相关事务可串行化执行,无需中断任何事务。

可用SGT验证典型死锁情况。事务T1、T2分别以不同顺序写两个数据项,就会形成死锁:

串行化图体现,显然构成环:

RW反向依赖

SGT中,WR依赖和WW依赖都与直觉相符,RW反向依赖较难理解。PostgreSQL论文专门描述了一个RW反向依赖场景。

该场景需维护两张表:

  • 收入表(reciepts),记入当日的收入情况,每行都会记录一个批次号
  • 独立的控制表(current_batch),只有一条记录即当前的批次号。也可把这里的批次号理解为一个工作日

同时,还有三个事务T1、T2、T3:

  • T2记录新的收入(NEW-RECEIPT),从控制表中读取当前的批次号,然后在收入表中插入一条新记录
  • T3负责关闭当前批次(CLOSE-BATCH),将控制表中的批次号递增,意味着后续再发生的收入会划归下一批次
  • T1是报告(REPORT),读取当前控制表的批次号,处理逻辑是用当前已+1的批次号再-1。T1用这个批次号作为条件,读取收据表中的所有记录。查询到这批次,即这一日所有交易

例子很像银行存款系统的日终翻牌。因为T1要报告当天收入,所以要在T3后执行。事务T2记录当天每笔入账,须在T3前执行,这样才能出现在当天报表。三者顺序执行可正常工作,否则异常,如下:

T2先拿到一个批次号x,随后T3执行,批次号关闭后,x这个批次号其实已经过期,但是T2还继续使用x,记录当前的这笔收入。T1正常在T3后执行,此时T2尚未提交,所以T1的报告中漏掉了T2的那笔收入。因为T2使用时过期的批次号x,第二天的报告中也不会统计到这笔收入,最终这笔收入就神奇地消失了。

在理解了这个例子的异常现象后,我们用串行化图方法来验证一下。我们是把事务中的SQL抽象为对数据项的操作,可以得到下面这张图。

img
img

图中batch是指批次号,reps是指收入情况。

接下来,我们按照先后顺序提取有向边,先由T2.R(batch) -> T3.W(batch),得到T2到T3的RW依赖;再由T3.W(batch)->T1.R(batch),得到 T3到T1的WR依赖;最后由T1.R(reps)->T2.W(reps),得到T1到T2的RW依赖。这样就构成了下面的串行化图。

显然这三个事务之间是存在环的,那么这三个事务就是不能串行化的。

这个异常现象中很有意思的一点是,虽然T1是一个只读事务,但如果没有T1的话,T2与T3不会形成环,依然是可串行化执行的。这里就为我们澄清了一点:我们直觉上认为的只读事务不会影响事务并发机制,其实是不对的。

工程实现:CockroachDB

RW反向依赖是特别存在,在于传统的锁机制无法记录这种情况。因此论文“Serializable Snapshot Isolation in PostgreSQL”提出,增加一种锁SIREAD,记录快照隔离(SI)上所有执行过的读操作(Read),从而识别RW反向依赖。

SIREAD并不是锁,只是一种标识。但这方案面临困境:读操作涉及数据范围太大,跟踪标识带来的成本可能比S2PL还高,也就无法达到最初目标。

CockroachDB做了关键设计,读时间戳缓存(Read Timestamp Cache,RTC)。

RTC

执行任何读取操作时,操作的时间戳都会被记录在所访问节点的本地RTC。当任何写操作访问这节点时,都以将要访问的Key为输入,向RTC查询最大的读时间戳(MRT),如MRT>这写入操作的时间戳,继续写入就会形成RW依赖。这时须终止并重启写入事务,让写入事务拿到一个更大的时间戳重新尝试。

RTC是以Key范围组织读时间戳。这样,当读取操作携带了谓词条件如where子句,对应操作就是个范围读取,会覆盖若干Key,那整个Key的范围也可被记录在RTC。这样处理好处是,可兼容一种特殊情况。

如事务T1第一次范围读取(Range Scan)数据表,where“>=1 and <=5”,读取到1、2、5,T1完成后,事务T2在该表插入4,因为RTC记录范围区间[1,5],所以4也可被检测出存在RW依赖。这个地方,有点像MySQL间隙锁的原理。

RTC是个大小有限,采用LRU淘汰算法的缓存。达存储上限时,最老的时间戳被抛弃。为应对缓存超限,会将RTC中出现过的所有Key上最早的那个读时间戳记录,作为低水位线(Low Water Mark)。如一个写操作将要写的Key不在RTC中,则会返回该低水位线。

相对乐观

SGT的运行机制和传统的S2PL一样属于悲观协议。但SGT没有锁的管理成本,所以性能比S2PL更好。

CockroachDB基于SGT理论进行工程化,使可串行化真正成为生产级可用的隔离级别。从整体并发控制机制看,CockroachDB和上一讲的TiDB一样,虽然在局部看是悲观协议,但因为不符合严格的VRW顺序,所以在全局来看仍是一个相对乐观的协议。

这种乐观协议同样存在[第13讲]提到问题,所以CockroachDB也在原有基础上进行了改良,通过增加全局的锁表(Lock Table),使用加锁的方式,先进行一轮全局有效性验证,确定无冲突的情况下,再使用单个节点的SGT。

总结

  1. 并发控制机制的划分方法很多,没有统一标准,使用Transactional Information Systems : Theory, Algorithms, and the Practice of Concurrency Control and Recovery提出的划分标准,分为悲观协议与乐观协议两种。这里的乐观协议是上一讲提到的狭义乐观协议,悲观协议又分为锁和非锁两大类,我们简单介绍了2PL这一个分支。
  2. 我们回顾了Percolator模型,按照S2PL的定义,Percoloatro本质就是S2PL,因此TiDB的乐观锁也属于S2PL。
  3. S2PL是数据库并发控制的主流技术,但是锁管理复杂,在实现串行化隔离级别时开销太大。而后,我们讨论了非锁协议中的串行化图检测(SGT)。PostgreSQL最早提出了SGT的工程实现方式SSI。CockroachDB在此基础上又进行了优化,降低了SIREAD的开销,是生产级的可串行化隔离。
  4. CockroachDB最初和TiDB一样都是局部采用悲观协议,而不做全局有效性验证,是广义的乐观协议。后来,CockroachDB同样也将乐观协议改为悲观协议,采用的方式是增加全局的锁表,进行全局有效性验证,而后再转入单个的SGT处理。

串行化理论,只有当相关事务形成DAG图时,这些事务才是可串行化的。这个理论不仅适用于SGT,2PL的最终调度结果也同样是DAG图。在更大范围内,批量任务调度时DAG也同样被作为衡量标准,如Spark

参考

FAQ

看到SGT使用了DAG检测时,就想到Spark和“环路检测”。所以,知识学到最后,还是需要从底层和基础去探寻答案。

Q:读时间戳缓存RTC,是为防止RW反依赖,这里读时间戳比写时间戳大的判定,是否和分布式数据库的时钟机制有关,如果授时不存在误差,是否就无需RTC设计?

A:RTC设计是为简化SIREAD,不是因为时间误差,就算用TSO没有时间误差,也需要RTC。

Q:有数据库教材将MVCC作为一种重要的并发控制技术,与乐观协议、悲观协议并列。如何理解MVCC与乐观协议、悲观协议的关系呢?

MVCC与乐观、悲观协议无直接关系,因为乐观悲观本质区别在“何时校验冲突”,而 MVCC 是另一层次技术,对冲突检验的时间点没任何影响,不论乐观悲观协议,都可以有 MVCC。

Q:MVCC可看作单个数据的无锁结构吗?乐观锁和悲观锁是全局事务级别的并发控制。

A:MVCC是一种数据库并发控制策略,为每个数据行维护多版本来实现高并发。每个版本都有一个时间戳,因此不同事务可同时访问同一行数据的不同版本,避免了锁竞争和阻塞。

而乐观锁/悲观锁是在事务级别实现并发控制的策略:

  • 乐观锁假设在事务提交时不会发生冲突,因此不会阻塞其他事务的执行
  • 悲观锁则假设在事务执行期间会发生冲突,并在事务执行期间持有锁以防止其他事务对数据进行修改

因此,MVCC和乐观锁、悲观锁都是用于实现并发控制的策略,但MVCC更适于单个数据的无锁结构,且可提供更好并发性能。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
PostgreSQL数据库的SSI实现
👆点击“博文视点Broadview”,获取更多书讯 PostgreSQL数据库默认的隔离级别是 Read Committed,它同时支持Repeatable Read和Serializable。在9.1(不含)之前的版本中,PostgreSQL的Serializable级别等价于Snapshot Isolation,而非真正的Serializable。 Snapshot Isolation主要存在写偏序(Write Skew)问题,这个问题在PostgreSQL 9.1中已通过Serializable S
博文视点Broadview
2023/05/06
1.1K0
PostgreSQL数据库的SSI实现
数据库并发控制理论
并发控制技术,是数据库事务实现的基石,在确保事务隔离性正确的前提下,尽可能提高事务的并发度。
用户6879030
2024/06/05
2910
数据库并发控制理论
腾讯HTAP数据库TBase分布式事务揭秘
    TBase是一款腾讯自研高性能HTAP数据库,提供高性能的OLTP和OLAP能力,同时保证可扩展全局一致性分布式事务(ACID)。在OLTP方面,TBase采用MVCC+全局时钟+2PC+SSI的方式来实现全局一致性分布式事务,同时引入大量性能优化的设计来减少全局事务带来的开销。在小规模集群上,TBase能够提供超过300万TPMTotal的事务处理吞吐量(工业界标准TPCC测试集)。 本篇文章先介绍TBase的架构体系和数据库事务的基本原理,然后介绍学术界最先进的分布式事务设计方案,最后阐述我们
腾讯大数据
2019/08/07
3.2K0
腾讯HTAP数据库TBase分布式事务揭秘
腾讯与中国人民大学开源最新研究成果:3TS腾讯事务处理技术验证系统
作者:李海翔,腾讯TEG数据库技术专家 一个是全球领先的科技公司,一个是中国数据库基础学术研究的摇篮,近日,中国人民大学-腾讯协同创新实验室正式举行揭牌仪式。据了解,双方已聚焦在数据库基础研究领域进行了多年的前沿产学研合作,以及数据库人才合作培养计划,在推进数据库安全可控的同时面向未来大规模多场景数字化时代进行前沿创新研究储备,其中实验室输出的包括“全时态数据库系统”等多项成果相继被VLDB等国际顶会收录,同时申请获得了多项国家技术专利。 在本次实验室揭牌亮相的同时,腾讯与中国人民大学研究团队还
腾讯技术工程官方号
2020/11/30
8320
精通Java事务编程(8)-可串行化隔离级别之可串行化的快照隔离
串行化的隔离级别和高性能就是相互矛盾的吗?也许不是,一个称为可串行化快照隔离(SSI, serializable snapshot isolation)算法很有前途。提供完整的可串行化保证,而性能与快照隔离相比只有很小性能损失。 SSI在 2008 年首次被提出,如今既用于单节点DB(PostgreSQL9.1后的可串行化)和分布式DB(FoundationDB)。由于 SSI 与其他并发控制机制相比还很年轻,还在实践中证明自己。
JavaEdge
2022/07/25
9990
精通Java事务编程(8)-可串行化隔离级别之可串行化的快照隔离
支撑微信支付的数据库如何提供超300万TPCC事务处理能力?
腾讯TBase是一款腾讯自研高性能HTAP数据库,提供高性能的OLTP和OLAP能力,同时保证可扩展全局一致性分布式事务(ACID),为用户提供高一致性的分布式数据库服务和高性能的数据仓库服务。一方面解决了传统数据库扩展不足、数据sharding之后数据库事务的严格一致性难题、数据安全、跨地域容灾等问题,同时具备了高性能事务处理、数据治理、混合负载支持等能力。
腾讯云数据库 TencentDB
2019/08/09
9880
支撑微信支付的数据库如何提供超300万TPCC事务处理能力?
腾讯云李海翔:数据库的并发控制技术深度探索
腾讯云开发者社区
2017/05/17
2.9K0
腾讯云李海翔:数据库的并发控制技术深度探索
解读年度数据库PostgreSQL:如何处理并发控制(一)
原文:http://www.enmotech.com/web/detail/1/748/1.html
数据和云01
2019/07/08
1K0
重新学习MySQL数据库6:浅谈MySQL的中事务与锁
本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看
Java技术江湖
2019/11/29
6000
PostgreSQL技术大讲堂 - 第20讲:事务概述与隔离级别
PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUG PG技术大讲堂。
用户5892232
2023/06/29
3770
PostgreSQL技术大讲堂 - 第20讲:事务概述与隔离级别
读懂数据库中的乐观锁和悲观锁和MVCC
在数据库的实际使用过程中,我们常常会遇到不希望数据被同时写或者读的情景,例如秒杀场景下,两个请求同时读到系统还有库存1个,然后又先后把库存更新为0,这时候就会出现超卖的情况,这时候货物的实际库存和我们的记录就会对应不上了。
BUG弄潮儿
2020/07/30
8820
CMU 15-445 -- Timestamp Ordering Concurrency Control - 15
本系列为 CMU 15-445 Fall 2022 Database Systems 数据库系统 [卡内基梅隆] 课程重点知识点摘录,附加个人拙见,同样借助CMU 15-445课程内容来完成MIT 6.830 lab内容。
大忽悠爱学习
2023/10/11
3060
CMU 15-445 -- Timestamp Ordering Concurrency Control - 15
一文详解开放数据湖的并发控制
并发控制在数据库管理系统中至关重要,以确保多个用户对共享数据的一致且安全的访问。关系数据库(RDBMS),例如MySQL(InnoDB)[1]和分析数据库(例如数据仓库),已经提供了强大的并发控制机制来有效地处理此机制。随着数据的规模和复杂性的增长,管理并发访问变得更具挑战性,尤其是在大型分布式系统(如数据湖)[2]中,预计将处理分析领域中的不同类型的工作负载。尽管由于缺乏存储引擎[3]和ACID保证,数据湖传统上一直在并发操作中挣扎,但Lakehouse架构具有带有Apache Hudi,Apache Iceberg和Delta Lake等开放式格式的体系结构,从某些广泛使用的并发控制方法中汲取灵感高并发工作负载。
ApacheHudi
2025/02/18
2060
一文详解开放数据湖的并发控制
OceanBase 高并发场景技术解读
数据库系统是属于既要又要的系统,既要保证数据库的正确性,又要高并发。在高并发的场景下保证数据库的正确性,关键在于保证事务的 ACID。以 ACID 的 I(Isolation) 为例,I 表示的是在并发事务的场景下,事务并发执行的效果与事务串行执行的效果完全相同,这种隔离级别就是所说的可串行化隔离级别,但是可串行化隔离级别的代价比较大,往往伴随着大量的冲突等待或者冲突失败。
公众号:码到三十五
2024/05/24
3130
OceanBase 高并发场景技术解读
解读年度数据库PostgreSQL:如何处理并发控制(一)
墨墨导读:最近电子工业出版社博文视点出版了《PostgreSQL指南:内幕探索》,日前「数据和云」公众号推荐了这本书并赠送了五本,百多位用户参与,几十条留言未能放出,为了让大家更好地学习开源数据PostgreSQL,经出版社官方授权,刊载本书部分章节内容以飨读者,本文节选了第五章《并发控制》5.1 -5.2。
数据和云
2019/07/09
8930
解读年度数据库PostgreSQL:如何处理并发控制(一)
最新开源:3TS腾讯事务处理技术验证系统(下)
作者:李海翔,腾讯TEG数据库技术专家 近日,中国人民大学-腾讯协同创新实验室正式举行揭牌仪式。据了解,双方已聚焦在数据库基础研究领域进行了多年的前沿产学研合作,以及数据库人才合作培养计划,在推进数据库安全可控的同时面向未来大规模多场景数字化时代进行前沿创新研究储备,其中实验室输出的包括“全时态数据库系统”等多项成果相继被VLDB等国际顶会收录,同时申请获得了多项国家技术专利。 在本次实验室揭牌亮相的同时,腾讯与中国人民大学研究团队还开源公布了一项最新合作研究成果——3TS腾讯事务处理技术验证系统。 T
腾讯技术工程官方号
2020/12/03
9310
干货分享 | Spanner事务处理技术详解
点击上方蓝字每天学习数据库 作者简介:李海翔,网名“那海蓝蓝”,腾讯金融云数据库技术专家。中国人民大学信息学院工程硕士企业导师。著有《数据库事务处理的艺术:事务管理和并发访问控制》、《数据库查询优化器的艺术:原理解析与SQL性能优化》、《大数据管理》,广受好评。 ---- Spanner支持事务的四个特性ACID,2012年的《Spanner: Google’s Globally-Distributed Database》论文,并没有明确描述ACID分别是怎么实现的,只是描述了C特性实现的一些内容,而D
腾讯云数据库 TencentDB
2019/06/06
15.8K0
干货分享 | Spanner事务处理技术详解
数据库事务一致性实现上的各种细节,你注意到了吗? | DB·洞见
数据库的事务包含原子性、一致性、隔离性、持久性四个特性。隔离性与一致性紧密相连,它们也容易让人迷惑。SQL标准定义了4个隔离级别,但由于定义使用的是自然语言,而非形式化语言,导致人们对隔离级别的理解有所差异,各个数据库系统的实现方式也有所不同。然而在分布式的场景下,又面临新的问题。 探索前沿研究,聚焦技术创新。本期由腾讯云数据库高级工程师孟庆钟为大家介绍数据库事务一致性的实现,内容包括事务的基本概念以及特性、主要的隔离级别及实现、TDSQL事务一致性的实现。 事务的基本概念及特性 1.1 事务的基本
腾讯云数据库 TencentDB
2022/12/22
1.8K0
数据库事务一致性实现上的各种细节,你注意到了吗? | DB·洞见
SQL事务隔离实用指南
您可能已经在数据库的文档中看到了隔离级别,感到有些手足无措。很少有日常使用事务的例子真正提到了隔离。大多数使用数据库的默认隔离级别,并希望获得最好的隔离级别。这是一个需要理解的基本话题,如果你花点时间来研究这个指南,你会对SQL事务隔离有深入的认识。 基本的定义 为了正确地理解SQL隔离级别,我们首先应该考虑事务本身。交易的概念来自合同法:法律交易必须是原子的(要么所有的条款都适用,要么没有),一致的(遵守法律协议),并且是持久的(在承诺之后,各方不能收回他们的承诺)。这些属性是数据库管理系统中流行的“AC
CSDN技术头条
2018/02/08
1.3K0
SQL事务隔离实用指南
『数据库』数据库系统效率Max--数据库并发控制
数据库管理系统必须提供并发控制机制 并发控制机制是衡量一个数据库管理系统性能的重要标志之一
风骨散人Chiam
2020/10/28
8300
推荐阅读
相关推荐
PostgreSQL数据库的SSI实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档