Introdução
Com o crescimento das aplicações modernas, o volume de consultas ao banco de dados aumenta significativamente, especialmente em cenários com alta concorrência e grande volume de leitura. Mesmo com otimizações tradicionais, como índices bem definidos, tuning de queries e ajustes de configuração, chega um momento em que o banco de dados se torna um gargalo.
Esse cenário é comum em sistemas que escalam rapidamente, como APIs, dashboards e aplicações transacionais com forte demanda de leitura.
É nesse contexto que surge o ReadySet, uma solução que atua como uma camada inteligente de cache para bancos relacionais como o MySQL e também o PostgreSQL.
O que é o ReadySet?
O ReadySet é um mecanismo de cache em tempo real que funciona como um endpoint compatível com bancos relacionais. Ele recebe queries SQL e armazena automaticamente seus resultados em memória, acelerando respostas subsequentes.
Ele é compatível com:
- MySQL
- PostgreSQL
Diferente de soluções tradicionais como Redis ou Memcached, o ReadySet não exige mudanças na aplicação nem implementação manual de lógica de cache.
A própria query SQL é utilizada como base para o gerenciamento do cache.
Como o ReadySet funciona?
O funcionamento do ReadySet pode ser entendido em três pilares:
- Interceptação de Queries
A aplicação se conecta ao ReadySet da mesma forma que se conectaria ao banco de dados. As queries são enviadas normalmente, sem necessidade de alteração.
- Cache Inteligente
O ReadySet identifica padrões de leitura e armazena resultados em memória, permitindo respostas mais rápidas em acessos frequentes aos mesmos dados.
- Atualização via CDC (Change Data Capture)
O ReadySet utiliza o conceito de Change Data Capture (CDC) para manter os dados sincronizados.
Ele monitora os mecanismos de log dos bancos suportados:
- No MySQL: binlog
- No PostgreSQL: WAL (Write-Ahead Log)
Capturando alterações como:
- INSERT
- UPDATE
- DELETE
O cache é atualizado automaticamente, sem necessidade de invalidação manual, mantendo a consistência dos dados.
Proposta do laboratório
Após compreender os conceitos do ReadySet, foi construído um laboratório local com o objetivo de validar seu comportamento na prática.
Neste experimento, foi utilizado o MySQL como banco de dados principal, mas a arquitetura apresentada também se aplica ao uso com PostgreSQL.
O ambiente foi composto por:
- Um banco de dados
- O ReadySet como camada de cache
- Uma aplicação simples para geração de carga
- Execução de testes e observação dos resultados
Hands-on
Antes da execução do laboratório, foi necessária a preparação do ambiente no Oracle Linux 8, com a remoção do Podman e instalação do Docker:
|
1 2 3 4 5 |
dnf remove -y podman podman-compose buildah dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo dnf install -y docker-ce docker-ce-cli containerd.io systemctl enable docker systemctl start docker |
Após a instalação, foi criada a estrutura de diretórios utilizada no laboratório:
|
1 2 3 |
mkdir -p /dados/docker mkdir -p /dados/readyset cd /dados/readyset |
Em seguida, os arquivos do projeto foram organizados conforme a estrutura abaixo:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@perconaServer readyset]# tree . ├── app │ ├── benchmark.py │ ├ │ └── Dockerfile ├── docker-compose.yml ├── init │ ├── schema.sql │ └── seed.sql └── mysql-config └── custom.cnf |
Construção do ambiente com docker-compose ja banco de dados ja preparado com um pequena amostra de dados
|
1 2 3 4 5 6 7 8 9 10 |
[root@perconaServer readyset]# [root@perconaServer readyset]# docker compose up -d [+] Running 6/6 ✔ Network readyset_default Created ✔ Volume "readyset_mysql_data" Created ✔ Volume "readyset_readyset_cache" Created ✔ Container rs-mysql Healthy ✔ Container rs-cache Healthy ✔ Container rs-demo-app Started [root@perconaServer readyset]# |
Apos subir o docker verificação de funcionamento e replicação entre o Mysql e o Readyset
|
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 36 37 38 |
[root@perconaServer readyset]# docker compose logs readyset | grep Streaming rs-cache | 2026-04-21T19:25:58.901147Z INFO replicators::noria_adapter: Streaming replication started position=mysql-bin.000003:197 [root@perconaServer readyset]# # ---------------------------------------------------- [root@perconaServer readyset]# mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo mysql: [Warning] Using a password on the command line interface can be insecure. Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.0.45-readyset ReadySet Copyright (c) 2000, 2025, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> SHOW READYSET STATUS; +----------------------------+--------------------------+ | Variable_name | Value | +----------------------------+--------------------------+ | Database Connection | Connected | | Connection Count | 2 | | Status | Online | | Replication Status | Running | | Maximum Replication Offset | mysql-bin.000003:508 | | Minimum Replication Offset | mysql-bin.000003:508 | | Last started Controller | 2026-04-21 19:25:57 UTC | | Last completed snapshot | 2026-04-21 19:25:58 UTC | | Last started replication | 2026-04-21 19:25:58 UTC | | Enabled Features | None | +----------------------------+--------------------------+ 10 rows in set (0.01 sec) mysql> |
Meu docker compose ja nos trouxe em sua construção uma pequena amostra de dados:
tabelas: produtos, clientes, pedidos , validando esses dados em ambos ambientes:
|
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 |
[root@perconaServer readyset]# #mysql [root@perconaServer readyset]# time mysql -h 127.0.0.1 -P 3306 -u rsuser -preadyset demo \ > -e "SELECT status, COUNT(*) AS total, SUM(valor_total) FROM pedidos GROUP BY status;" mysql: [Warning] Using a password on the command line interface can be insecure. +-----------+-------+------------------+ | status | total | SUM(valor_total) | +-----------+-------+------------------+ | pendente | 4 | 3799.70 | | pago | 5 | 6349.20 | | enviado | 3 | 3399.50 | | entregue | 4 | 5849.30 | +-----------+-------+------------------+ real 0m0.027s user 0m0.016s sys 0m0.006s [root@perconaServer readyset]# [root@perconaServer readyset]# #readyset [root@perconaServer readyset]# time mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo \ > -e "SELECT status, COUNT(*) AS total, SUM(valor_total) FROM pedidos GROUP BY status;" mysql: [Warning] Using a password on the command line interface can be insecure. +-----------+-------+------------------+ | status | total | SUM(valor_total) | +-----------+-------+------------------+ | pendente | 4 | 3799.70 | | pago | 5 | 6349.20 | | enviado | 3 | 3399.50 | | entregue | 4 | 5849.30 | +-----------+-------+------------------+ real 0m0.015s user 0m0.002s sys 0m0.007s [root@perconaServer readyset]# |
Um ponto interessante observado nos testes foi a diferença no tempo de resposta entre a execução direta no MySQL e via ReadySet.
Na execução direta no MySQL, a query apresentou um tempo de aproximadamente 0.027s, enquanto a mesma consulta executada via ReadySet retornou em cerca de 0.015s.
Embora ambos os tempos sejam baixos, já que se trata de um ambiente controlado e com volume reduzido de dados, é possível notar uma redução de latência ao utilizar o ReadySet.
Esse comportamento indica que parte do processamento da query foi otimizado, especialmente nas execuções realizadas através da camada de cache.
Além disso, os resultados retornados foram idênticos em ambos os cenários, o que demonstra consistência nos dados apresentados.
Validando a sincronização de registros e caches :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[root@perconaServer readyset]# # Novo pedido [root@perconaServer readyset]# mysql -h 127.0.0.1 -P 3306 -u rsuser -preadyset demo \ > -e "INSERT INTO pedidos (cliente_id, produto_id, quantidade, valor_total, status) \ > VALUES (2, 3, 2, 699.80, 'pendente');" mysql: [Warning] Using a password on the command line interface can be insecure. [root@perconaServer readyset]# [root@perconaServer readyset]# # Consulta imediatamente no ReadySet - cache já atualizado via binlog [root@perconaServer readyset]# mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo \ > -e "SELECT status, COUNT(*) AS total FROM pedidos GROUP BY status;" mysql: [Warning] Using a password on the command line interface can be insecure. +-----------+-------+ | status | total | +-----------+-------+ | pendente | 5 | | pago | 5 | | enviado | 3 | | entregue | 4 | +-----------+-------+ [root@perconaServer readyset]# |
Criando os caches no Readyset e consultando
|
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 36 37 |
mysql> # Cache 1: pedidos (GROUP BY status) mysql> CREATE CACHE pedidos_status FROM -> SELECT status, COUNT(*) AS total, SUM(valor_total) AS receita -> FROM pedidos GROUP BY status; SHOW CACHES; Query OK, 0 rows affected (0.54 sec) mysql> mysql> # Cache 2: produtos por categoria mysql> CREATE CACHE produtos_categoria FROM -> SELECT nome, preco, estoque FROM produtos -> WHERE categoria = 'Informatica' ORDER BY preco DESC; Query OK, 0 rows affected (0.17 sec) mysql> mysql> # Cache 3: top clientes com JOIN mysql> CREATE CACHE top_clientes FROM -> SELECT c.nome, COUNT(p.id) AS num_pedidos, SUM(p.valor_total) AS total_gasto -> FROM clientes c JOIN pedidos p ON c.id = p.cliente_id -> GROUP BY c.id, c.nome ORDER BY total_gasto DESC LIMIT 5; Query OK, 0 rows affected (0.33 sec) mysql> mysql> # Confirmar caches criados mysql> SHOW CACHES; +--------------------+--------------------+------------------------------------------------------------------------------------------------------+ | query id | cache name | query text | | | properties | count | +--------------------+--------------------+------------------------------------------------------------------------------------------------------+ | q_cb4815eaf4cdc571 | pedidos_status | SELECT `demo`.`pedidos`.`status`, count(*) AS `total`, sum(`demo`.`pedidos`.`valor_total`) AS `receita` FROM `demo`.`pedidos` GRO | | | deep | 0 | | q_848dc1aab7bc4e9 | produtos_categoria | SELECT `demo`.`produtos`.`nome`, `demo`.`produtos`.`preco`, `demo`.`produtos`.`estoque` FROM `demo`.`produtos` WHERE (`demo`.`pro | | | deep | 0 | | q_6b7703dc667b5f1 | top_clientes | SELECT `demo`.`clientes`.`nome`, count(`demo`.`pedidos`.`id`) AS `num_pedidos`, sum(`demo`.`pedidos`.`valor_total`) AS `total_gas | | | deep | 0 | +--------------------+--------------------+------------------------------------------------------------------------------------------------------+ 3 rows in set (0.01 sec) |
Para validar seu comportamento em uma situação mais próxima de um ambiente real, foi realizado um teste com aumento significativo de volume de dados, inserindo aproximadamente 500k registros nas tabelas.
Além disso, foi utilizada uma consulta mais complexa, envolvendo:
JOINentre tabelasGROUP BYHAVINGORDER BYLIMIT
Execução da consulta sem a criação do cache:
|
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 |
[root@perconaServer readyset]# time mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo -e " > SELECT > c.nome, > SUM(pe.valor_total) AS total_gasto, > COUNT(*) AS pedidos > FROM pedidos pe > JOIN clientes c ON pe.cliente_id = c.id > GROUP BY c.id > HAVING total_gasto > 10000 > ORDER BY total_gasto DESC > LIMIT 10;" mysql: [Warning] Using a password on the command line interface can be insecure. +---------------+-------------+---------+ | nome | total_gasto | pedidos | +---------------+-------------+---------+ | Cliente 8702 | 16602.10 | 2 | | Cliente 1875 | 16600.37 | 2 | | Cliente 11432 | 16599.87 | 2 | | Cliente 4605 | 16598.13 | 2 | | Cliente 7335 | 16595.89 | 2 | | Cliente 508 | 16594.17 | 2 | | Cliente 10065 | 16593.66 | 2 | | Cliente 3238 | 16591.93 | 2 | | Cliente 12795 | 16591.42 | 2 | | Cliente 5968 | 16589.69 | 2 | +---------------+-------------+---------+ real 0m2.551s user 0m0.006s sys 0m0.004s [root@perconaServer readyset]# |
Criação do cache para atender a consulta:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[root@perconaServer readyset]# mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo -e " > CREATE CACHE top_gastadores FROM > SELECT > c.nome, > SUM(pe.valor_total) AS total_gasto, > COUNT(*) AS pedidos > FROM pedidos pe > JOIN clientes c ON pe.cliente_id = c.id > GROUP BY c.id > HAVING total_gasto > 10000 > ORDER BY total_gasto DESC > LIMIT 10;" mysql: [Warning] Using a password on the command line interface can be insecure. [root@perconaServer readyset]# |
Warmup – ReadySet executa a query pela primeira vez e armazena seu resultado em cache.
|
1 2 3 4 5 6 7 |
root@perconaServer readyset]# mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo -e " > SELECT c.nome, SUM(pe.valor_total) AS total_gasto, COUNT(*) AS pedidos > FROM pedidos pe JOIN clientes c ON pe.cliente_id = c.id > GROUP BY c.id HAVING total_gasto > 10000 > ORDER BY total_gasto DESC LIMIT 10;" > /dev/null mysql: [Warning] Using a password on the command line interface can be insecure. [root@perconaServer readyset]# |
Resultados obtidos com a consulta em cache:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[root@perconaServer readyset]# time mysql -h 127.0.0.1 -P 3307 -u rsuser -preadyset demo -e " > SELECT c.nome, SUM(pe.valor_total) AS total_gasto, COUNT(*) AS pedidos > FROM pedidos pe JOIN clientes c ON pe.cliente_id = c.id > GROUP BY c.id HAVING total_gasto > 10000 > ORDER BY total_gasto DESC LIMIT 10;" mysql: [Warning] Using a password on the command line interface can be insecure. +---------------+-------------+---------+ | nome | total_gasto | pedidos | +---------------+-------------+---------+ | Cliente 8702 | 16602 | 2 | | Cliente 1875 | 16600 | 2 | | Cliente 11432 | 16600 | 2 | | Cliente 4605 | 16598 | 2 | | Cliente 7335 | 16596 | 2 | | Cliente 508 | 16594 | 2 | | Cliente 10065 | 16594 | 2 | | Cliente 3238 | 16592 | 2 | | Cliente 12795 | 16591 | 2 | | Cliente 5968 | 16590 | 2 | +---------------+-------------+---------+ real 0m0.015s user 0m0.003s sys 0m0.006s |
Conclusão
A implementação do ReadySet demonstrou, de forma prática, o impacto que uma camada de cache inteligente pode gerar em aplicações que utilizam MySQL.
Durante os testes, foi possível observar claramente a diferença de comportamento entre a execução tradicional e o uso do cache.
Na execução sem cache, a consulta apresentou um tempo de aproximadamente 2.55 segundos, o que é esperado, já que o banco de dados precisa realizar todas as etapas de processamento, incluindo joins, agregações (SUM, COUNT), aplicação de filtros (HAVING) e ordenação dos resultados.
Após o aquecimento do cache (warmup), a mesma consulta passou a ser respondida em cerca de 0.015 segundos.
Essa diferença representa uma redução superior a 99% no tempo de resposta, evidenciando o ganho significativo proporcionado pelo uso do cache.
Esse comportamento reforça um ponto fundamental: o principal benefício do ReadySet não está apenas na aceleração de leitura, mas na eliminação do custo de execução de queries complexas no banco de dados.
Mesmo em um ambiente controlado, os resultados foram expressivos e indicam que, em cenários com maior volume de dados e carga de acesso, o impacto tende a ser ainda mais relevante.
ReadySet se mostra como uma solução eficiente para otimização de leitura, permitindo ganhos reais de performance sem necessidade de alterações na aplicação, atuando como uma camada complementar ao banco de dados.
Demonstração prática
Links uteis:
https://github.com/readysettech
https://github.com/marcio28costa/readyset_handson





