Skip to content

Benchmarking

github-actions[bot] edited this page Oct 3, 2026 · 19 revisions

基准与压力测试

简体中文 | English

本页说明基准测试的方法、已有结果和后续计划。每项结果都会标明测试环境;尚未测量的项目会注明仍在计划中。

服务端每次更新称为一个 tick;通常每秒更新 20 次,每次的目标耗时不超过 50 毫秒。

当前服务端基准通过 skript-reflect 调用 System.nanoTime():操作返回后立即记录总耗时(wallNs),校验查询和结果计数不计入其中。每 tick 运行的观察器另行记录相邻两次执行的最大间隔(gapNs)。观察窗口从操作开始持续到返回后再等待一个 tick,因此与总耗时的结束时刻不同;这个间隔也不是直接从服务端内部测得的单个 tick 耗时。

本页和自动生成的报告以六位小数显示毫秒,完整保留纳秒计时值;JSON 报告和 CI 历史保存原始的整数纳秒值。纳秒只是计时单位,不代表单次结果具有纳秒级准确度;调度、JVM 垃圾回收(GC)、其他插件、数据库执行和脚本恢复运行的时机都会影响读数。

这些数字对服务端意味着什么

超过 5000 行的读取会失败,不会保存部分结果。写入时,一条语句绑定的值超过 30000 个,插件会将其拆成多条语句。这些限制可以避免请求无限增大,但允许执行的大批量写入仍可能拉长 tick。

从列表变量执行 insert many 时,插件跨 tick 分段读取源变量。每段最多处理 4096 个步骤,每 64 步检查一次时间,运行约 2 毫秒后暂停。单个值的转换仍可能超过 2 毫秒,因此这只是控制主线程耗时的目标,并非硬上限。整次写入可能需要更多 tick 才完成;写入语句返回前,脚本应保持源变量不变。

测试分别观察了三个方面:

  • JVM 基准中的插入耗时。 一次 5000 行的 insert many,在 Intel Xeon Platinum 8573C 上用时 8.022 毫秒,在 AMD EPYC 7763 上用时 8.241 毫秒。对应的 CI 记录分别位于 dev/bench/intel-xeon-platinum-8573c/data.js 和 dev/bench/amd-epyc-7763-64-core-processor/data.js。
  • 当前服务端读写。 使用 System.nanoTime() 分别记录五种数据库的操作总耗时,以及 tick 观察器的最大执行间隔。当前实测结果见下表。
  • 旧版数据库驱动的行为。 另一项测试连续写入二十批、每批 5000 行,MySQL 用时 17.04 秒,PostgreSQL 和 MariaDB 各用时 1.99 秒。对其中一批,MySQL 服务端统计到 5038 条语句,其中 5000 条是插入;MariaDB 统计到 2 条,其中 1 条是多行插入。这些数据来自旧版 MySQL 插入路径,只对应当时的驱动设置。

当前服务端基准在 SQLite、MySQL、MariaDB 和 PostgreSQL 上对比 5000 行的 insert many 与手写 SQL。MySQL 默认使用参数化多行 INSERT,每条语句最多绑定 30000 个值;其他 JDBC 路径使用预处理批次。手写 SQL 则是一条直接写入值的多行 INSERT,其字符串构造不计入耗时。两者的耗时差异还包含插件读取源变量、参数绑定和写入形式的成本,不能全部归因于 ORM 映射。MongoDB 只执行插件读写用例,并明确跳过 SQL 对照。

手写 Skript 循环如果只写入普通变量,测到的是内存操作,不能用来比较数据库写入性能。

tick 超出量是观察到的最大 tick 间隔减去 50 毫秒,负数按零计算。这个指标能显示 tick 延迟,却不能直接给出写入在主线程上用了多久;在一个正常 tick 内完成的工作也不会显示为超出。表中的“热读”和“热写”只是指在其他用例之后再执行一次 5000 行读取或写入,不表示缓存一定已预热。

当前 CI 覆盖范围

tick 作业分别对下表的五种数据库实现(后端)运行 100、500、1000、2500、5000 和 10000 行的插件写入与读取,并在最后再次执行 5000 行写入和读取。支持 SQL 的数据库另有 5000 行的插件与原始 SQL 对照。输入行只填充 id 列,这样比较集中在行的传输和存储,而不是某种六列数据组合。

测试范围与环境

