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>