LevelDB 开发者指南:从打开数据库到性能调优的完整 API 实践
本文以 LevelDB 官方使用文档 doc/index.md 为主体,系统讲解这个由 Google 团队实现的持久化有序键值存储库的全部核心 API:打开/关闭数据库、Status 错误处理、读写与原子批量更新、同步写与持久化权衡、并发模型、迭代器、快照、Slice、自定义比较器、以及块大小/压缩/缓存/布隆过滤器等性能调优手段。读完后,你可以直接基于当前仓库的头文件(include/leveldb/db.h、include/leveldb/options.h 等)写出可运行的 LevelDB 应用,并理解每个参数背后的实现依据。
LevelDB 是什么
LevelDB 提供一个持久化的键值存储(persistent key value store)。键和值都是任意字节数组;键按照用户指定的比较器(comparator)在存储中保持有序。当前仓库的版本常量定义在 db.h 中:kMajorVersion = 1、kMinorVersion = 23。
整个库的公共 API 都位于 include/leveldb/ 目录下:db.h(数据库主接口)、options.h(三组选项结构)、iterator.h(迭代器)、write_batch.h(批量写)、slice.h(字节切片)、status.h(状态码)、cache.h、comparator.h、env.h、filter_policy.h 等。
打开数据库
一个 LevelDB 数据库有一个名称,对应文件系统中的一个目录,数据库的全部内容都存储在该目录中。以下示例展示如何打开一个数据库,必要时创建它:
#include <cassert>
#include "leveldb/db.h"
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
assert(status.ok());
...
这里涉及的两个开关在 Options 中都有定义和注释:
create_if_missing(默认false):若目录不存在则创建数据库;error_if_exists(默认false):如果你希望在数据库已经存在时报错,在DB::Open调用前加上options.error_if_exists = true;。
从源码结构看,打开流程由 DB::Open 的静态接口进入,实现位于 db/db_impl.cc,Open 成功时向 *dbptr 写入堆分配的数据库指针,失败时置空并返回非 OK 的 Status。
独占锁机制:LevelDB 在打开数据库时会向操作系统申请文件锁以防止多进程误用。db_impl.cc 第 300 行附近即调用 env_->LockFile(LockFileName(dbname_), &db_lock_);锁文件名由 filename.cc 生成,就是数据库目录下的 LOCK 文件(dbname + "/LOCK"),它也是目录扫描时识别数据库类型 kDBLockFile 的依据(见 filename_test.cc)。因此同一时刻只有一个进程能打开同一个数据库。
Status 错误处理
上例中的 leveldb::Status 类型会被大多数可能出错的 LevelDB 函数返回。你可以检查结果是否 ok,并打印关联的错误信息:
leveldb::Status s = ...;
if (!s.ok()) cerr << s.ToString() << endl;
status.h 定义了完整的语义:
Status::OK()表示成功,ok()等价于内部state_ == nullptr;- 预置的错误工厂包括
NotFound、Corruption、NotSupported、InvalidArgument、IOError,分别对应内部代码kNotFound=1、kCorruption=2、kNotSupported=3、kInvalidArgument=4、kIOError=5; - 配套的谓词
IsNotFound()、IsCorruption()、IsIOError()等可用于分支判断——例如Get在键不存在时返回的就是Status::IsNotFound()为真的状态; ToString()在成功时返回字符串 "OK",失败时返回带前缀的错误描述。
Status 内部以 new[] 出的字符数组承载(state_[0..3] 为消息长度、state_[4] 为代码、state_[5..] 为消息),拷贝构造会深拷贝该状态,可安全跨线程传递 const 方法。
关闭数据库
用完数据库后,直接删除数据库对象即可:
... open the db as described above ...
... do something with db ...
delete db;
注意 DB::NewIterator 的注释要求:返回的迭代器必须在该 DB 删除之前先删除("The returned iterator should be deleted before this db is deleted")。
读与写
数据库提供 Put、Delete、Get 三个方法修改/查询数据。以下代码把 key1 下的值迁移到 key2:
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);
if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);
三个接口均为纯虚方法,声明在 DB 中,并各自带有 "consider setting options.sync = true" 的注释提示。Get 的行为细节:命中则把值写入 *value 并返回 OK;键不存在则 *value 保持不变并返回 IsNotFound() 为真的状态。Delete 对不存在的键不算错误。
原子更新(WriteBatch)
注意上面"迁移值"的写法有一个隐患:如果进程在 Put(key2) 之后、Delete(key1) 之前崩溃,同一值会残留在多个键下。要避免这类问题,可以使用 WriteBatch 类原子地应用一组更新:
#include "leveldb/write_batch.h"
...
std::string value;
leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);
if (s.ok()) {
leveldb::WriteBatch batch;
batch.Delete(key1);
batch.Put(key2, value);
s = db->Write(leveldb::WriteOptions(), &batch);
}
WriteBatch 持有一组按加入顺序依次应用的编辑(write_batch.h 头部注释举例:对同一键依次 Put v1 / Delete / Put v2 / Put v3,最终值为 v3)。上面示例中先 Delete 再 Put,是为了在 key1 与 key2 相同时不至于把值整个丢掉。
除原子性收益外,WriteBatch 还能加速批量更新——把大量修改放进同一批次一次 Write。实现上,批次的操作被序列化进 rep_(二进制表示,见 db/write_batch.cc 与 db/write_batch_internal.h),测试用例 db/write_batch_test.cc 覆盖了顺序语义与 Iterate 回放逻辑。
同步写与持久化权衡
默认情况下,LevelDB 的每次写是异步的:调用返回时只是把数据从进程内存推进了操作系统;从操作系统内存到持久存储的落盘是异步完成的。对某一次写设置 sync 标志可以让写操作"一路推到底"才返回:
leveldb::WriteOptions write_options;
write_options.sync = true;
db->Put(write_options, ...);
WriteOptions 的注释给出了精确的崩溃语义对照:
sync == false时,语义类似write()系统调用;进程自身崩溃(机器未重启)不会丢数据,因为更新在写被判定"完成"之前已推入操作系统;但机器崩溃可能丢失最近的少量更新;sync == true时,语义类似write()后跟fsync()。在 Posix 系统上,这通过调用fsync(...)、fdatasync(...)或msync(..., MS_SYNC)实现(对应WritableFile::Sync())。
文档明确指出:异步写通常比同步写快一千倍以上,代价是机器崩溃可能丢失最后几条更新。两种缓解方案值得记入实战手册:
- 批量重载:向数据库灌入大量数据时,可依赖"崩溃后重跑批量加载"来兜底丢掉的更新;
- 混合策略:每第 N 次写使用同步写,崩溃后从上一次同步写完成的位置恢复(同步写可以同时更新一个"断点标记")。
此外,WriteBatch 提供了折中:把多条更新放进同一批次并整体做同步写,一次 sync 的额外开销被批次内的所有写均摊。从源码结构看,db->Write 在 db/db_impl.cc 中把 w.sync = options.sync 传给写批处理,并在第 1237-1248 行附近对 options.sync 为真时检查落盘错误并置 sync_error。测试侧,db/db_test.cc 用 data_sync_error_ / delay_data_sync_ 等原子标志模拟同步失败与延迟,验证了上述语义。
并发模型
- 跨进程:一个数据库同一时刻只允许一个进程打开,LevelDB 通过向操作系统申请锁防止误用(即前述
LOCK文件锁,见 db/filename.h 的LockFileName); - 进程内:同一个
leveldb::DB对象可以被多个并发线程安全共享——不同线程可以无外部同步地向同一数据库写入、获取迭代器或调用Get,LevelDB 内部自动完成所需的同步。DB 的类注释即声明 "A DB is safe for concurrent access from multiple threads without any external synchronization"; - 其他对象需要自行加锁:
Iterator、WriteBatch、Slice、Status等对象的 const 方法可无同步并发调用,但只要任一线程可能调用非 const 方法(如Next()、batch.Put()),共享该对象的线程必须使用自己的锁协议保护访问。这些约束逐一写在各头文件顶部注释中(如 iterator.h、write_batch.h)。
迭代
以下示例打印数据库中全部键值对:
leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
cout << it->key().ToString() << ": " << it->value().ToString() << endl;
}
assert(it->status().ok()); // 检查扫描过程中发现的任何错误
delete it;
只处理 [start, limit) 范围内键的变体:
for (it->Seek(start);
it->Valid() && it->key().ToString() < limit;
it->Next()) {
...
}
也可以反向遍历(注意:反向迭代通常比正向略慢):
for (it->SeekToLast(); it->Valid(); it->Prev()) {
...
}
这六个核心方法 Valid / SeekToFirst / SeekToLast / Seek / Next / Prev 加上 key()、value()、status() 构成 Iterator 抽象接口,全部为纯虚函数。两个使用要点来自接口注释:迭代器初始处于无效状态,必须先调用某个 Seek 方法;key()/value() 返回的 Slice 所引用的底层存储只在迭代器下次修改前有效,需要长期保存请先 ToString()。
快照(Snapshot)
快照提供对整个键值存储状态的一致只读视图。ReadOptions::snapshot 非空时,读操作作用于该指定版本的 DB 状态;为空时,读操作作用于当前状态的隐式快照。
leveldb::ReadOptions options;
options.snapshot = db->GetSnapshot();
... apply some updates to db ...
leveldb::Iterator* iter = db->NewIterator(options);
... read using iter to view the state when the snapshot was created ...
delete iter;
db->ReleaseSnapshot(options.snapshot);
创建快照用 DB::GetSnapshot(),快照不再需要时必须用 DB::ReleaseSnapshot 释放,这允许实现丢弃仅为支持该快照读而保留的状态。Snapshot 是不可变对象,因此可被多线程无同步安全访问。ReadOptions 中三个字段(verify_checksums、fill_cache、snapshot)的语义在 options.h 中有完整注释。
Slice:轻量字节视图
it->key() 与 it->value() 的返回值是 leveldb::Slice 的实例。Slice 是一个简单结构,包含一个长度和一个指向外部字节数组的指针——相比返回 std::string,它省去了拷贝(可能是很大的键/值)的开销。另外,LevelDB 的方法不返回以 null 结尾的 C 风格字符串,因为键和值中允许包含 '\0' 字节。
C++ 字符串和 C 风格字符串可以方便地转为 Slice:
leveldb::Slice s1 = "hello";
std::string str("world");
leveldb::Slice s2 = str;
Slice 也可以转回 C++ 字符串:
std::string str = s1.ToString();
assert(str == std::string("hello"));
生命周期陷阱:Slice 只是指针+长度(见 slice.h 中的 data_ 与 size_ 两个成员),调用方必须保证 Slice 使用期间其指向的外部字节数组仍然存活。以下写法有 bug:
leveldb::Slice slice;
if (...) {
std::string str = ...;
slice = str;
}
Use(slice);
当 if 语句块结束时 str 被销毁,slice 的底层存储随之消失,Use(slice) 变成悬垂引用。Slice 还提供 compare(三向字节比较)、starts_with、remove_prefix 等便捷方法,均直接作用于 data_/size_,无拷贝。
自定义比较器(Comparator)
前面的示例都使用默认的键排序——按字节字典序。打开数据库时也可以提供自定义比较器。假设每个键由两个数组成,按第一个数排序、第一个数相同再按第二个数排:
class TwoPartComparator : public leveldb::Comparator {
public:
// Three-way comparison function:
// if a < b: negative result
// if a > b: positive result
// else: zero result
int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {
int a1, a2, b1, b2;
ParseKey(a, &a1, &a2);
ParseKey(b, &b1, &b2);
if (a1 < b1) return -1;
if (a1 > b1) return +1;
if (a2 < b2) return -1;
if (a2 > b2) return +1;
return 0;
}
// Ignore the following methods for now:
const char* Name() const { return "TwoPartComparator"; }
void FindShortestSeparator(std::string*, const leveldb::Slice&) const {}
void FindShortSuccessor(std::string*) const {}
};
然后使用该比较器创建数据库:
TwoPartComparator cmp;
leveldb::DB* db;
leveldb::Options options;
options.create_if_missing = true;
options.comparator = &cmp;
leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);
...
向后兼容规则
比较器 Name() 方法的返回值会在数据库创建时被持久化到数据库中,并在之后每次打开数据库时校验。如果名称变了,leveldb::DB::Open 调用会失败。因此,只有在新的键格式与比较函数和现有数据库不兼容、且可以丢弃所有现有数据库内容时,才应该改名。这一点在 Options::comparator 的注释里同样以 REQUIRES 形式强调;名称不一致的校验路径体现在版本描述(MANIFEST 记录)的解析中,db/version_edit.cc 在比较器名不匹配时报出 "comparator name" 错误。
如果计划好,键格式仍可渐进演化:例如在每个键的末尾存储一个版本号(一字节通常够用)。当想切换到新键格式时(比如给 TwoPartComparator 的键增加可选的第三部分):(a) 保持比较器名称不变;(b) 新键递增版本号;(c) 修改比较函数,根据键中的版本号决定如何解释键。
性能调优
性能可以通过修改 options.h 中定义的各类型的默认值来调整。当前仓库中 Options 的完整默认值一览(以源码为准):
| 参数 | 默认值 | 说明 |
|---|---|---|
comparator |
字节字典序比较器 | 定义键的排序 |
create_if_missing / error_if_exists |
false / false |
创建/存在性检查 |
paranoid_checks |
false |
内部损坏的激进检查 |
env |
Env::Default() |
环境抽象 |
info_log |
nullptr(写 DB 目录下的日志文件) |
内部进度/错误日志 |
write_buffer_size |
4 MB | 内存中累积的未排序日志数据量 |
max_open_files |
1000 | 数据库可使用的打开文件数 |
block_cache |
nullptr(内部自动创建 8 MB 缓存) |
未压缩块缓存 |
block_size |
4 KB(未压缩) | 每块打包的用户数据量 |
block_restart_interval |
16 | 键增量编码的重启点间隔 |
max_file_size |
2 MB | 切换新文件前写入的字节数 |
compression |
kSnappyCompression |
块压缩算法(另有 kNoCompression、kZstdCompression) |
zstd_compression_level |
1 | zstd 压缩级别,支持范围 [-5, 22] |
reuse_logs |
false |
实验性:打开时追加复用现有 MANIFEST 和 log 文件 |
filter_policy |
nullptr |
布隆过滤器等,减少磁盘读 |
块大小(Block Size)
LevelDB 把相邻的键组织到同一个块中,块是与持久存储之间传输的单位。默认块大小约为 4096 字节的未压缩数据(对应 block_size = 4 * 1024,且该参数可动态修改)。以批量扫描为主的应用可以考虑调大块;以小块点查为主的应用,若性能测量显示有效,可改用更小的块。块小于 1KB 或大于几 MB 意义不大;且压缩对更大的块更有效。
压缩(Compression)
每个块在写入持久存储前被独立压缩。压缩默认开启,因为默认压缩方法非常快,并且会自动对不可压缩数据关闭压缩。仅在基准测试证明有性能收益时才应完全禁用压缩:
leveldb::Options options;
options.compression = leveldb::kNoCompression;
... leveldb::DB::Open(options, name, ...) ....
CompressionType 枚举包含三个值:kNoCompression = 0x0、kSnappyCompression = 0x1、kZstdCompression = 0x2,注释特别提醒这些值是磁盘持久格式的一部分,不得改动已有条目。kSnappyCompression 的注释还给出量级参考:在 Intel Core 2 2.4GHz 上压缩约 200–500MB/s、解压约 400–800MB/s,显著快于大多数持久存储速度,因此通常不值得切换到 kNoCompression;即便输入不可压缩,Snappy 实现也能高效检测并切换到未压缩模式。
块缓存(Block Cache)
数据库内容以文件集合形式存储于文件系统,每个文件保存一串压缩块。若 options.block_cache 非空,它用于缓存常用的未压缩块内容;为空时 LevelDB 会自动创建并使用一个 8MB 的内部缓存。
#include "leveldb/cache.h"
leveldb::Options options;
options.block_cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache
leveldb::DB* db;
leveldb::DB::Open(options, name, &db);
... use the db ...
delete db
delete options.block_cache;
注意:缓存存放的是未压缩数据,应按应用层数据规模(不扣除压缩收益)来设定大小。压缩块的缓存交给操作系统缓冲区缓存或客户端自实现的 Env 负责。做批量读时,可以关闭缓存以免批量数据把缓存内容挤出:
leveldb::ReadOptions options;
options.fill_cache = false;
leveldb::Iterator* it = db->NewIterator(options);
for (it->SeekToFirst(); it->Valid(); it->Next()) {
...
}
delete it;
LRU 缓存的实现位于 util/cache.cc,接口声明在 cache.h。
键布局(Key Layout)
磁盘传输与缓存的单位是块,按数据库排序的相邻键通常落在同一块中。因此应用可以通过键布局优化性能:把经常一起访问的键放近,把很少用的键放到键空间的单独区域。文档给出的文件系统例子:要存储 filename -> 权限位、长度、file_block_id 列表 和 file_block_id -> 数据 两类条目。可以给文件名键加前缀 '/',给 file_block_id 键加前缀 '0',这样只扫描元数据时就不必把笨重的文件内容拉进缓存。
过滤器(Filters)
由于 LevelDB 在磁盘上的数据组织方式,一次 Get() 可能涉及多次磁盘读。可选的 FilterPolicy 机制可以大幅减少磁盘读次数:
leveldb::Options options;
options.filter_policy = NewBloomFilterPolicy(10);
leveldb::DB* db;
leveldb::DB::Open(options, "/tmp/testdb", &db);
... use the database ...
delete db;
delete options.filter_policy;
上述代码给数据库关联了一个基于布隆过滤器的策略:每键在内存中保留若干位数据(示例传参 10,即每键 10 位)。该过滤器大约把 Get() 所需的非必要磁盘读减少 100 倍;每键位数越大,减少越多,代价是更多内存。文档建议:工作集装不进内存、且大量随机读的应用应设置过滤器策略。过滤器实现位于 util/bloom.cc,块级组织在 table/filter_block.cc,接口细节见 filter_policy.h。
使用自定义比较器时,必须确保过滤器策略与比较器兼容。例如某比较器在比较键时忽略尾部空格,那么 NewBloomFilterPolicy 就不能用于它——应提供同样忽略尾部空格的自定义过滤器策略:
class CustomFilterPolicy : public leveldb::FilterPolicy {
private:
leveldb::FilterPolicy* builtin_policy_;
public:
CustomFilterPolicy() : builtin_policy_(leveldb::NewBloomFilterPolicy(10)) {}
~CustomFilterPolicy() { delete builtin_policy_; }
const char* Name() const { return "IgnoreTrailingSpacesFilter"; }
void CreateFilter(const leveldb::Slice* keys, int n, std::string* dst) const {
// Use builtin bloom filter code after removing trailing spaces
std::vector<leveldb::Slice> trimmed(n);
for (int i = 0; i < n; i++) {
trimmed[i] = RemoveTrailingSpaces(keys[i]);
}
builtin_policy_->CreateFilter(trimmed.data(), n, dst);
}
};
高级应用也可以提供不使用布隆过滤器、而以其他方式归纳一组键的过滤器策略。
校验和(Checksums)
LevelDB 为存储在文件系统中的所有数据都关联校验和。有两个独立的开关控制校验和验证的激进程度:
ReadOptions::verify_checksums:设为true可对某次特定读取从文件系统读出的全部数据强制校验和验证。默认不做这种验证(默认false,见 ReadOptions);Options::paranoid_checks:打开数据库前设为true,让实现在检测到内部损坏时尽快报错。错误可能在打开数据库时抛出,也可能在之后的某个操作中抛出,取决于损坏发生在数据库的哪一部分。默认关闭,这样即使持久存储的部分内容损坏,数据库仍可使用。
如果数据库损坏(例如打开 paranoid 检查后无法打开),可以用 leveldb::RepairDB 函数恢复尽可能多的数据。RepairDB 声明在 db.h,实现见 db/repair.cc:其头部注释描述了恢复流程——先把所有 log 文件转成 table,扫描每个 table 计算最小/最大键和最大序列号,然后重建描述文件(log number 置 0、next-file-number 取最大文件号+1、所有 table 文件都放入 level 0)。由于可能丢失部分数据,对重要数据库要谨慎使用。paranoid_checks 与校验相关行为有对应测试覆盖,可参考 db/corruption_test.cc 与 db/fault_injection_test.cc。
近似大小(Approximate Sizes)
GetApproximateSizes 方法可用于获取一个或多个键范围占用的文件系统空间近似字节数:
leveldb::Range ranges[2];
ranges[0] = leveldb::Range("a", "c");
ranges[1] = leveldb::Range("x", "z");
uint64_t sizes[2];
db->GetApproximateSizes(ranges, 2, sizes);
上述调用把 sizes[0] 设为键范围 [a..c) 占用的近似文件系统字节数,sizes[1] 设为 [x..z) 的近似值。Range 是左闭右开结构(start 包含在范围内,limit 不包含);GetApproximateSizes 的接口注释还提醒:返回的是文件系统空间(压缩十倍的数据只占十分之一),且结果可能不包含最近写入的数据大小。
环境(Environment)
LevelDB 实现发出的所有文件操作(以及其他操作系统调用)都经过一个 leveldb::Env 对象。高级客户端可以提供自己的 Env 实现以获得更细的控制,例如在文件 IO 路径中引入人为延迟来限制 LevelDB 对系统其他活动的影响:
class SlowEnv : public leveldb::Env {
... implementation of the Env interface ...
};
SlowEnv env;
leveldb::Options options;
options.env = &env;
Status s = leveldb::DB::Open(options, ...);
默认实现见 util/env_posix.cc(Posix)与 util/env_windows.cc(Windows)。仓库还提供了内存文件系统示例 helpers/memenv/memenv.cc 及其测试 helpers/memenv/memenv_test.cc。
移植(Porting)
LevelDB 可以通过为 port/port.h 导出的类型/方法/函数提供平台特定实现来移植到新平台,更多细节见 port/port_example.h。此外,新平台可能还需要新的默认 leveldb::Env 实现,可参考 util/env_posix.cc(原文档写作 leveldb/util/env_posix.h,当前仓库中该实现位于 util/ 下)。
深入实现:相关文档
关于 LevelDB 实现的更多细节,可查阅仓库 doc/ 目录下的以下文档:
- 实现笔记——LSM 树整体架构、写入/读取/压缩流程;
- 不可变 Table 文件格式——数据文件、块、索引块、过滤块的字节级布局;
- 日志文件格式——写日志(WAL)的帧格式与 CRC。
配合 README.md 中的构建与测试说明(CMake 构建、GoogleTest 单元测试),以上材料可以支撑从 API 使用到格式细节的完整学习路径。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00