先确认每种数据库跑了哪些用例,以及结果来自什么环境。这张表用于检查测试覆盖范围,不比较耗时。

  • 后端:被测数据库;SQLite 使用插件的 "JDBC" 连接类型。
  • 插件多行数与重复测试:是否运行前述六种行数,以及最后重复执行的 5000 行读写。
  • 原始 SQL 对照:是否用 execute query 和 execute update 执行手写 SQL,与插件语句对照。MongoDB 不支持 SQL,因此跳过这组对照;它的插件读写仍会测试。
  • 新版 CI 结果:本次工作流链接、运行主机的 CPU 型号,以及数据库容器镜像或数据库名称。
后端 插件多行数与重复测试 原始 SQL 对照 新版 CI 结果
SQLite 已完成 已完成 Run #4,AMD EPYC 7763,SQLite
MySQL 已完成 已完成 Run #4,Intel Xeon 6973P-C,mysql:8.4
MariaDB 已完成 已完成 Run #4,AMD EPYC 7763,mariadb:11.4
PostgreSQL 已完成 已完成 Run #4,AMD EPYC 7763,postgres:17
MongoDB 已完成 跳过:不支持 SQL Run #4,Intel Xeon 6973P-C,mongo:8

SQL 对照分别记录 pluginwrite、pluginread、rawwrite 和 rawread 日志;MongoDB 则在插件读写后明确记录 SKRIPTORM_BENCH=SKIP。

5000 行读写:不同数据库的表现

Run #4 于 2026-10-03 在提交 6476c9b 上完整通过,环境为 Paper 26.2 build 124、Skript 2.16.2、 Java 25.0.4.1、Linux 6.17.0-1022-azure 和 4 个可见 CPU。下表是该次运行的 5000 行结果。 以下实测表以毫秒显示,保留六位小数:78.302884 ms 对应 78302884 ns,没有取整到毫秒。 正常情况下,tick 观察器每隔约 50 ms 执行一次,因此采用纳秒计时后,测得的间隔仍可能是几十毫秒; 计时精度与操作本身花了多久是两回事。每个数只代表共享 GitHub runner 上的一次运行, 不能据此给数据库做普遍排名。

各数据库都处理 5000 行,将任务规模固定下来,便于观察本次运行中读写等待时间和 tick 延迟的差异。总耗时与 tick 间隔放在一起,可以区分“整次操作等待较久”和“期间某次服务端更新迟到”;数据库操作即使跨多个 tick 完成,也不意味着这些 tick 全都被阻塞。

  • 后端:执行这一行测试的数据库,环境见上一表。
  • 写入总耗时、读取总耗时:从开始执行插件语句到返回的时间,包含源变量读取或结果存储、数据库执行,以及等待脚本恢复运行的时间;后续校验不计入。
  • 写入最大 tick 间隔、读取最大 tick 间隔:在对应操作的观察窗口内,tick 观察器相邻两次执行之间的最大间隔。窗口持续到操作返回后一个 tick;正常间隔约为 50 毫秒,超过它表示观察到更新延迟,这不是插件独占主线程的时间。
后端 写入总耗时 写入最大 tick 间隔 读取总耗时 读取最大 tick 间隔
SQLite 735.828866 ms 65.767643 ms 103.186998 ms 103.066121 ms
MySQL 462.517747 ms 50.395059 ms 78.302884 ms 78.218777 ms
MariaDB 685.122311 ms 66.066614 ms 95.789059 ms 95.813175 ms
PostgreSQL 585.103286 ms 66.926809 ms 94.172914 ms 94.082624 ms
MongoDB 717.433104 ms 50.968606 ms 78.761947 ms 78.579400 ms

大批量写入与重复写入

这张表把同一数据库的 10000 行写入与后续重复的 5000 行写入放在一起,检查更大的任务是否伴随更长的 tick 间隔。重复的 5000 行应与上一表中首次执行的 5000 行对照,才能观察重复运行时的变化;不能将不同规模的两列直接相减,作为缓存预热的收益。

  • 后端:执行写入的数据库,与上一表使用同一次运行。
  • 10000 行写入耗时:首次执行 10000 行写入的总耗时,计时范围与上一表相同。
  • 最大 tick 间隔:10000 行写入开始到返回后一个 tick 的观察窗口中,tick 观察器相邻两次执行之间的最大间隔。
  • 5000 行热写入耗时、热写入最大 tick 间隔:六种行数用例结束后,再执行一次 5000 行写入时的总耗时和最大观察器执行间隔;两者的统计窗口与上一表相同。“热写入”只是重复运行的标签,不保证某个缓存已经预热。
