Introdução: Por que o cache é necessário em C++?
No software moderno de alta carga, a velocidade de acesso aos dados é crítica. Seja um servidor web processando milhões de requisições ou um terminal de negociação de alta frequência — cada acesso extra ao banco de dados ou a um disco lento pode se tornar um gargalo. É aqui que o cache entra em ação — uma técnica de armazenamento de dados frequentemente solicitados em memória de alta velocidade (RAM) para acesso instantâneo posterior.
A linguagem C++ oferece ao desenvolvedor oportunidades únicas para construir sistemas de cache eficientes: desde o controle refinado sobre a memória até o uso de poderosas ferramentas externas. Neste artigo, percorreremos o caminho desde soluções embutidas simples até sistemas industriais, como Redis e Memcached, e analisaremos quando e qual abordagem escolher.
Você aprenderá:
- Como implementar um cache simples em C++ sem bibliotecas de terceiros.
- Como conectar e usar Redis e Memcached.
- Comparação de desempenho e cenários de aplicação de cada abordagem.
Soluções embutidas: Seu próprio cache em C++
Antes de conectar serviços externos, vale a pena entender que para muitas tarefas um cache embutido é suficiente. As principais vantagens: latência de rede zero, simplicidade de implantação e controle total sobre os dados.
Cache mais simples com std::unordered_map
A opção mais básica é usar um array associativo. No entanto, ele não possui um mecanismo de despejo de dados (eviction policy) e crescerá indefinidamente, o que pode levar ao estouro de memória.
#include <unordered_map>#include <string>
class SimpleCache { std::unordered_map<std::string, std::string> cache_;public: void put(const std::string& key, const std::string& value) { cache_[key] = value; } std::string get(const std::string& key) { auto it = cache_.find(key); if (it != cache_.end()) { return it->second; } return ""; // ou lançar uma exceção }};Cache LRU (Least Recently Used)
Uma solução mais avançada é o cache LRU, que remove os elementos menos usados ao atingir o limite. A implementação requer uma combinação de tabela hash e lista duplamente encadeada.
#include <list>#include <unordered_map>
template<typename K, typename V>class LRUCache { size_t capacity_; std::list<std::pair<K, V>> items_; std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> cache_;
public: LRUCache(size_t cap) : capacity_(cap) {}
V get(const K& key) { auto it = cache_.find(key); if (it == cache_.end()) { throw std::runtime_error("Key not found"); } items_.splice(items_.begin(), items_, it->second); return it->second->second; }
void put(const K& key, const V& value) { auto it = cache_.find(key); if (it != cache_.end()) { it->second->second = value; items_.splice(items_.begin(), items_, it->second); return; } if (items_.size() == capacity_) { auto last = items_.back(); cache_.erase(last.first); items_.pop_back(); } items_.emplace_front(key, value); cache_[key] = items_.begin(); }};Esse cache é ideal para trabalhar em um único processo, quando os dados pertencem apenas a esse processo e não exigem sincronização.
Memcached: Cache distribuído para dados simples
Memcached é um sistema de cache leve e de alto desempenho que opera sobre o protocolo TCP/UDP. Ele armazena dados na memória RAM e não suporta persistência (persistence). Uma excelente escolha para armazenar em cache res