RediSQL模块
在享受Redis简单、快捷的扁平式数据存储的同时,我们有时也会想要 使用传统的关系式数据库来存储复杂的、多层级的结构化数据,但这 样一来似乎就必须放弃Redis这种内存数据库带来的快速存取优势,为 了解决这种“鱼和熊掌不可兼得”的问题,有开发者创建了一个将关 系式数据库嵌入Redis服务器中的项目,它就是RediSQL( https://github.com/RedBeardLab/rediSQL )。RediSQL通过Redis的 模块机制嵌入了一个完整的SQLite实现,用户可以通过这个实现在 Redis服务器中使用SQLite的全部功能。
更令人兴奋的是,通过将数据库文件存放在Redis数据库中而不是硬盘 文件当中,RediSQL获得了内存级别的数据读写速度,这对于传统关系 式数据库受限于硬盘读写速度的缺陷来说,无疑是一次巨大的提升。 换句话说,通过使用RediSQL,用户将能够同时享受到关系式数据库的 强大功能以及内存数据库的快速存取优势。
编译模块
为了使用RediSQL,我们需要先使用以下命令,从RediSQL的项目页面下载其源码:
$ git clone http://github.com/RedBeardLab/rediSQL/
Cloning into 'rediSQL'...
remote: Counting objects: 1404, done.
remote: Total 1404 (delta 0), reused 0 (delta 0), pack-reused 1404
Receiving objects: 100% (1404/1404), 7.28 MiB | 487.00 KiB/s, done.
Resolving deltas: 100% (513/513), done.
Checking connectivity... done.
在获得源码之后,我们需要进入源码目录,并使用以下命令编译 RediSQL模块的源码:
$ cargo build --release
Updating crates.io index
Downloaded log v0.4.6
Downloaded fnv v1.0.6
...
Downloaded rustc-demangle v0.1.13
Downloaded autocfg v0.1.1
Compiling arrayvec v0.4.10
Compiling libc v0.2.46
...
Compiling redisql_lib v0.3.1 (/Users/huangz/rediSQL/redisql_lib)
Compiling rediSQL v0.7.1 (/Users/huangz/rediSQL)
Finished release [optimized] target(s) in 4m 41s
因为RediSQL使用Rust语言编写,编译它需要 用到Rust语言附带的包管理工具cargo,所以如果你的计算机上尚未安 装Rust语言编程环境,可以通过执行以下命令进行安装:
关于Rust语言的更多信息可以访问它们的官方网站 www.rustlang.org 来获得。 |
编译完成的RediSQL模块将存放在源码文件夹的/target/release文件 夹当中,我们可以通过访问该文件夹来验证这一点:
$ cd target/release/
$ ls
build deps examples incremental
文件夹中的libredis_sql.dylib就是编译完成的模块文件,我们只要 将其载入Redis服务器中,就可以正式开始使用RediSQL了:
redis> MODULE LOAD /Users/huangz/rediSQL/target/release/libredis_sql.dylib
OK
使用示例
要在关系式数据库中存储数据,需要先创建出相应的数据库。在 RediSQL中,这一步可以通过执行CREATE_DB命令来完成:
# 创建一个名为MYDB的数据库
redis> REDISQL.CREATE_DB MYDB
OK
在此之后,我们还需要在数据库中创建出相应的表用于存储数据,这 一步可以通过RediSQL的EXEC命令配合SQLite的CREATE TABLE命令来完 成,前者用于在RediSQL中执行SQL命令,而后者则用于在关系式数据 库中创建表格:
# 创建一个名为users的表,其中包含Name和Age两个字段
redis> REDISQL.EXEC MYDB "CREATE TABLE users (Name TEXT, Age INT);"
1) DONE
2) (integer) 0
之后,通过再次执行EXEC命令和SQLite的INSERT命令,可以将相应的 数据行插入表格当中:
# 向users表格插入行
redis> REDISQL.EXEC MYDB "INSERT INTO users VALUES('peter', 32);"
1) DONE
2) (integer) 1
redis> REDISQL.EXEC MYDB "INSERT INTO users VALUES('jack', 28);"
1) DONE
2) (integer) 1
redis> REDISQL.EXEC MYDB "INSERT INTO users VALUES('mary', 25);"
1) DONE
2) (integer) 1
然后,我们可以通过执行EXEC命令和SELECT命令来获取刚刚插入的数据行:
# 获取users表格中的所有数据行
redis> REDISQL.EXEC MYDB "SELECT * FROM users"
1) 1) "peter"
2) (integer) 32
2) 1) "jack"
2) (integer) 28
3) 1) "mary"
2) (integer) 25
因为RediSQL嵌入的是具有完整功能的SQLite实现,所以包括关联查询 在内的更为复杂的功能也是可用的:
# 创建books表并插入与users表相关联的数据
redis> REDISQL.EXEC MYDB "CREATE TABLE books (Reader TEXT, Title TEXT);"
1) DONE
2) (integer) 0
redis> REDISQL.EXEC MYDB "INSERT INTO books VALUES('peter', 'Redis in Action');"
1) DONE
2) (integer) 1
redis> REDISQL.EXEC MYDB "INSERT INTO books VALUES('peter', 'Redis Guide');"
1) DONE
2) (integer) 1
# 执行关联查询
redis> REDISQL.EXEC MYDB "SELECT Title FROM users, books WHERE users.Name = books.Reader;"
1) 1) "Redis Guide"
2) 1) "Redis in Action"
最后,每个RediSQL数据库都存储在单独的Redis数据库键中,比如上 面创建的MYDB数据库对应的就是MYDB键:
redis> TYPE MYDB
rediSQLDB
API简介
本节中我们将会看到RediSQL中最常见的一组API,并学习如何创建数 据库、删除数据库、执行语句、执行查询以及复制数据库等,至于项 目的完整API则可以通过访问以下页面查看: https://redisql.redbeardlab.com/rediSQL/references/ 。
-
创建数据库
CREATE_DB命令用于创建数据库,它接受一个数据库名字和一个可选的路径作为参数:
REDISQL.CREATE_DB name [path]
比如,通过执行以下命令可以创建一个名为BLOG的数据库:
REDISQL.CREATE_DB BLOG
而执行以下命令则会创建一个名为CMS的数据库:
REDISQL.CREATE_DB CMS
如果用户在执行CREATE_DB命令时没有给定path参数,那么RediSQL将 使用特殊字符串:memory:作为path参数的值,这意味着数据库中的 数据将被存储在Redis中(也就是内存中);如果用户想要把数据存储 到硬盘文件中,那么只需要在path参数中指定数据库文件的具体路径即可。
举个例子,如果用户想要把数据库存储在当前文件夹的weather.db文件中,那么可以执行以下命令:
redis> REDISQL.CREATE_DB WEATHER "weather.db" OK
CREATE_DB命令的复杂度为O(1)。
-
删除数据库
RediSQL没有直接提供用于删除数据库的命令,不过由于RediSQL创建 的每个数据库都对应一个Redis键,所以我们可以直接使用Redis内置 的DEL命令来移除RediSQL创建的数据库:
DEL db [db ...]
存储在内存中的RediSQL数据库在DEL命令执行之后将被移除,而存储 在文件中的RediSQL数据库文件则会被关闭。
举个例子,如果我们想要删除之前创建的WEATHER数据库,那么只需要 执行以下命令即可:
redis> DEL WEATHER (integer) 1
DEL命令的复杂度为O(N),其中N为被移除数据库包含的行数量。
-
执行语句
RediSQL的EXEC命令接受一个数据库和一个SQLite语句作为参数,然后 在数据库中执行指定的语句:
REDISQL.EXEC db "statement"
举个例子,通过执行以下命令,我们可以向MYDB数据库的users表插入 一个新的行:
redis> REDISQL.EXEC MYDB "INSERT INTO users VALUES('peter', 32);"
1) DONE
2) (integer) 1
与此类似,通过执行以下命令,我们可以从MYDB数据库的users表中获 取所有数据:
redis> REDISQL.EXEC MYDB "SELECT * FROM users"
1) 1) "peter"
2) (integer) 32
2) 1) "jack"
2) (integer) 28
3) 1) "mary"
2) (integer) 25
EXEC命令的复杂度由被执行的SQLite语句决定。
-
执行查询
如果用户想要执行的SQLite语句是不需要修改数据库的只读语句,那 么可以使用RediSQL的QUERY命令代替EXEC命令,以此来获得更为安全 的语句执行环境以及潜在的性能优化:
REDISQL.QUERY db "statement"
比如,我们可以使用以下语句去代替之前使用EXEC命令执行SELECT语 句的做法:
redis> REDISQL.QUERY MYDB "SELECT * FROM users"
1) 1) "peter"
2) (integer) 32
2) 1) "jack"
2) (integer) 28
3) 1) "mary"
2) (integer) 25
尝试使用QUERY命令去执行一条写入语句将引发错误:
redis> REDISQL.QUERY MYDB "INSERT INTO users VALUES('tom', 33);"
(error) ERR - Error Code: 0 => Statement is not read only but it may modify the database, use `E
XEC_STATEMENT` instead. | Not read only statement
与EXEC命令一样,QUERY命令的复杂度也由被执行的语句决定。
-
复制数据库
用户可以通过执行COPY命令,将源数据库的所有数据复制至目标数据库:
REDISQL.COPY source destination
比如,通过执行以下命令,我们可以将MYDB数据库中的所有数据都复制到NEWDB:
# 创建目标数据库
redis> REDISQL.CREATE_DB NEWDB
OK
# 执行复制操作
redis> REDISQL.COPY MYDB NEWDB
OK
# 从目标数据库中获取数据
redis> REDISQL.QUERY NEWDB "SELECT * FROM users"
1) 1) "peter"
2) (integer) 32
2) 1) "jack"
2) (integer) 28
3) 1) "mary"
2) (integer) 25
无论源数据库存储在文件中还是内存中,COPY命令都会顺利执行。除 此之外,如果用户给定的目标数据库非空,那么命令在执行复制操作 之前将先清空目标数据库,然后再执行具体的复制操作。
COPY命令的复杂度为O(N),其中N为源数据库包含的行数量。