Perl的DBI(Database Independent Interface)模块是Perl访问数据库的标准接口,而SQLite是一个无需服务进程的嵌入式数据库,两者结合非常适合编写轻量级脚本。整套方案不需要安装MySQL那样的数据库服务,只要一个文件就能存数据,备份也只需复制文件,特别适合日志分析、配置存储、小工具开发等场景。本文将从环境准备开始,完整演示用DBI操作SQLite的全过程。

环境准备与数据库连接
首先确认系统是否安装了DBI和DBD::SQLite这两个模块。DBI是统一的数据库接口层,DBD::SQLite是针对SQLite的驱动,真正干活的其实是后者,它内部已经包含了SQLite引擎本身,连SQLite的可执行文件都不需要单独安装。检查和安装命令如下:
# 检查模块是否已安装 perl -MDBI -e 'print "DBI OK\n";' perl -MDBD::SQLite -e 'print "DBD::SQLite OK\n";' # 未安装时通过cpan安装 cpan DBI cpan DBD::SQLite
连接SQLite数据库非常简单,调用DBI->connect方法即可。如果指定的数据库文件不存在,SQLite会自动创建一个新的空库文件,这一点和MySQL完全不同,使用时要注意别因为路径写错而意外建出一个新库。建议在DSN中开启RaiseError和AutoCommit选项,前者让错误直接抛出异常,后者控制自动提交行为,后面讲事务时会用到。
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
# 数据库文件路径,不存在会自动创建
my $db_file = 'test.db';
my $dbh = DBI->connect(
"dbi:SQLite:dbname=$db_file",
'', '', # SQLite不需要用户名密码
{
RaiseError => 1, # 出错直接抛异常
AutoCommit => 1, # 自动提交事务
}
) or die "连接失败: " . $DBI::errstr;
print "数据库连接成功\n";连接成功后得到的$dbh就是数据库句柄,后续所有操作都围绕它展开。程序结束前记得调用$dbh->disconnect()断开连接,虽然脚本退出时系统会自动回收,但养成显式关闭的习惯可以避免数据写入延迟的问题。
建表与增删改查基础操作
执行SQL语句主要用两个方法:do和prepare加execute的组合。do适合不需要传入参数、只执行一次的语句,比如建表、删除表;而prepare方式可以预编译SQL,反复执行时性能更好,也是使用占位符的标准姿势。先来看建表和插入数据:
# 建表(do方法适合一次性DDL语句)
$dbh->do(q{
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER DEFAULT 0
)
});
# 使用占位符插入数据,防止SQL注入
my $sth = $dbh->prepare("INSERT INTO users (name, age) VALUES (?, ?)");
$sth->execute('张三', 28);
$sth->execute('李四', 35);
$sth->execute('王五', 22);
# 插入单条数据也可以直接用do
$dbh->do("INSERT INTO users (name, age) VALUES (?, ?)", undef, '赵六', 40);
print "插入的记录ID: " . $dbh->last_insert_id('', '', 'users', '') . "\n";这里的问号就是占位符,数据通过execute的参数传入,驱动会自动处理转义,从根本上杜绝SQL注入风险。千万别图省事把变量直接拼进SQL字符串,这在处理用户输入时是严重的安全隐患。查询数据则用fetchrow_hashref或fetchrow_array来取结果:
# 查询所有记录
my $query = $dbh->prepare("SELECT id, name, age FROM users WHERE age > ?");
$query->execute(25);
while (my $row = $query->fetchrow_hashref()) {
printf "ID:%d 姓名:%s 年龄:%d\n",
$row->{id}, $row->{name}, $row->{age};
}
$query->finish();
# 更新和删除
$dbh->do("UPDATE users SET age = ? WHERE name = ?", undef, 29, '张三');
my $del = $dbh->prepare("DELETE FROM users WHERE id = ?");
$del->execute(4);
print "删除了 " . $del->rows . " 条记录\n";fetchrow_hashref返回字段名做键的哈希引用,代码可读性好;fetchrow_array返回列表,速度稍快但依赖字段顺序。取完结果后调用finish释放语句句柄资源。rows方法对UPDATE和DELETE返回受影响的行数,但对SELECT语句在不同数据库上表现不一致,不建议依赖它统计查询条数。
事务控制与错误处理
当需要批量写入数据时,逐条自动提交效率很低,因为每次提交都会触发一次磁盘写入。正确做法是把AutoCommit关掉,或者用显式事务把多条语句包起来,要么全部成功,要么全部回滚。DBI提供了begin_work方法,它会临时关闭自动提交,调用commit或rollback后自动恢复:
# 批量插入使用事务,性能提升明显
my $sth = $dbh->prepare("INSERT INTO users (name, age) VALUES (?, ?)");
eval {
$dbh->begin_work; # 开启事务
for my $i (1 .. 1000) {
$sth->execute("用户$i", 20 + $i % 30);
}
$dbh->commit; # 全部成功后提交
};
if ($@) {
warn "事务失败,已回滚: $@";
eval { $dbh->rollback }; # 出错时回滚
}上千条数据放在一个事务里,写入速度能比逐条提交快几十倍,这是SQLite使用中最重要的性能技巧之一。错误处理方面,开启RaiseError后配合eval块就能捕获SQL异常,异常信息保存在$@变量中。也可以关闭RaiseError改用$DBI::err和$DBI::errstr手动判断错误码和错误信息,适合需要精细控制错误分支的场景。
另外提两个实用细节:一是SQLite对并发写入支持有限,同一时刻只允许一个写操作,多进程同时写会碰到database is locked错误,可以在连接属性里设置sqlite_busy_timeout让写操作自动等待;二是处理大量数据时善用bind_columns绑定变量,能减少内存分配开销,处理百万行级别数据时效果明显。掌握这些要点后,用Perl配合SQLite写数据处理脚本就基本没有障碍了。