后端 10000 行写入耗时 最大 tick 间隔 5000 行热写入耗时 热写入最大 tick 间隔
SQLite 1501.908676 ms 94.670715 ms 779.342861 ms 50.628368 ms
MySQL 1398.534310 ms 51.944607 ms 684.881529 ms 50.515912 ms
MariaDB 1149.905662 ms 101.135769 ms 675.078706 ms 50.746686 ms
PostgreSQL 1255.801227 ms 95.786030 ms 623.383051 ms 50.885682 ms
MongoDB 1196.824500 ms 53.798503 ms 683.646486 ms 50.583169 ms

插件语句与原始 SQL

以下对照与上面的曲线分开运行,四列都是处理 5000 行的总耗时。插件写入包含跨 tick 读取输入变量的时间,原始 SQL 的字符串则在计时前就已构造完成。两者走的是不同的 写入路径,因此耗时比值不能直接当成 ORM 自身的开销。

每行固定数据库和行数,横向比较插件与手写 SQL 的完整调用时间,能看出这两条读写路径在本次环境中的耗时差异。写入与读取分列,是因为输入变量处理、数据库执行和结果存储的成本不同,不能合成一个“ORM 性能”数字。

  • 后端:两条读写路径使用的数据库。
  • 插件写入、插件读取:分别执行 insert many 与 select many 的总耗时,包含插件在主线程上的变量处理与等待数据库返回的时间。
  • 原始 SQL 写入、原始 SQL 读取:分别执行 execute update 和 execute query 的总耗时。写入 SQL 在计时前构造完毕;读取仍包含结果存储。
  • 不适用(不支持 SQL):MongoDB 不接受 SQL,不能运行这组原始 SQL 用例。这不表示它没有读写能力,也不表示耗时为零;其插件读写照常列出。
后端 插件写入 原始 SQL 写入 插件读取 原始 SQL 读取
SQLite 727.519482 ms 17.148948 ms 112.968835 ms 104.414372 ms
MySQL 586.972838 ms 46.793973 ms 76.245264 ms 91.635107 ms
MariaDB 628.867037 ms 74.916475 ms 91.608649 ms 89.937610 ms
PostgreSQL 778.881589 ms 61.349564 ms 99.224999 ms 102.880893 ms
MongoDB 685.494020 ms 不适用(不支持 SQL) 78.850687 ms 不适用(不支持 SQL)

MariaDB:不同数据量与重复运行

下面展开同一次 Run #4 的 MariaDB 结果,环境为 AMD EPYC 7763、mariadb:11.4,其余服务端版本与前文一致。数据取自原始纳秒记录,没有另外启动一轮测试。

固定数据库和运行环境,再改变行数,可以观察任务变大时总耗时和 tick 延迟如何变化。将首次与重复的 5000 行放在一起,也能观察相同任务在不同运行顺序下的表现;每项只有一次采样,不能据此判断耗时必然随行数线性增长。

  • 操作:插件写入或读取;“重复”指其他行数用例结束后再次执行。
  • 请求行数:脚本要求处理的行数,不一定是实际返回的行数。
  • 总耗时:从开始执行语句到返回的时间,包含变量处理、数据库执行和等待恢复运行,后续校验不计入。
  • 最大 tick 间隔:操作开始到返回后一个 tick 的观察窗口中,tick 观察器相邻两次执行之间的最大间隔,正常约为 50 毫秒。
  • tick 超出量:最大 tick 间隔减去 50 毫秒,负数按零计算,便于直接观察相对正常更新周期的延迟。
  • 结果:操作是否成功。10000 行读取超过 5000 行上限,记录的是查询被拒绝时的耗时,不是成功读取 10000 行的耗时。
