Ruby GDBM操作简介及数据存储原理


Posted in Ruby onApril 19, 2022

DBM简介

dbm(database manager) 是使用本地文件来存储数据的数据库,基于Key -Value对数据进行存储、读取,且有些dbm的实现( berkeley db)还支持BTree索引。dbm效率相对较高,甚至在某些情况下比关系型数据库系统的速度还更高,因为几乎所有dbm都支持比BTree效率要高的hash索引方式。

有多种dbm实现:标准dbm、ndbm( new dbm)、gdbm(GNU DBM)、sdbm( small dbm)、Berkeley db等, gdbm是对ndbm的扩展,它支持缓存功能。

DBM数据存储原理

  • dbm的数据存取基于key-value的hash格式
  • dbm/ndbm中,key单独存放在一个文件中,key+value存放在另一个文件中。对于gdbm,则是key作为索引数据单独存放在db文件中的一个地方(索引区),key+value存放在db文件中的另一个地方(数据区)
  • 为了高效率查询,除了key作为索引单独存放外,还额外存放key+value在db文件中的偏移位置以及大小,使得可以直接seek()跳转到指定位置处读取指定大小的数据
  • 删除记录时,只是删除索引区的key,数据区的key+value不方便删除也没必要删除,数据区这段孤儿空间称为保留空间或碎片空间,可以作为空闲空间留待后续复用
  • gdbm在执行读取操作之后会将数据缓存下来,因此,第一次读取可能速度慢,但是第二次速度将非常快。keys、values等操作的的结果也都会被缓存下来
  • 因为碎片空间可被复用,所以dbm还会记录所有的碎片空间的位置以及大小,gdbm中以链表方式记录之
  • 因删除记录不会释放空间,所以db文件大小不会减小。换句话说,dbm的文件会随着时间的推移不断增大,除非重组dbm,重组时,将根据索引区存在的key找到数据区所有对应的key+value数据,并将它们写入临时文件,最后重命名覆盖原db文件
  • 插入数据时,如果没有碎片空间,默认将插入在尾部,如果中间有碎片空间,则判断待写入数据的大小是否能够插入在碎片空间中
  • 更新数据时,如果更新后的数据变大,且该数据后面没有碎片空间,则直接原地移除并在文件尾部插入更新后的数据,如果有足够的空间存放更新后的数据,则原地更新
  • dbm只能存储字符串,数值、布尔、对象等都不能直接存储

Ruby使用gdbm

Ruby中要使用gdbm,它依赖于gdbm扩展库和头文件,所以需先安装:

# sudo yum install gdbm-devel
# Windows:
#   ridk exec uname -a确定32位还是64位,
#   然后ridk exec pacman -S mingw-w64-<$arch>-gdbm
sudo apt install libgdbm-dev
gem install gdbm

使用类方法GDBM.new()或者GDBM.open()可打开gdbm来操作db文件。

require 'gdbm'

gdbm = GDBM.new("/tmp/lang.db")
gdbm["perl"] = "Perl"
gdbm["shell"] = "Shell"
gdbm["php"] = "PHP"
gdbm.close

查看其文件内容:

$ ls -l /tmp/lang.db
-rw-rw-rw- 1 longshuai longshuai 8192 May 17 21:22 /tmp/lang.db

$ cat /tmp/lang.db
P |...x...l9php...rdshe...}N;iperl...perlPerlshellShellphpPHP

其中…表示的是乱码部分。

注意其大小为8K,且数据区默认在db文件的尾部,包含了key和value。

从db中检索数据:

gdbm = GDBM.open("/tmp/lang.db")
pp gdbm["perl"]
pp gdbm["php"]
gdbm.close

new()、open()

new()或open():open()可给定语句块,语句块退出时自动关闭IO流,未给定语句块时,open()等价于new()。

new(filename, mode = 0666, flags = nil)
open(filename, mode = 0666, flags = nil)
open(filename, mode = 0666, flags = nil) { |gdbm| ... }

