世界上使用最多的数据库是什么?并不是
Oracle
也不是MySQL
,PostgreSQL
,而是SQLite
。
这个小巧的嵌入式数据库引擎,手机,浏览器等无数的应用程序都内置了 SQLite 数据库。
SQLite 简介
SQLite是遵守ACID的关系数据库管理系统,它包含在一个相对小的C程序库中。与许多其它数据库管理系统不同,SQLite不是一个客户端/服务器结构的数据库引擎,而是被集成在用户程序中。 SQLite遵守ACID,实现了大多数SQL标准。它使用动态的、弱类型的SQL语法。
目前使用的版本主要都是SQLite3
。
SQLite 特点
嵌入式
不需要单独的线程,可以直接嵌入程序中。连部署都不用考虑了。
关系型数据库
- 遵守
ACID
,自带事务。 - 支持
SQL
,简单易用。
SQLite 使用场景
服务端程序的辅助存储
服务端程序里,用于保存节点的元数据信息。用于本地缓存等场景。
手机等终端应用程序的数据库
终端应用程序通常也需要保存些数据,类似钉钉,微信等。这肯定比存文件方便可靠多了。
工具类程序
最典型的就是YUM
了。类似的应用肯定也还有。
单元测试
通常,服务端程序开发都会使用MySQL
等关系型数据库,用于支持并发。但在写单元测试时,如果使用SQLMock
会非常麻烦。此时,可以使用SQLite
来替代SQLMock
实现单测。
SQLite 使用方法
这里使用Golang
来举例,SQLite支持各种语言,用法基本一样。
常规用法
Golang
中,需要传入两个参数,第一个是指定数据库类型,第二个则是数据库路径。
db, err := sql.Open("sqlite3", "./foo.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
内存模式
SQLite
的内存模式(Memory Mode)就是一种在内存中创建数据库的方式,即将数据库文件保存在内存中,而不是保存在磁盘上。在程序结束后,SQLite
的数据就会消失,非常适合用作单元测试,避免数据干扰。但在每次运行时都需要重新创建表格、插入数据等操作。
Golang
中,将SQLite
的路径配置成:memory:
就使用了内存模式。
db, err := sql.Open("sqlite3", ":memory:")
共享缓存的内存模式
如果是使用Golang
开发,有可能遇到一个情况。
有的SQL
报错,找不到表,但实际上表一开始已经初始化了。
OperationalError:no such table
这并不是因为SQLite
出现数据损坏,或者是MVCC
的问题,而是多线程造成的。Golang
属于多线程模式,链接数据库时可能会有多个会话。但按照上述的用法,每个会话都会有一个自己的SQLite
实例,当程序用了另一个会话时,访问的是另一个SQLite
实例,自然就会报错找不到表了。
如果使用文件的方式,则能正常运行。当然,SQLite
也有有共享缓存的内存模式。
db, err := sql.Open("sqlite3", "file::memory:?cache=shared")
这样,同一个进程中的不同会话,都会使用同一块内存。就能解决上述问题了。
注意
SQLite
和MySQL
在一些SQL的处理上存在一些不同。建议使用ORM
来屏蔽差异。
此外,SQLite
中,不同表如果用一个索引名字,是会冲突的,需要注意。
database is locked
处理
SQLite
可以多个会话读,但同时只能有一个写会话,否则会产生锁库的报错。所以,我们在代码实现时需要注意避免同时存在多个会话的写操作。
限制会话数
db.SetMaxOpenConns(1);
设置后,会话数会被限制成1
,但如果需要多个会话操作数据库时,程序会直接卡死。
比如某个事务中,有读取另一个表的请求,第二个数据库操作会因为拿不到会话而卡死。
配置参数
db, err := sql.Open("sqlite3", "file::memory:?cache=shared&_journal=WAL&_busy_timeout=10000")
启用WAL
启用SQLite3
的 WAL(Write-Ahead Logging)
模式可以显著降低锁库的概率。在WAL
模式下,读写操作不相互阻塞,可以实现高并发的读写操作。
设置忙碌超时时间
设置忙碌时间可以让SQLite
在发现锁定时,等待一会。这也可以降低报错的情况。
注意
上述两个参数也可以在会话中设置,但由于database/sql
通常会创建多个连接
,如下操作只会影响其中的一个链接。在其它链接访问时,依然会报错。
db.Exec("PRAGMA busy_timeout = 10000;")
暂无评论内容