操作 请求行数 总耗时 最大 tick 间隔 tick 超出量 结果
写入 100 183.019540 ms 77.813136 ms 27.813136 ms 成功
读取 100 58.978086 ms 54.904413 ms 4.904413 ms 成功
写入 500 125.585622 ms 50.931863 ms 0.931863 ms 成功
读取 500 59.719091 ms 59.571377 ms 9.571377 ms 成功
写入 1000 122.445042 ms 50.273827 ms 0.273827 ms 成功
读取 1000 59.743000 ms 59.761403 ms 9.761403 ms 成功
写入 2500 139.899172 ms 64.122445 ms 14.122445 ms 成功
读取 2500 79.123322 ms 79.081254 ms 29.081254 ms 成功
写入 5000 685.122311 ms 66.066614 ms 16.066614 ms 成功
读取 5000 95.789059 ms 95.813175 ms 45.813175 ms 成功
写入 10000 1149.905662 ms 101.135769 ms 51.135769 ms 成功
读取 10000 59.551810 ms 59.517446 ms 9.517446 ms 被拒绝:超过 5000 行读取上限
写入(重复) 5000 675.078706 ms 50.746686 ms 0.746686 ms 成功
读取(重复) 5000 82.868479 ms 82.686620 ms 32.686620 ms 成功

本次 10000 行写入的总耗时和最大 tick 间隔都高于 5000 行写入;重复的 5000 行写入总耗时相近,但最大 tick 间隔更短。100 行写入却比 500 行慢,可见这次结果并未随行数单调增加。运行顺序、启动阶段和共享主机活动都可能影响单次读数,但仅凭这张表无法确认原因,也不能只按行数推算耗时。

各用例的影响行数或保存行数已校验,10000 行读取也按预期被拒绝。gap 不是插件 CPU 耗时,而是 tick 观察器的最大执行间隔,其中包含调度和共享主机活动。本次运行早于提交 21898af 的 MySQL max_allowed_packet 修复;要测量当前 MySQL 路径,需要在该修复推送后重新运行。

下文区分现有测量结果与后续计划,并说明各项指标适合在哪一层采集。

测量分层

为了分析耗时来源,基准测试分为三个层次:

  • A:不启动 Paper 的 JVM 基准。 JMH 是用于反复运行并测量 Java 代码的基准测试工具,用例放在独立模块中。纯逻辑可以不连接数据库单独测量;现有插入用例使用内存 H2,因此结果同时包含数据库执行和插件代码的成本。
  • B:真实数据库。 测量数据库往返、语句解析与执行、连接池及事务提交等成本。通过 Testcontainers 启动各后端实例,沿用集成测试的配置。
  • C:Paper 服务端。 观察脚本执行期间的耗时和 tick 间隔,包括主线程读取源变量、保存查询结果等工作。基准脚本运行在一次性服务端上;内存占用与 MSPT(每个 tick 的耗时,单位毫秒)的分布仍需另外测量。

分层测量有助于定位问题:值转换属于 A,驱动批处理属于 B,主线程上的 Skript 变量读写属于 C。

触发器的总耗时包含数据库操作、主线程上的变量处理,以及等待脚本恢复运行的时间,不能单独用于判断服务端是否卡顿。最长 tick 间隔可以显示延迟,但不能准确拆分这些成本。

当前服务端用例报告什么

Paper 基准脚本目前记录 wallNs、gapNs、tick 数,以及影响行数或已保存行数。报告从 gapNs 推算 tick 超出量,并校验行数。这套测量尚不能单独得出主线程 CPU 耗时、数据库耗时、内存分配量或 MSPT 百分位。

后续计划补充的指标

下表列的是后续测量计划,不是实测成绩。把指标与适合采集它的层次放在一起,是为了决定应在插件代码、真实数据库还是完整服务端中补充测量,从而定位耗时或内存占用来自哪里。

指标是要观察的量;拟记录内容说明具体测什么;测量层引用前文的 A(不启动 Paper 的 JVM 基准)、B(真实数据库)和 C(Paper 服务端)。列在表中不代表当前用例已经输出该指标。

指标 拟记录内容 测量层
主线程耗时 操作在服务端主线程上占用的时间 C
后台耗时 数据库工作在后台线程执行的时间 B、C
触发器延迟 从语句开始到脚本恢复运行的总耗时 B、C
吞吐量 每秒处理的行数和值数 A、B、C
提交给驱动的语句数与行数 在 JDBC 边界统计调用与行数 B
数据库收到的语句数与写入行数 通过数据库计数器统计 B
每次操作的内存分配量 每次操作分配的字节数 A、B
峰值堆占用 操作期间持续采样得到的最大堆占用 B、C
回收后堆占用 用例结束并执行垃圾回收后的堆占用 B、C
MSPT 分布(p50、p99、最大值) 每个 tick 的耗时:中位数(p50)、99% 的 tick 不超过的耗时(p99)和最大值 C

