python(八):utilizando o banco de dados lmdb em python

O LMDB (Lightning Memory-Mapped Database) é um banco de dados de alta performance baseado em mapeamento de memória, projetado para operações rápidas e eficientes com grandes volumes de dados. Ele armazena todos os dados em um único arquivo, reduzindo significativamente a sobrecarga de acesso a múltiplos arquivos pequenos, especialmente em sistemas com disco rígido mecânico ou NFS. Em Python, o uso do LMDB é simplificado com a biblioteca lmdb. Instale-a com: ``` pip install lmdb


### 1. Criação de um ambiente LMDB vazio

import lmdb

Define o caminho do diretório onde o banco será criado

env = lmdb.open("./train", map_size=1099511627776) # 1 TB de espaço env.close()


 Se o diretório não existir, será criado automaticamente. O parâmetro `map_size` define o tamanho máximo do arquivo de dados (em bytes). Caso o banco já exista, ele não será sobrescrito. ### 2. Operações de escrita: inserir, atualizar e remover dados

import lmdb

env = lmdb.open("./train", map_size=1099511627776)

with env.begin(write=True) as txn: # Inserir ou atualizar chaves txn.put(b'1', b'aaa') txn.put(b'2', b'bbb') txn.put(b'3', b'ccc')

# Remover uma chave
txn.delete(b'1')

# Atualizar valor
txn.put(b'3', b'ddd')

env.close()


 Todos os comandos devem ser executados dentro de um contexto de transação (`write=True`). Após as alterações, use `commit()` para salvar (embora o `with` já faça isso automaticamente). ### 3. Consulta de dados

import lmdb

env = lmdb.open("./train")

with env.begin() as txn: # Obter valor por chave value = txn.get(b'2') print(value.decode('utf-8'))

# Iterar sobre todas as entradas
for key, val in txn.cursor():
    print(key.decode('utf-8'), val.decode('utf-8'))

 A função `cursor()` permite percorrer todos os pares chave-valor. Os valores retornados são em formato `bytes`, então use `.decode()` para converter para string. ### 4. Leitura de um banco existente

import lmdb

env_db = lmdb.open("trainC", readonly=True) txn = env_db.begin()

Buscar valor

print(txn.get(b'200'))

Iterar sobre todos os dados

for key, value in txn.cursor(): print(key.decode('utf-8'), value.decode('utf-8'))

env_db.close()


Princípios Inetrnos do LMDB
---------------------------

 O LMDB utiliza **mmap** (memory mapping) para mapear diretamente o arquivo de dados na memória virtual do processo. Isso elimina cópias de dados entre kernel e usuário, permitindo acesso direto via ponteiros. - **Leitura**: usa `mmap` com acesso somente leitura → zero-coppy. - **Escrita**: usa chamadas de sistema `write()` para garantir consistência do sistema de arquivos. O banco organiza os dados em uma estrutura B+Tree com **append-only**, o que significa que atualizações são feitas adicionando novos nós, mantendo versões anteriores intactas. Isso garante segurança contra corrupção em caso de falha. Além disso, o LMDB implementa **MVCC (Multi-Version Concurrency Control)** com uma única thread de escrita, evitando deadlocks e permitindo leitores simultâneos sem bloqueios. Exemplo com imagens
-------------------

 Para armazenar imagens em formato binário junto com rótulos: ```
import lmdb
import cv2

image_path = 'cat.jpg'
label = 'cat'

env = lmdb.open('lmdb_dir', map_size=int(1e10))

# Ler imagem como binário
with open(image_path, 'rb') as f:
    image_data = f.read()

# Armazenar em pares chave-valor
cache = {
    'image_000': image_data,
    'label_000': label
}

with env.begin(write=True) as txn:
    for k, v in cache.items():
        if isinstance(v, str):
            txn.put(k.encode(), v.encode())
        else:
            txn.put(k.encode(), v)

env.close()

Leitura de imagens do LMDB

import cv2
import lmdb
import numpy as np

env = lmdb.open('lmdb_dir', readonly=True)

with env.begin() as txn:
   img_bin = txn.get(b'image_000')
   label = txn.get(b'label_000').decode('utf-8')

   # Converter binário para array NumPy
   img_array = np.frombuffer(img_bin, dtype=np.uint8)
   # Decodificar para imagem RGB
   img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

   cv2.imshow('Imagem', img)
   cv2.waitKey(0)

Integração com PyTorch

Use o DataLoader com suporte a LMDB para carregar dados de forma eficiente durante treinamento: ``` import lmdb import numpy as np from torch.utils.data import Dataset, DataLoader

class LMDBDataset(Dataset): def init(self, db_path, meta_info_path): self.env = lmdb.open(db_path, readonly=True, lock=False) self.meta_info = pickle.load(open(meta_info_path, 'rb')) self.keys = self.meta_info['keys'] self.sizes = self.meta_info['resolution']

def __len__(self):
    return len(self.keys)

def __getitem__(self, idx):
    key = self.keys[idx]
    size = [int(x) for x in self.sizes[idx].split('_')]
    C, H, W = size

    with self.env.begin() as txn:
        buf = txn.get(key.encode('ascii'))
    img_flat = np.frombuffer(buf, dtype=np.uint8)
    img = img_flat.reshape(H, W, C)

    # Convert to tensor (exemplo simples)
    img_tensor = torch.from_numpy(img).permute(2, 0, 1).float() / 255.0
    return img_tensor, key

Exemplo de uso

dataset = LMDBDataset('datasets/ECSSD.lmdb', 'datasets/ECSSD.lmdb/meta_info.pkl') loader = DataLoader(dataset, batch_size=4, shuffle=True, num_workers=2)


 Esse padrão é ideal para conjuntos de imagens grandes, como ImageNet, onde o custo de IO é crítico. Conclusão
---------

 O LMDB oferece um excelente equilíbrio entre simplicidade, desempenho e escalabilidade para armazenamento de dados em aplicações de aprendizado profundo. Sua natureza de arquivo único, acesso direto à memória e suporte nativo a múltiplos processos tornam-no uma escolha ideal para pipelines de dados em produção. </div>

Tags: lmdb Python memory-mapped database Pytorch

Publicado em 9-4 02:17