内存表空间的真相和谎言
作者:Emanuel Calvo Franco
RAM 中的表空间(Linux)
感谢 Jose Vazquez 的合作。
此文章将随时变更
现在,“内存数据库”这个术语的使用频率很高。虽然存在采用这种工作方式的 Postgres 版本(FastDb 就是其中之一),但是使用一台小型服务器进行测试可能很有趣。
在本文中,我们将展示如何以基本且实用的方式进行操作以及一些结果。需要注意的是,这些测试都是在虚拟机上进行的,因此在其他专门的环境中可能会差异很大。
在一般情况下,这种设置(依我的经验)都会出现在 OLAP 服务器中,因为它们需要强大的处理能力。此外,它们通常是与数据集群分开的服务器,因此它们被用作复制的从属服务器,其数据在突然关机情况下可以轻松恢复。
在某种程度上,此技术允许使用表空间、物化视图等对象来占用该空间。
首先,为了简化操作,我们必须向内核发送一个参数来指定内存中磁盘的大小。在本例中,我们将其设置为 4 GB,但是为了让操作更加轻松,我们希望将其设置为更小的值(2 或 1 GB)
kernel /vmlinuz-2.6.28-13-server root=UUID=5ac65858-cdf5-4c26-8557-220563f52901 ro quiet splash ramdisk_size=4194304
如果希望永久保留此设置,我们可以触及 sysctl.conf
kernel.ramdisk_size=4194304
在操作系统启动时,从 shell 执行
mke2fs /dev/ram0 mkdir /mnt/ram0 mount /dev/ram0 /mnt/ram0 mkdir /mnt/ram0/pgspace chown postgres:postgres /mnt/ram0/pgspace
使用上述命令,我们在 RAM 中格式化了一个空间,并将它挂载到分区表中的一个目录中。需要注意的是,这里我们使用的是 ext2 格式化工具。根据情况,可以使用其他分区。
现在,我们需要操作数据库
ubuntu=# CREATE TABLESPACE ram_space LOCATION '/mnt/ram0/pgspace'; CREATE TABLESPACE ubuntu=# create table test_ram (i integer, name text) tablespace ram_space; CREATE TABLE ubuntu=# create temp table test_ram_temp (i integer, name text) tablespace ram_space; CREATE TABLE ubuntu=# create temp table test_disk_temp (i integer, name text); CREATE TABLE ubuntu=# create table test_disk (i integer, name text); CREATE TABLE
我们在该目录中创建了一个表空间,然后在两个可组合的位置中创建了两个类型的表:临时-标准备在内存-磁盘中。
我们来看看部分结果
ubuntu=# explain analyze insert into test_ram values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.019..9354.014 rows=1000000 loops=1) Total runtime: 22836.532 ms (2 rows) ubuntu=# explain analyze insert into test_ram_temp values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.025..7507.349 rows=1000000 loops=1) Total runtime: 12773.371 ms (2 rows) ubuntu=# explain analyze insert into test_disk values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.025..7948.205 rows=1000000 loops=1) Total runtime: 16902.042 ms (2 rows) ubuntu=# explain analyze insert into test_disk_temp values (generate_series(1,1000000),random()::text); QUERY PLAN --------------------------------------------------------------------------------------------- Result (cost=0.00..0.02 rows=1 width=0) (actual time=0.018..8135.287 rows=1000000 loops=1) Total runtime: 13716.049 ms (2 rows)
我们所做的是,插入一个与生成随机数相加的序列,并将其转换为文本。在此类小型测试中,我们插入了 1,000,000 条记录(数量不可小视!)。
我们来看一下摘要
RAM 中的标准表:22836.532 磁盘中的标准表:16902.042
RAM 中的临时表:12773.371 磁盘中的临时表:13716.049
在你的机器上,结果可能会不同。在本案例中,我使用了一个装有 PGLive(Pgsql 8.3.5)的虚拟机 (VBox) 运行的。
构想
乍一看,我们发现在处理表类型时存在差异(不管它位于何处)。在进行选择时,这可能是一个决定性因素。
也许可以将此类操作与内存中的物化视图以及临时类型结合起来。
需要考虑的另一件事是,记住当启动操作系统时,它会删除表空间的内容,因此此类技术仅在使用临时表时才有效。即使我们可以从这些表更新到磁盘中的表,但这种工作系统也不一致,因此始终建议先刻录到磁盘,然后更新到内存中。
构想 2
但是,http://www.linux.com/feature/142658 中提出了一种办法,那就是使用 SSD 来存储索引。
这更加有条理,并且并不是什么新鲜事,因为人们往往倾向于使用更快的媒体来存储索引。此外,就本文而言,如果供电突然中断或服务器当机,我们唯一需要做的事情就是重新建立索引。
就引用的本文而言,此文建议使用 XFS 来存储索引。我认为应该使用我们操作系统支持的最轻量级文件系统,因为我们寻求的是速度,而不是安全性。
我们可以这样创建内存(或在钢笔或 SSD 中创建)中的索引:
create index indice_ on tabla_ ( i ) tablespace ram_space;
构想 3
其他构想包括使用钢笔驱动器来存储 WAL。这可以通过一个相对简单的操作来实现,即创建一个名为 pg_xlog 的符号链接,并指向相关设备(我们可以在 PGDATA 中找到此目录)。将原装 pg_xlog 的内容复制到钢笔驱动器中(或移动过去),创建该链接,然后就完成了。
此构想与内存中的表空间非常相似,但与 WAL 一起执行的巨大差异在于这样做很危险。WAL 中的损坏可能会产生各种问题,因此服务器当机将使得我们无法使用 WAL!(除非我们拥有一个同步备份)。
为了解决钢笔驱动器可能造成的潜在不稳定性(可能是因为钢笔驱动器较为脆弱,或者可能因任何原因导致 WAL 丢失),我们需要将钢笔中的内容同步到另一块磁盘中。这样,Postgres 服务器就可以写入到钢笔中,但操作系统会对内容执行备份操作。
对于 WAL,我们需要至少一个带日志的文件系统(例如 ext3)。
第 4 个观点
我们正在与 AOSUG 社区(开放 Solaris - www.aosug.com.ar)合作,研究如何使用 ZFS 来管理内存存储设备。有了这种方式,将增加许多非常有用的特性。