要把触发器总耗时拆成数据库耗时与主线程耗时,还需要额外的测量手段;目前的 wallNs 包含这两部分,以及脚本等待恢复运行的时间。

计数与耗时

在共享 CI 主机上,相同测试的耗时曾相差 10% 至 20%;语句数则比较稳定。因此,CI 可以直接检查计数,耗时只作报告,不据此判定构建失败。

前述旧版二十批写入测试对每个数据库使用相同脚本和表定义。MySQL 与 MariaDB 共用 SQL 方言,但驱动发送批次的方式不同:当时在 JDBC 层,插件只提交一个批次;服务端计数却显示,Connector/J 将其中 5000 行逐行发送。单看耗时无法找到这个原因。当前 MySQL 路径改为参数化多行插入,下述旧计数不能作为新版的预期值。

基准模块还会统计 JDBC 层的调用。使用内存 H2 和通用 JDBC 路径时,一次 5000 行、六列的插入提交了一个批次。插件准确报告了 5000 个影响行,调用结束后表中也有 5000 行。

ServerSideCountIntegrationTest 测量真实数据库收到的语句。对 MySQL 系数据库,它在插件调用前后读取 Questions,扣除探针自身的开销,同时读取 Innodb_rows_inserted、Com_insert 和 Com_stmt_execute。这些计数覆盖整个服务器,因此测试会先检查是否有其他连接;如有,就只报告数字,不据此断言结果。驱动版本和设置会改变计数,所以结果会连同环境信息一起记录,不作为 CI 通过条件。

在一次 5000 行测试中,MySQL 8.0.46 和 8.4.11 各记录到 5038 条语句,其中 5000 条是插入语句(comInsert=5000、comStmtExecute=0)。MariaDB 11.4.13 记录到 2 条语句,其中一条是多行插入(comInsert=1、comStmtExecute=1)。测试账号无法从 MariaDB 取得插入行数。PostgreSQL 16.15 为目标表统计到 5000 行、为整个数据库统计到 5057 行,但现有计数器无法提供语句数;等待异步统计视图更新用了 814 毫秒。各后端都由插件报告了准确的 5000 个影响行,表中也确实增加了 5000 行。

这些都是每个后端在一台主机上的单次结果,不代表区间。日志记录了环境信息:os=Linux 6.17.0-1022-azure arch=amd64 cores=4 java=25.0.4.1 commit=81526798498f5ad30068fcb3299f2085062f868d。即使在同一次工作流中,MySQL 和 MariaDB 的作业运行于 Intel Xeon Platinum 8370C,PostgreSQL 的作业则运行于 AMD EPYC 7763。比较耗时时应看各作业实际使用的主机。

测试读取 PostgreSQL 的 pg_stat_user_tables.n_tup_ins 和 pg_stat_database.tup_inserted,并等待这些异步统计数据更新。它们统计行数,无法推算发送了多少条语句。要获得语句数,需要配置 pg_stat_statements 并重启服务器,或在协议层另行计数。

工具与后续方案

JMH、Testcontainers 和基准历史记录已用于现有测试。其余工具列为补充指标的可选方案。

这张表说明前述测量计划可以用什么工具实现,供选择测量方法时参考,不比较工具本身的速度。用途说明要收集哪类数据,括号中的 A、B、C 对应测量层;工具列出可使用的库或诊断工具;作用解释它能提供什么证据,以及仍需另外记录什么。

用途 工具 作用
微基准(A) JMH 正式计时前先运行代码,让 JVM 完成常见优化;用独立进程测量,并避免 JVM 把结果未被使用的被测代码删掉
提交给驱动的语句数与行数(B) datasource-proxy 或计数驱动 统计 JDBC 调用;单次批量调用实际包含多少行,还需另行记录
数据库收到的语句数与往返数(B) 数据库会话计数器或网络抓包 观察驱动批处理后的实际行为;结果受驱动版本和连接参数影响
tick 采样的百分位(C) HdrHistogram 对多次 tick 采样计算分位数
真实数据库(B) Testcontainers 启动测试数据库;复用实例可减少重复启动时间
历史与对比 benchmark-action/github-action-benchmark 按 CPU 和后端保存结果;耗时变化不作为构建失败条件
内存分配与停顿 JMH -prof gc、JFR、async-profiler 分析内存分配,以及 JVM 为垃圾回收等操作暂停线程的时间
MSPT(C) Paper 的 tick 采样 获取服务端自身记录的 tick 耗时