当指定要操作的db文件不存在时,会创建文件,可指定创建文件时的权限。此外,flag参数接受如下值:

### 注意:writer方式可读可写
READER  - 以只读方式打开,即返回一个reader
WRITER  - 以可读写方式打开,即返回一个writer
WRCREAT - writer,如果数据库文件不存在,则创建
NEWDB   - writer,总是截断覆盖已存在的数据库文件

# 上面的三个writer可使用位或(|)的方式结合下面的选项:
SYNC   - 以sync模式写入数据库文件
NOLOCK - 打开时不锁定数据库文件

在未给定任何选项时,即默认情况下,总是先尝试以WRCREAT的方式打开,即以writer打开且文件不存在时创建。但如果打开失败(比如另一个进程已经打开且还未关闭),则尝试使用reader方式打开。

reader和reader之间互相兼容,writer和writer之间以及writer和reader之间互斥。所以,在某一时刻,允许同时有多个reader,但只能有一个writer

当打开gdbm实例后,它可以按照操作hash结构的方式去操作db,此外,gdbm已经mix-in Enumerable模块,所以可以直接使用该模块中的一些方法,比如find、grep、map等。

gdbm方法

######### 查询、插入、更新 #########
["key"]
fetch(key [, default]) → value
检索指定的key。
使用`[]`检索时,如果key不存在将返回nil,
使用fetch检索时,如果key不存在则报错,或者返回指定的默认值

values_at(key, ...) → array
检索一个或多个key,并以数组方式返回对应的value

["key"]= value
store(key, value) → value
更新指定的key,如果key不存在则插入

########## 遍历 #########
each_pair { |key, value| block } → gdbm
each_key { |key| block } → gdbm
each_value { |value| block } → gdbm
分别根据key-value、key、value遍历db

######### 其它检索、筛选方式 #########
key(value) → key
根据value找到其key,如果有多个相同的value,返回第一个

keys → array
以数组方式返回db中所有的key

values → array
以数组方式返回所有value

select { |key, value| block } → array
筛选所有满足条件的key-value

######### 判断key或value是否存在 #########
has_key?(k) → true or false
include?(k) → true or false
key?(k) → true or false
member?(k) → true or false
判断key是否存在

has_value?(v) → true or false
value?(v) → true or false
判断指定的value是否存在

######### 删除 #########
delete(key) → value or nil
根据key移除key-value并返回被移除的Key-value,db若空,返回nil

shift → (key, value) or nil
移除指定的key-value,并以数组方式返回之,db若空,则返回nil

delete_if { |key, value| block } → gdbm
移除满足条件(语句块返回true)的key-value,直接修改gdbm

reject { |key, value| block } → hash
等价于delete_if,但不修改gdbm,而是以hash的方式返回

reject! { |key, value| block } → gdbm
等价于delete_if,直接修改gdbm

clear → gdbm
清空db中所有key-value

######## 大小判断 #########
empty? → true or false
db是否为空

length → fixnum
size → fixnum
等价,返回db中的key-value数量

####### 其它操作 #########
invert → hash
反转gdbm中key-value:key作为value,value作为key,并以hash的方式返回

close → nil
关闭已打开的db文件

closed? → true or false
判断db文件是否已关闭

replace(other) → gdbm
将另一个gdbm(即other)的内容覆盖替换到当前的gdbm

update(other) → gdbm
用另一个gdbm(即Other)合并到当前gdbm,若key冲突,则当前gdbm的key被覆盖

reorganize → gdbm
重组gdbm

cachesize = size → size
设置gdbm内部的hash桶缓存大小

######## gdbm模式 #########
sync → gdbm
将IO buffer中的数据刷入磁盘中的db文件,全部写入成功才返回
如果以SYNC标记打开,则无需sync()

fastmode = boolean → boolean
syncmode = boolean → boolean
打开或关闭sync模式。
sync模式下,写入操作需要写入磁盘db文件成功(或失败)后才返回,
非sync模式下,只需写入io buffer即可返回。
syncmode方法在gdbm >= 1.8才可用,在此版本之前,使用方法fastmode=

