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
安装Ruby和 Rails的详细步骤
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实现文件上传二法
2006/10/09 PHP
PHP 的异常处理、错误的抛出及回调函数等面向对象的错误处理方法
2012/12/07 PHP
PHP实现搜索相似图片
2015/09/22 PHP
WordPress中注册菜单与调用菜单的方法详解
2015/12/18 PHP
PHP 极验验证码实例讲解
2016/09/29 PHP
用js得到网页中所有的div的id
2020/10/19 Javascript
jQuery checkbox全选/取消全选实现代码
2009/11/14 Javascript
原始XMLHttpRequest方法详情回顾
2013/11/28 Javascript
js中把JSON字符串转换成JSON对象最好的方法
2014/03/21 Javascript
jQuery实现自动与手动切换的滚动新闻特效代码分享
2015/08/27 Javascript
JavaScript对象学习小结
2015/09/02 Javascript
第二章之Bootstrap 页面排版样式
2016/04/25 Javascript
基于Bootstrap和jQuery构建前端分页工具实例代码
2016/11/23 Javascript
Bootstrap基本模板的使用和理解1
2016/12/14 Javascript
tab栏切换原理
2017/03/22 Javascript
jquery ui sortable拖拽后保存位置
2017/04/27 jQuery
基于Datatables跳转到指定页的简单实例
2017/11/09 Javascript
nodejs express配置自签名https服务器的方法
2018/05/22 NodeJs
JavaScript设计模式之工厂模式简单实例教程
2018/07/03 Javascript
js纯前端实现腾讯cos文件上传功能的示例代码
2019/05/14 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
[46:55]完美世界DOTA2联赛决赛 FTD vs Phoenix 第三场 11.08
2020/11/11 DOTA
python模拟登录百度贴吧(百度贴吧登录)实例
2013/12/18 Python
Python实现获取网站PR及百度权重
2015/01/21 Python
python3使用scrapy生成csv文件代码示例
2017/12/28 Python
浅谈numpy数组中冒号和负号的含义
2018/04/18 Python
django_orm查询性能优化方法
2018/08/20 Python
django之跨表查询及添加记录的示例代码
2018/10/16 Python
详解python 爬取12306验证码
2019/05/10 Python
python networkx 根据图的权重画图实现
2019/07/10 Python
Django分组聚合查询实例分享
2020/04/29 Python
Python使用re模块验证危险字符
2020/05/21 Python
Spark处理数据排序问题如何避免OOM
2020/05/21 Python
天网面试题
2013/04/07 面试题
SpringBoot集成Redis,并自定义对象序列化操作
2021/06/22 Java/Android
python解析照片拍摄时间进行图片整理
2022/07/23 Python