基准模块直接声明 JMH 及其注解处理器,因为现成的 Gradle 插件不支持本项目使用的 Gradle 版本。运行 ./gradlew :benchmarks:jmh,结果会写入 build/benchmarks/results.json。可用 -Pbenchmarks.filter=<正则> 选择用例,或用 -Pbenchmarks.forks=1 缩短本地测试时间。JMH 的注解处理器生成 Java 代码,因此基准类使用 Java。

CI 检查

编译、单元测试、ktlint 和集成测试编译已纳入 CI。

tick 基准作业会因结果行数不符或脚本报告错误而失败,不会因耗时变化而失败。后续如增加其他稳定且可复现的检查指标,可以与受共享 CI 主机影响较大的耗时数据分开处理。

基线放在哪里

基准结果只与硬件环境相近的运行比较。

  • CI 历史由基准测试 action 保存在 gh-pages 分支的 dev/bench/<slug>/ 下,按 CPU 型号分组。ubuntu-latest 可能分配不同处理器:同一提交的两次运行分别落在 AMD EPYC 9V74 和 Intel Xeon Platinum 8573C 上,插入用例耗时相差 1.32 倍。每个作业都会单独记录 CPU 型号;新版 tick 结果还按后端分开,存于 dev/bench/<slug>/tick-ns/<backend>/。
  • gh-pages 分支用于存储基准数据,本项目没有从中发布图表站点。JMH 原始结果位于 dev/bench/<slug>/data.js;tick 结果位于 dev/bench/<slug>/tick-ns/<backend>/。
  • **benchmarks/baseline.json**仅供本地参考,不作为 CI 阈值。benchmarks/baseline.environment.txt 记录生成基线时使用的机器。

结果还会记录 JDK、操作系统、CPU、容器镜像、数据库驱动、Paper 和 Skript 的版本,以及提交和工作区状态,以便区分代码变动与环境变动。

测试计划

  • 阶段 0:微基准。 为值转换、结果存储、批次输入及 insert many 发送的语句数建立 JMH 用例,保存 JSON 结果和参考基线。
  • 阶段 1:数据库矩阵。 为各后端、各类语句和不同表结构加入语句计数。
  • 阶段 2:服务端 tick(已配置五种后端)。 ./gradlew serverBenchmark 启动装有 Skript、skript-reflect 和本插件的 Paper 服务端,运行 server-benchmark/skript 中的脚本。脚本通过 SKRIPTORM_BENCH 输出以纳秒为单位的耗时及最长 tick 间隔;如果写入行数不符或脚本输出 FAIL,任务就会失败。CI 另行检查一次读取最多 5000 行、每条写入语句最多绑定 30000 个值的限制。夜间和按需运行的 tick 矩阵将结果存入 dev/bench/<slug>/tick-ns/<backend>/。详见读取行和写入行。脚本计时器无法提供 MSPT 百分位。
  • 阶段 3:压力与故障测试(计划中)。 覆盖并发、取消、执行途中断开数据库、查询期间重载插件、超限结果与批次,以及连接池耗尽。
  • 阶段 4:完善 CI 报告(计划中)。 增加上述尚缺的指标,将稳定的正确性检查与耗时趋势分开处理。

这里不覆盖什么

  • 在线玩家。 空闲服务端上的结果无法预测大量玩家和实体活动时的表现。
  • 长时间运行。 现有用例仅持续数秒,无法发现几天后才出现的泄漏。
  • 未纳入测试的驱动。 通用 JDBC 连接可使用本仓库未测试的驱动;MySQL 的结果不能代表这些驱动。
  • PostgreSQL 语句数。 当前统计信息能提供行数和事务数,不能统计单条操作发送的语句数。
  • 跨机器的绝对耗时。 耗时必须结合测试环境解读。

skript-orm

参考

实用指南


skript-orm (English)

Reference

Practical guides


Wiki 由仓库中的 README 和 docs/ 自动生成。修改文档请到仓库提交,直接编辑 Wiki 的内容会在下次同步时被覆盖。

This wiki is generated from the README files and docs/ in the repository. Please submit changes there; direct wiki edits are overwritten on the next sync.

Clone this wiki locally