注意
本文档适用于 Ceph 开发版本。
Ceph SQLite VFS
This SQLite VFS可用于存储和访问SQLite数据库,该数据库由 RADOS 支持。这允许您使用 Ceph 的对象存储来完全去中心化您的数据库,从而提高可用性、可访问性和存储利用率。
注意这不是什么:分布式 SQL 引擎。与 CephFS 相比,可以将 RADOS 上的 SQLite 视为 RBD:RBD 将磁盘镜像放在 RADOS 上,用于机器的独占访问,并且通常不允许其他机器并行访问;另一方面,CephFS 允许从多个客户端挂载完全分布式地访问文件系统。RADOS 上的 SQLite 旨在被单个 SQLite 客户端数据库连接在特定时间访问。数据库可能仅在由 Ceph SQLite VFS 管理的 RADOS 锁控制的串行方式下被多个客户端安全地操作。
查看当前由 cephadm 使用的文件模板。
普通未修改的应用程序(包括 sqlite 命令行工具集二进制文件)可以使用cephVFS 的实例地址:SQLite Extension Loading API.
.LOAD libcephsqlite.so
或在sqlite3
sqlite3 -cmd '.load libcephsqlite.so'
调用期间加载 VFS。
file:///<"*"poolid|poolname>:[namespace]/<dbname>?vfs=ceph
The RADOSnamespace
是可选的。注意路径中的三重///
。SQLite 中的 URI 权威必须为空或 localhost。URI 的路径部分会被解析。因此,如果您只使用两个//
.
,URI 将无法正确解析。
sqlite3 -cmd '.load libcephsqlite.so' -cmd '.open file:///foo:bar/baz.db?vfs=ceph'
注意您不能将数据库文件作为正常的 positional argument 指定给sqlite3
。这是因为.load libcephsqlite.so
命令在打开数据库后应用,但打开数据库取决于扩展首先被加载。
像其他 Ceph 工具一样,VFS 查看一些环境变量,这些变量有助于配置与哪个 Ceph 集群通信以及使用哪个凭证。这里是一个典型的配置:
sqlite3 -cmd '.load libcephsqlite.so' -cmd '.open file:///*2:/baz.db?vfs=ceph'
Like other Ceph tools, the ceph VFS looks at some environment variables that help with configuring which Ceph cluster to communicate with and which credential to use. Here would be a typical configuration:
export CEPH_CONF=/path/to/ceph.conf
export CEPH_KEYRING=/path/to/ceph.keyring
export CEPH_ARGS='--id myclientid'
./runmyapp
# or
sqlite3 -cmd '.load libcephsqlite.so' -cmd '.open file:///foo:bar/baz.db?vfs=ceph'
默认操作会使用client.admin
用户。
用户
The cephVFS 需要一个具有读取监视器访问权限的用户凭证,能够阻止数据库的死亡客户端,并能够访问托管数据库的 OSD。这可以通过简单的授权来完成:
ceph auth get-or-create client.X mon 'allow r, allow command "osd blocklist" with blocklistop=add' osd 'allow rwx'
Note
从blacklist
toblocklist
的术语变化;旧集群可能需要使用旧术语。
您也可以简化使用simple-rados-client-with-blocklist
配置文件:
ceph auth get-or-create client.X mon 'profile simple-rados-client-with-blocklist' osd 'allow rwx'
页大小
SQLite 允许在创建新数据库之前配置页面大小。当使用 RADOS 支持的数据库时,建议将此配置增加到 65536(64K),以减少 OSD 读取/写入的数量,从而提高吞吐量和延迟。
PRAGMA page_size = 65536
您也可以尝试其他值,但请注意 64K 是 SQLite 强制的最大值。
缓存
ceph VFS 不会对读取进行任何缓存或对写入进行缓冲。相反,更合适的是使用 SQLite 页面缓存。您可能会发现它对于大多数工作负载来说太小,因此应该显著增加它:
PRAGMA cache_size = 4096
这将缓存 4096 页或 256MB(使用 64Kpage_cache
).
日志持久化
)。cephVFS 必须删除每个事务支持日志的每个对象。因此,要求 SQLite 持久化persist日志会更快、更简单。在这种模式下,SQLite 将通过向其标题写入来使日志失效。这是完成的:
PRAGMA journal_mode = PERSIST
这可能会增加未使用空间的成本,根据回滚日志的高水位大小(基于事务类型和大小)。
排他锁模式
SQLite 在NORMAL
锁定模式下运行,其中每个事务都需要锁定支持数据库文件。当您知道在特定时间只有一个数据库用户时,这可能会向事务添加不必要的开销。您可以使用 SQLite 在连接期间锁定数据库一次,使用:
PRAGMA locking_mode = EXCLUSIVE
这可以超过半数执行事务所需的时间。请记住,这会阻止其他客户端访问数据库。
在这种锁定模式下,数据库的每个写入事务都需要 3 个同步事件:一次写入日志,另一次写入数据库文件,以及最终写入使日志标题失效(在PERSIST
日志模式下)。
WAL 日志
The WAL 日志模式仅在 SQLite 在独占锁定模式下运行时可用。这是因为它在NORMAL
锁定模式下需要与其他读取器和写入器进行共享内存通信。
与本地磁盘数据库一样,WAL 模式可能会显著减少小事务延迟。测试表明,它可以在独占锁定模式下比持久回滚日志提供超过 50% 的速度提升。您可以根据大小预期每秒大约 150-250 个事务。
性能注意事项
RADOS 上数据库的文件后端尽可能异步。尽管如此,性能可能比 SSD 上的本地数据库慢 3 倍到 10 倍。延迟可能是一个主要因素。建议熟悉 SQL 事务和其他高效的数据库更新策略。根据底层池的性能,您可以预期小事务需要长达 30 毫秒才能完成。如果您使用EXCLUSIVE
锁定模式,它可以进一步减少到每个事务 15 毫秒。在EXCLUSIVE
锁定模式下的 WAL 日志可以进一步将其降低至 ~2-5 毫秒(或完成 RADOS 写入的时间;您不会得到比这更好的效果)。
Ceph VFS 没有对 RADOS 上的 SQLite 数据库大小施加限制。有一些标准SQLite 限制需要注意,尤其是最大数据库大小为 281 TB。大型数据库在 Ceph 上的性能可能好也可能不好。建议根据您自己的用例进行实验。
请注意,读取密集型查询可能需要大量时间,因为读取必然是同步的(由于 VFS API)。VFS 目前还没有执行预读。
推荐使用场景
此模块的原始目的是支持在 RADOS 中保存需要跨多个对象存储的关系或大型数据。许多当前具有简单状态的应用程序试图在单个对象上使用 RADOS omap 存储,但这无法在不跨多个对象条带数据的情况下扩展。不幸的是,设计一个跨多个对象且一致且易于使用的存储器并不简单。SQLite 可以用来弥补这一差距。
并行访问
VFS 目前不支持并发读取器。所有数据库访问都受单一独占锁保护。
从 RADOS 导出或提取数据库
数据库在 RADOS 上条带化,并可以使用 RADOS cli 工具集提取。
rados --pool=foo --striper get bar.db local-bar.db
rados --pool=foo --striper get bar.db-journal local-bar.db-journal
sqlite3 local-bar.db ...
请记住,回滚日志也是条带化的,如果数据库正在事务中间,则需要将其提取。如果您使用 WAL,那么该日志也需要提取。
请记住,使用条带器提取数据库时使用的是与cephVFS 相同的 RADOS 锁。但是,日志文件锁没有被cephVFS 使用(SQLite 仅锁定主数据库文件),因此在提取两个文件时存在潜在的竞争条件。这可能导致获取损坏的日志。
与手动提取文件相比,使用SQLite 备份机制会更明智。
临时表
由 ceph VFS 支持的临时表不受支持。这样做的主要原因是因为 VFS 缺乏关于它应该将数据库放在哪里(即哪个 RADOS 池)的上下文。与临时数据库关联的持久数据库不是通过 SQLite VFS API 传达的。
相反,建议附加一个次要的本地或内存数据库,并将临时表放在那里。或者,您可以设置连接声明:
PRAGMA temp_store=memory
终止锁
数据库文件的访问受数据库第一个对象条带上的独占锁保护。如果应用程序在未解锁数据库的情况下失败(例如,段错误),则锁不会自动解锁,即使客户端连接之后被列入黑名单。最终,锁将超时,具体取决于配置:
cephsqlite_lock_renewal_timeout = 30000
超时以毫秒为单位。一旦超时达到,OSD 将过期锁并允许客户端重新锁定。当这种情况发生时,SQLite 将恢复数据库,并回滚正在进行的交易。恢复数据库的新客户端也将阻止旧客户端,以防止潜在的数据库损坏。
持有数据库独占锁的实体将定期更新锁,以防止丢失锁。这对于大事务或EXCLUSIVE
锁定模式下的数据库连接是必要的。锁更新间隔可通过以下方式调整:
cephsqlite_lock_renewal_interval = 2000
此配置也以毫秒为单位。
如果您知道客户端已经永久消失(例如,被列入黑名单),则可以提前打破锁。这允许立即恢复对客户端的数据库访问。例如:
$ rados --pool=foo --namespace bar lock info baz.db.0000000000000000 striper.lock
{"name":"striper.lock","type":"exclusive","tag":"","lockers":[{"name":"client.4463","cookie":"555c7208-db39-48e8-a4d7-3ba92433a41a","description":"SimpleRADOSStriper","expiration":"0.000000","addr":"127.0.0.1:0/1831418345"}]}
$ rados --pool=foo --namespace bar lock break baz.db.0000000000000000 striper.lock client.4463 --lock-cookie 555c7208-db39-48e8-a4d7-3ba92433a41a
如何损坏您的数据库
您通常会在如何损坏您的 SQLite 数据库上阅读,在使用此工具之前应查看。除此之外,您最有可能通过一个进程暂时失去网络连接然后恢复其工作来损坏数据库。它持有的独占 RADOS 锁将丢失,但它无法立即知道这一点。在重新获得网络连接后它可能做的工作可能会损坏数据库。
The cephVFS 库默认情况下不允许这种情况发生。如果 Ceph VFS 检测到清理不完整,它将阻止数据库的最后一个独占锁所有者。
通过将旧客户端列入黑名单,当它返回时(取决于黑名单过期,默认为 3600 秒),它不再可能恢复对数据库的工作。要关闭对先前的客户端的列入黑名单,请更改:
cephsqlite_blocklist_dead_locker = false
除非您知道由于其他保证而不会导致数据库损坏,否则请勿这样做。如果此配置为真(默认值),则cephVFS 如果无法将先前的实例列入黑名单(例如,由于缺乏授权),则会懦弱地失败。
一个示例,其中存在用于阻止数据库独占锁的最后一个死亡持有者的带外机制,是在ceph-mgr
中。监视器会意识到用于cephVFS 的 RADOS 连接,并在ceph-mgr
故障转移期间将其列入黑名单。这防止了僵尸ceph-mgr
继续工作并可能损坏数据库。因此,没有必要让cephVFS 在ceph-mgr
的新实例中执行列入黑名单命令(但它仍然这样做,无害地)。
要手动将cephVFS 列入黑名单,您可以使用cephVFS 的实例地址:ceph_status
SQL 函数查看
SELECT ceph_status();
{"id":788461300,"addr":"172.21.10.4:0/1472139388"}
您可以使用JSON1 扩展:
SELECT json_extract(ceph_status(), '$.addr');
172.21.10.4:0/3563721180
轻松地操作这些信息。
ceph osd blocklist add 172.21.10.4:0/3082314560
性能统计
The cephVFS 提供了一个 SQLite 函数,ceph_perf
,用于查询 VFS 的性能统计信息。数据来自“性能计数器”,就像其他 Ceph 服务通常通过管理员套接字查询的那样。
SELECT ceph_perf();
{"libcephsqlite_vfs":{"op_open":{"avgcount":2,"sum":0.150001291,"avgtime":0.075000645},"op_delete":{"avgcount":0,"sum":0.000000000,"avgtime":0.000000000},"op_access":{"avgcount":1,"sum":0.003000026,"avgtime":0.003000026},"op_fullpathname":{"avgcount":1,"sum":0.064000551,"avgtime":0.064000551},"op_currenttime":{"avgcount":0,"sum":0.000000000,"avgtime":0.000000000},"opf_close":{"avgcount":1,"sum":0.000000000,"avgtime":0.000000000},"opf_read":{"avgcount":3,"sum":0.036000310,"avgtime":0.012000103},"opf_write":{"avgcount":0,"sum":0.000000000,"avgtime":0.000000000},"opf_truncate":{"avgcount":0,"sum":0.000000000,"avgtime":0.000000000},"opf_sync":{"avgcount":0,"sum":0.000000000,"avgtime":0.000000000},"opf_filesize":{"avgcount":2,"sum":0.000000000,"avgtime":0.000000000},"opf_lock":{"avgcount":1,"sum":0.158001360,"avgtime":0.158001360},"opf_unlock":{"avgcount":1,"sum":0.101000871,"avgtime":0.101000871},"opf_checkreservedlock":{"avgcount":1,"sum":0.002000017,"avgtime":0.002000017},"opf_filecontrol":{"avgcount":4,"sum":0.000000000,"avgtime":0.000000000},"opf_sectorsize":{"avgcount":0,"sum":0.000000000,"avgtime":0.000000000},"opf_devicecharacteristics":{"avgcount":4,"sum":0.000000000,"avgtime":0.000000000}},"libcephsqlite_striper":{"update_metadata":0,"update_allocated":0,"update_size":0,"update_version":0,"shrink":0,"shrink_bytes":0,"lock":1,"unlock":1}}
您可以使用JSON1 扩展:
SELECT json_extract(ceph_perf(), '$.libcephsqlite_vfs.opf_sync.avgcount');
776
这告诉您 SQLite 调用了多少次 VFS 的SQLite IO Methods的 xSync 方法(对于all进程中的打开数据库连接)。您可以在执行一定数量的查询前后分析性能统计信息,以查看所需的文件系统同步次数(这只会与事务数成正比)。或者,您可能对完成写入的平均延迟更感兴趣:
SELECT json_extract(ceph_perf(), '$.libcephsqlite_vfs.opf_write');
{"avgcount":7873,"sum":0.675005797,"avgtime":0.000085736}
这将告诉您有 7873 次写入,平均完成时间为 85 微秒。这清楚地表明调用是异步执行的。回到同步:
SELECT json_extract(ceph_perf(), '$.libcephsqlite_vfs.opf_sync');
{"avgcount":776,"sum":4.802041199,"avgtime":0.006188197}
平均执行同步调用花费了 6 毫秒。这汇集了所有异步写入以及异步更新条带文件大小的操作。
调试
可以通过以下方式打开 libcephsqlite 的调试:
debug_cephsqlite
如果运行sqlite3
命令行工具,请使用:
env CEPH_ARGS='--log_to_file true --log-file sqlite3.log --debug_cephsqlite 20 --debug_ms 1' sqlite3 ...
这将把所有通常的 Ceph 调试信息保存到文件sqlite3.log
以供检查。
由 Ceph 基金会带给您
Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.