######### 转换 #########
to_a → array
to_hash → hash
转换为数组、转换为hash
Ruby 相关文章推荐
Ruby处理CSV数据方法详解
Apr 18 Ruby
Ruby处理YAML和json数据
Apr 18 Ruby
Ruby序列化和持久化存储 Marshal和Pstore介绍
Apr 18 Ruby
Ruby使用Mysql2连接操作MySQL
Apr 19 Ruby
Ruby GDBM操作简介及数据存储原理
Apr 19 Ruby
Python如何将list中的string转换为int
Jul 15 Ruby
Ruby处理CSV数据方法详解
Apr 18 #Ruby
Ruby处理YAML和json数据
Apr 18 #Ruby
Ruby序列化和持久化存储 Marshal和Pstore介绍
Apr 18 #Ruby
Ruby使用Mysql2连接操作MySQL
Apr 19 #Ruby
安装Ruby和 Rails的详细步骤
Python如何将list中的string转换为int
Jul 15 #Ruby
You might like
php mysql数据库操作分页类
2008/06/04 PHP
解析php中反射的应用
2013/06/18 PHP
php中array_unshift()修改数组key注意事项分析
2016/05/16 PHP
PHP脚本自动识别验证码查询汽车违章
2016/12/20 PHP
原生php实现excel文件读写的方法分析
2018/04/25 PHP
PHP lcfirst()函数定义与用法
2019/03/08 PHP
jQuery Ajax 全解析
2009/02/08 Javascript
caller和callee的区别介绍及演示结果
2013/03/10 Javascript
javascript创建createXmlHttpRequest对象示例代码
2014/02/10 Javascript
推荐25个超炫的jQuery网格插件
2014/11/28 Javascript
JavaScript获取两个数组交集的方法
2015/06/09 Javascript
JavaScript获得url查询参数的方法
2015/07/02 Javascript
jQuery获取父元素节点、子元素节点及兄弟元素节点的方法
2016/04/14 Javascript
关于JS中的方法是否加括号的问题
2016/07/27 Javascript
JavaScript对象创建模式实例汇总
2016/10/03 Javascript
jQuery动态产生select option下拉列表
2017/03/15 Javascript
JS使用cookie实现只出现一次的广告代码效果
2017/04/22 Javascript
Angular directive递归实现目录树结构代码实例
2017/05/05 Javascript
Vue的百度地图插件尝试使用
2017/09/06 Javascript
jQuery实现页码跳转式动态数据分页
2017/12/31 jQuery
nodejs遍历文件夹下并操作HTML/CSS/JS/PNG/JPG的方法
2018/11/01 NodeJs
微信小程序视图控件与bindtap之间的问题的解决
2019/04/08 Javascript
[01:34]传奇从这开始 2016国际邀请赛中国区预选赛震撼开启
2016/06/26 DOTA
[46:20]DOTA2-DPC中国联赛 正赛 PSG.LGD vs LBZS BO3 第二场 1月22日
2021/03/11 DOTA
实例分析python3实现并发访问水平切分表
2018/09/29 Python
Python设计模式之解释器模式原理与用法实例分析
2019/01/10 Python
python实现复制大量文件功能
2019/08/31 Python
解决pycharm安装第三方库失败的问题
2020/05/09 Python
详解向scrapy中的spider传递参数的几种方法(2种)
2020/09/28 Python
selenium3.0+python之环境搭建的方法步骤
2021/02/01 Python
HTML5 语音搜索(淘宝店语音搜素)
2013/01/03 HTML / CSS
HTML5制作表格样式
2016/11/15 HTML / CSS
澳大利亚游乐场设备品牌:Lifespan Kids
2019/05/24 全球购物
模具专业毕业推荐信
2014/03/08 职场文书
2014年青年志愿者工作总结
2014/12/09 职场文书
MySQL悲观锁与乐观锁的实现方案
2021/11/02 MySQL