一、入门代码
LMDB的全称是Lightning Memory-Mapped Database(快如闪电的内存映射数据库),它的文件结构简单,包含一个数据文件和一个锁文件:
LMDB文件可以同时由多个进程打开,具有极高的数据存取速度,访问简单,不需要运行单独的数据库管理进程,只要在访问数据的代码里引用LMDB库,访问时给文件路径即可。
让系统访问大量小文件的开销很大,而LMDB使用内存映射的方式访问文件,使得文件内寻址的开销非常小,使用指针运算就能实现。数据库单文件还能减少数据集复制/传输过程的开销。
在python中使用lmdb: linux中,可以使用指令‘pip install lmdb' 安装lmdb包。
1. 生成一个空的lmdb数据库文件
1 2 3 4 5 6 7 | # -*- coding: utf-8 -*- import lmdb
# 如果train文件夹下没有data.mbd或lock.mdb文件,则会生成一个空的,如果有,不会覆盖 # map_size定义最大储存容量,单位是kb,以下定义1TB容量 env = lmdb. open ( "./train" ,map_size = 1099511627776 ) env.close() |
2. LMDB数据的添加、修改、删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # -*- coding: utf-8 -*- import lmdb
# map_size定义最大储存容量,单位是kb,以下定义1TB容量 env = lmdb. open ( "./train" , map_size = 1099511627776 )
txn = env.begin(write = True )
# 添加数据和键值 txn.put(key = '1' , value = 'aaa' ) txn.put(key = '2' , value = 'bbb' ) txn.put(key = '3' , value = 'ccc' )
# 通过键值删除数据 txn.delete(key = '1' )
# 修改数据 txn.put(key = '3' , value = 'ddd' )
# 通过commit()函数提交更改 txn.commit() env.close() |
3. 查询lmdb数据库内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | # -*- coding: utf-8 -*- import lmdb
env = lmdb. open ( "./train" )
# 参数write设置为True才可以写入 txn = env.begin(write = True ) ############################################添加、修改、删除数据
# 添加数据和键值 txn.put(key = '1' , value = 'aaa' ) txn.put(key = '2' , value = 'bbb' ) txn.put(key = '3' , value = 'ccc' )
# 通过键值删除数据 txn.delete(key = '1' )
# 修改数据 txn.put(key = '3' , value = 'ddd' )
# 通过commit()函数提交更改 txn.commit() ############################################查询lmdb数据 txn = env.begin()
# get函数通过键值查询数据 print txn.get( str ( 2 ))
# 通过cursor()遍历所有数据和键值 for key, value in txn.cursor():
print (key, value)
############################################
env.close() |
4. 读取已有.mdb文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # -*- coding: utf-8 -*- import lmdb
env_db = lmdb.Environment( 'trainC' ) # env_db = lmdb.open("./trainC")
txn = env_db.begin()
# get函数通过键值查询数据,如果要查询的键值没有对应数据,则输出None print txn.get( str ( 200 ))
for key, value in txn.cursor(): #遍历
print (key, value)
env_db.close() |
二、进阶
LMDB 介绍
LMDB 全称为 Lightning Memory-Mapped Database,就是非常快的内存映射型数据库,LMDB使用内存映射文件,可以提供更好的输入/输出性能,对于用于神经网络的大型数据集( 比如ImageNet),可以将其存储在 LMDB 中。
因为最开始 Caffe 就是使用的这个数据库,所以网上的大多数关于 LMDB 的教程都通过 Caffe 实现的,对于不了解 Caffe 的同学很不友好,所以本篇文章只讲解 LMDB。
LMDB属于key-value数据库,而不是关系型数据库( 比如 MySQL ),LMDB提供 key-value 存储,其中每个键值对都是我们数据集中的一个样本。LMDB的主要作用是提供数据管理,可以将各种各样的原始数据转换为统一的key-value存储。
LMDB效率高的一个关键原因是它是基于内存映射的,这意味着它返回指向键和值的内存地址的指针,而不需要像大多数其他数据库那样复制内存中的任何内容。
LMDB不仅可以用来存放训练和测试用的数据集,还可以存放神经网络提取出的特征数据。如果数据的结构很简单,就是大量的矩阵和向量,而且数据之间没有什么关联,数据内没有复杂的对象结构,那么就可以选择LMDB这个简单的数据库来存放数据。
LMDB的文件结构很简单,一个文件夹,里面是一个数据文件和一个锁文件。数据随意复制,随意传输。它的访问简单,不需要单独的数据管理进程。只要在访问代码里引用LMDB库,访问时给文件路径即可。
用LMDB数据库来存放图像数据,而不是直接读取原始图像数据的原因:
- 数据类型多种多样,比如:二进制文件、文本文件、编码后的图像文件jpeg、png等,不可能用一套代码实现所有类型的输入数据读取,因此通过LMDB数据库,转换为统一数据格式可以简化数据读取层的实现。
- lmdb具有极高的存取速度,大大减少了系统访问大量小文件时的磁盘IO的时间开销。LMDB将整个数据集都放在一个文件里,避免了文件系统寻址的开销,你的存储介质有多快,就能访问多快,不会因为文件多而导致时间长。LMDB使用了内存映射的方式访问文件,这使得文件内寻址的开销大幅度降低。
LMDB 的基本函数
env = lmdb.open()
:创建 lmdb 环境txn = env.begin()
:建立事务txn.put(key, value)
:进行插入和修改txn.delete(key)
:进行删除txn.get(key)
:进行查询txn.cursor()
:进行遍历txn.commit()
:提交更改
创建一个 lmdb 环境:
# 安装:pip install lmdb
import lmdb
env = lmdb.open(lmdb_path, map_size=1099511627776)
lmdb_path
指定存放生成的lmdb数据库的文件夹路径,如果没有该文件夹则自动创建。
map_size
指定创建的新数据库所需磁盘空间的最小值,1099511627776B=1T。可以在这里进行存储单位换算。
会在指定路径下创建data.mdb
和lock.mdb
两个文件,一是个数据文件,一个是锁文件。
修改数据库内容:
txn = env.begin(write=True)
# insert/modify
txn.put(str(1).encode(), "Alice".encode())
txn.put(str(2).encode(), "Bob".encode())
# delete
txn.delete(str(1).encode())
txn.commit()
先创建一个事务(transaction) 对象txn
,所有的操作都必须经过这个事务对象。因为我们要对数据库进行写入操作,所以将write
参数置为True
,默认其为False
。
使用.put(key, value)
对数据库进行插入和修改操作,传入的参数为键值对。
值得注意的是,需要在键值字符串后加.encode()
改变其编码格式,将str
转换为bytes
格式,否则会报该错误:TypeError: Won't implicitly convert Unicode to bytes; use .encode()
。在后面使用.decode()
对其进行解码得到原数据。
使用.delete(key)
删除指定键值对。
对LMDB的读写操作在事务中执行,需要使用commit
方法提交待处理的事务。
查询数据库内容:
txn = env.begin()
print(txn.get(str(2).encode()))
for key, value in txn.cursor():
print(key, value)
env.close()
每次commit()
之后都要用env.begin()
更新 txn(得到最新的lmdb数据库)。
使用.get(key)
查询数据库中的单条记录。
使用.cursor()
遍历数据库中的所有记录,其返回一个可迭代对象,相当于关系数据库中的游标,每读取一次,游标下移一位。
也可以想文件一样使用with
语法:
with env.begin() as txn:
print(txn.get(str(2).encode()))
for key, value in txn.cursor():
print(key, value)
完整的demo如下:
import lmdb
import os, sys
def initialize():
env = lmdb.open("lmdb_dir")
return env
def insert(env, sid, name):
txn = env.begin(write=True)
txn.put(str(sid).encode(), name.encode())
txn.commit()
def delete(env, sid):
txn = env.begin(write=True)
txn.delete(str(sid).encode())
txn.commit()
def update(env, sid, name):
txn = env.begin(write=True)
txn.put(str(sid).encode(), name.encode())
txn.commit()
def search(env, sid):
txn = env.begin()
name = txn.get(str(sid).encode())
return name
def display(env):
txn = env.begin()
cur = txn.cursor()
for key, value in cur:
print(key, value)
env = initialize()
print("Insert 3 records.")
insert(env, 1, "Alice")
insert(env, 2, "Bob")
insert(env, 3, "Peter")
display(env)
print("Delete the record where sid = 1.")
delete(env, 1)
display(env)
print("Update the record where sid = 3.")
update(env