SQLite作为嵌入式数据库,虽然轻量,但一旦承载了核心业务数据,它的性能状况就必须被纳入监控体系。Datadog作为一款流行的监控平台,其Agent本身并没有内置SQLite的集成检查,这并不意味着我们无法监控SQLite,恰恰相反,Datadog Agent提供了完善的自定义检查(Custom Check)机制,只要写一点Python代码,就能把SQLite的关键运行指标采集并上报到Datadog。本文将以一个完整的实战项目为例,演示从零搭建这套监控体系的全过程。

一、为什么SQLite需要自定义检查而不是官方集成
打开Datadog的官方集成列表,你会发现MySQL、PostgreSQL、Redis都有现成的集成方案,唯独找不到SQLite。原因其实很好理解:SQLite是嵌入式数据库,没有独立的服务进程,也没有监听端口,Agent无法像连接其他数据库那样去连接它。SQLite的所有状态都体现在数据库文件本身和应用程序进程内部。
但这并不代表SQLite没有值得监控的指标。通过PRAGMA命令和文件系统信息,我们可以获取到非常有价值的数据,比如page cache的命中率直接反映查询是否走内存缓存、数据库文件大小的增长趋势可以预判磁盘空间压力、PRAGMA integrity_check的结果能及时发现数据损坏。这些指标一旦异常,往往意味着应用层即将出现问题。
自定义检查正是为这种场景设计的。Datadog Agent内置了Python解释器,我们只需要编写一个继承自AgentCheck类的Python类,配合一份YAML配置文件,Agent就会定期执行我们的采集逻辑。整个开发流程不依赖任何外部Python环境,部署也非常简单。
二、环境准备与项目结构
本次实战假设目标机器上已经安装了Datadog Agent 7.x版本,操作系统以Linux为例(Windows的差异会在后文说明)。首先确认Agent的自定义检查目录存在,Linux下通常是/etc/datadog-agent/conf.d/(配置文件目录)和/etc/datadog-agent/checks.d/(检查脚本目录)。
项目结构很简单,只需要两个文件:
/etc/datadog-agent/conf.d/sqlite_check.d/conf.yaml /etc/datadog-agent/checks.d/sqlite_check.py
注意目录名的规律:配置文件放在conf.d下以检查名命名的子目录中,文件名固定为conf.yaml;而Python脚本的文件名必须与检查名完全一致,即sqlite_check.py对应检查名sqlite_check。命名不一致是新手最常见的错误,Agent会静默忽略不匹配的脚本,让你误以为配置没生效。
配置文件里我们定义要监控的数据库实例列表,这样可以同时监控机器上的多个数据库文件:
init_config: {}
instances:
- db_path: /var/lib/myapp/data.db
tags:
- env:production
- app:myapp
- db_path: /var/lib/myapp/cache.db
tags:
- env:production
- app:myapp每个instance对应一个数据库文件,tags字段让我们在Datadog平台上能按应用、按环境筛选指标,这在多服务共用一台主机时尤其重要。
三、编写自定义检查脚本采集SQLite指标
接下来是核心部分,编写Python采集脚本。脚本的基本思路是:对每个instance指定的数据库文件,打开一个只读连接,执行一系列PRAGMA命令获取指标,然后通过self.gauge()方法上报。完整代码如下:
from datadog_checks.base import AgentCheck
import os
import sqlite3
__version__ = "1.0.0"
class SqliteCheck(AgentCheck):
__check_name__ = 'sqlite_check'
def check(self, instance):
db_path = instance.get('db_path')
if not db_path or not os.path.exists(db_path):
self.service_check(
'sqlite.can_connect',
AgentCheck.CRITICAL,
message='数据库文件不存在: %s' % db_path
)
return
tags = instance.get('tags', [])
try:
# 以只读模式打开,避免影响业务写入
conn = sqlite3.connect('file:%s?mode=ro' % db_path, uri=True)
cursor = conn.cursor()
# 上报连接状态
self.service_check('sqlite.can_connect', AgentCheck.OK, tags=tags)
# 数据库文件大小(字节)
size = os.path.getsize(db_path)
self.gauge('sqlite.db_size', size, tags=tags)
# 页大小与页数,可推算总数据量
page_size = cursor.execute('PRAGMA page_size').fetchone()[0]
page_count = cursor.execute('PRAGMA page_count').fetchone()[0]
self.gauge('sqlite.page_size', page_size, tags=tags)
self.gauge('sqlite.page_count', page_count, tags=tags)
# 缓存命中情况,注意只读连接需要单独初始化统计
cursor.execute('PRAGMA cache_size')
cache_size = cursor.fetchone()[0]
self.gauge('sqlite.cache_size', cache_size, tags=tags)
# 空闲页数量,反映碎片程度
freelist = cursor.execute('PRAGMA freelist_count').fetchone()[0]
self.gauge('sqlite.freelist_count', freelist, tags=tags)
# journal模式
journal = cursor.execute('PRAGMA journal_mode').fetchone()[0]
self.gauge('sqlite.journal_mode_ok', 1 if journal != 'delete' else 0, tags=tags)
conn.close()
except sqlite3.Error as e:
self.service_check(
'sqlite.can_connect',
AgentCheck.CRITICAL,
message='SQLite访问失败: %s' % str(e),
tags=tags
)代码中有几个值得展开说明的点。第一,打开连接时使用了URI模式mode=ro,确保监控脚本以只读方式访问数据库,绝不会干扰业务写入,也不会在监控过程中意外创建WAL文件。第二,freelist_count这个指标容易被忽视,但非常实用:如果它持续增长,说明数据库里有大量被删除数据留下的空页,此时执行VACUUM可以回收空间。第三,service_check用来上报健康状态而不是数值,当数据库文件丢失或无法访问时会触发CRITICAL事件,配合Datadog的告警规则可以第一时间收到通知。
关于page cache命中率,需要特别说明:SQLite的PRAGMA cache_stats统计是按连接隔离的,Agent每次检查时新建的连接拿到的是全新缓存,命中率永远是初始值,没有参考意义。如果想监控真实命中率,正确做法是在应用层定期读取sqlite3_status(SQLITE_STATUS_PAGECACHE_HIT)并写入一个状态文件或独立的统计数据库,再让Agent脚本去读这个文件。这是一个典型的误区,很多初次尝试的人在这里浪费时间。
四、部署验证与监控面板搭建
脚本和配置放好后,先别急着重启Agent,用Agent自带的诊断命令验证检查是否能正常加载:
sudo -u dd-agent datadog-agent check sqlite_check
这条命令会立即执行一次检查并输出采集到的所有指标,任何Python语法错误、导入错误都会在这里暴露。确认无误后再重启Agent:
sudo systemctl restart datadog-agent
指标默认每15秒采集一次,如果觉得频率过高(对IO敏感的场景),可以在配置文件中通过min_collection_interval调整:
init_config: {}
instances:
- db_path: /var/lib/myapp/data.db
min_collection_interval: 60
tags:
- env:production指标上报后,在Datadog的Metrics Explorer中搜索sqlite.db_size等指标名就能看到数据了。接下来建议搭建一个Dashboard,核心部件包括:sqlite.db_size的时间序列图用于观察增长趋势、sqlite.freelist_count用于判断是否需要VACUUM、sqlite.can_connect的状态面板用于展示可用性。再配一条Monitor规则,比如当sqlite.can_connect连续三个周期为CRITICAL时触发告警,基本的监控闭环就完成了。
Windows环境下的部署逻辑完全相同,只是路径不同:配置目录变为C:\ProgramData\Datadog\conf.d\sqlite_check.d\conf.yaml,脚本目录为C:\ProgramData\Datadog\checks.d\sqlite_check.py。配置文件里的db_path也要写成Windows格式,例如C:\App\data\mydb.db,注意YAML中反斜杠可能被解释为转义符,建议用单引号包裹路径字符串避免解析问题。
五、生产环境中的注意事项
最后总结几个实战中容易踩的坑。首先是锁竞争问题:即使在只读模式下,SQLite在查询过程中也需要获取共享锁,如果业务正好在执行大事务写入,监控脚本的查询可能会返回SQLITE_BUSY。上面的代码通过service_check捕获了这类异常,但更稳妥的做法是在连接后设置PRAGMA busy_timeout,给查询一个短暂等待的机会,而不是立即上报CRITICAL造成误报。
其次是采集频率与性能的平衡。SQLite数据库文件如果达到几个GB,每次os.path.getsize开销极小可以忽略,但一旦你往脚本里加了PRAGMA integrity_check这种全库扫描操作,大库上可能耗时数分钟,务必把这类重操作的采集间隔拉长到小时级别,或者干脆放到独立的定时任务中。
最后是指标命名规范。自定义指标在Datadog中按数量计费,如果把数据库名、日期这类高基数值塞进tags,指标基数会爆炸式增长。建议tags只保留环境、应用名、主机这几个低基数维度,路径等明细信息放到事件消息里而不是tags里。遵循这个原则,整个SQLite监控方案既能覆盖关键性能数据,又不会给账单带来意外惊喜。至此,一个从采集、上报到可视化告警的完整SQLite监控项目就搭建完成了。
SQLite监控Datadog Agent数据库性能修改时间:2026-09-11 18:40:47