<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Bits & Blocos]]></title><description><![CDATA[Bits & Blocos]]></description><link>https://jefferson-f.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 16 Sep 2026 23:08:36 GMT</lastBuildDate><atom:link href="https://jefferson-f.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Como Lidar com o Pesadelo do Gerenciador de Buffer]]></title><description><![CDATA[Após a luta contra o “Byte Fantasma” e a serialização e a deserialização manual (que rendeu algumas boas histórias), havia chegado o momento enfrentar o próximo “boss”: gerenciador de buffer.
Basicamente, ele é responsável por fazer a gestão do cache...]]></description><link>https://jefferson-f.hashnode.dev/como-lidar-com-o-pesadelo-do-gerenciador-de-buffer</link><guid isPermaLink="true">https://jefferson-f.hashnode.dev/como-lidar-com-o-pesadelo-do-gerenciador-de-buffer</guid><category><![CDATA[SIGSEGV]]></category><category><![CDATA[buffer-pool]]></category><category><![CDATA[buffer-management]]></category><category><![CDATA[TCC]]></category><category><![CDATA[C++]]></category><category><![CDATA[Banco de Dados]]></category><category><![CDATA[Databases]]></category><category><![CDATA[LRU Cache]]></category><category><![CDATA[pinning]]></category><category><![CDATA[hashmap]]></category><category><![CDATA[#DoublyLinkedList]]></category><category><![CDATA[cache]]></category><category><![CDATA[Complexity in Algorithm]]></category><category><![CDATA[auroradb]]></category><category><![CDATA[devlog]]></category><dc:creator><![CDATA[Jefferson Ferreira]]></dc:creator><pubDate>Wed, 29 Oct 2025 20:35:32 GMT</pubDate><content:encoded><![CDATA[<p>Após a luta contra o “Byte Fantasma” e a serialização e a deserialização manual (que rendeu algumas boas histórias), havia chegado o momento enfrentar o próximo “boss”: gerenciador de buffer.</p>
<p>Basicamente, ele é responsável por fazer a gestão do cache. Em termos simples: quais páginas ficam na memória principal e quais saem, de forma a minimizar as lentas e dolorosas idas ao disco.</p>
<h2 id="heading-escolha-da-estrategia-por-que-lru">Escolha da Estratégia: Por que LRU?</h2>
<p>Antes de mergulhar nos detalhes obscuros da implementação (e, já adianto, foi um desafio e tanto), preciso defender minha escolha da estratégia de substituição de páginas. Para o AuroraDB, eu escolhi a política LRU (Least Recently Used), ou “Menos Recentemente Usado”.</p>
<p>A ideia é fundamentalmente simples:</p>
<ol>
<li><p>Enquanto há espaço livre no buffer pool, qualquer página solicitada do disco é carregada nele.</p>
</li>
<li><p>Quando o buffer pool enche e precisamos carregar uma nova página, a política LRU entra em ação.</p>
</li>
<li><p>A regra é clara: a página que não é acessada há mais tempo é removida.</p>
</li>
</ol>
<p>“Mas espera, LRU não é a melhor escolha para SGBDs!”</p>
<p>Eu sei. Mas tem uma explicação! (Abaixa essa faca)</p>
<p>Politicas como a LRU clássica de fato tem problemas conhecidos em bancos de dados, principalmente com scans sequenciais muito longos, que podem “lavar” o cache inteiro, jogando fora páginas que seriam importantes. Políticas mais robustas (e complexas) como LRU-K, entre outras, são geralmente consideradas superiores para essa tarefa.</p>
<p>Ai você me pergunta: “Se não é a mais adequada, por que diabos você a escolheu?”</p>
<p>Terei que ser honesto aqui: Tempo.</p>
<ol>
<li><p><strong>Simplicidade de Implementação:</strong> Entre as políticas existentes, a LRU é uma das mais diretas de se implementar do “zero”. Dado o cronograma apertado do TCC, simplicidade foi um critério com bastante peso.</p>
</li>
<li><p><strong>Desenho do Experimento:</strong> Eu estava ciente dessa limitação. Por isso, os experimentos planejados para o TCC foram desenhados para mitigar alguns dos problemas, portanto, foi um trade-off consciente.</p>
</li>
</ol>
<h2 id="heading-onde-o-pesadelo-realmente-comeca-a-implementacao">Onde o Pesadelo Realmente Começa: A Implementação</h2>
<p>Escolher foi a parte fácil, só precisei ler um pouco sobre as estratégias. Agora, como implementar? Isso foi um grande desafio.</p>
<h3 id="heading-o-problema-da-eficiencia-o1">O Problema da Eficiência: O(1)</h3>
<p>A politica LRU exige duas operações principais:</p>
<ul>
<li><p>Encontrar uma página no cache (ela já está na memória?)</p>
</li>
<li><p>Mover a página para o topo da lista sempre que for acessada.</p>
</li>
</ul>
<p>Como você talvez já tenha percebido, não dá para simplesmente usar um array, afinal buscas e movimentações seriam essencialmente ineficientes, o tempo gasto dependeria do tamanho do array. A solução clássica aqui é uma combinação de estruturas de dados:</p>
<ul>
<li><p><strong>Hash Map:</strong> Busca instantânea. A chave é o ID da página e o valor é um ponteiro para o nó da página na nossa lista. Com isso, temos a busca em O(1).</p>
</li>
<li><p><strong>Lista Duplamente Encadeada:</strong> É ela que vai manter a ordem LRU. A “cabeça” da lista é a página que foi usada mais recentemente e a “cauda” a menos recentemente usada.</p>
</li>
</ul>
<p>Quando uma página que já está no cache é acessada, fazemos isso:</p>
<ol>
<li><p>Achamos ela no hashmap (O(1)).</p>
</li>
<li><p>Usamos o ponteiro para pega-lá na lista duplamente encadeada.</p>
</li>
<li><p>Removemos ela da posição atual (O(1), pois é duplamente encadeada).</p>
</li>
<li><p>Movemos ela para a “cabeça” da lista (O(1)).</p>
</li>
</ol>
<p>Mágico!… E bem complexo de gerenciar ponteiros sem errar. Mas, essa é a implementação teórica, na prática, eu tive que adicionar mais duas estruturas de dados:</p>
<ul>
<li><p><strong>Vetor:</strong> Cada página tem 4004 bytes, mover/copiar essa quantidade de bytes pra lá e pra cá não é exatamente eficiente e ineficiência na minha casa não kkkkkkkk. A solução para isso foi o vetor. As páginas ficam estáticas em um vetor na memória, o índice delas nesse vetor é o que fica armazenado em cada nó da lista duplamente encadeada. Dessa forma, ao mover os nós, estou movendo uma quantidade massivamente menor de bytes, o que é mais eficiente.</p>
</li>
<li><p><strong>Fila (FIFO):</strong> Como as páginas ficam em um vetor, eu precisava de uma maneira de gerenciar quais frames do vetor estão livres. Minha primeira ideia foi ter uma variável que armazenava o valor do indice livre, porém, o que fazer se o índice 2 ficar livre e, logo em seguida, o indice 5 ficar livre? Se ficar em apenas uma variável, uma das duas informações eu vou perder. A solução que encontrei foi manter uma fila de frames livres. Vai liberar uma posição no vetor? Enqueue na fila. Vai ocupar uma posição no vetor? Dequeue na fila.</p>
</li>
</ul>
<h3 id="heading-integridade-e-o-adeus-ao-o1">Integridade e o Adeus ao O(1)</h3>
<p>Com essa estrutura de quatro partes (Hash Map, Lista LRU, Vetor de Páginas e Fila de Indices Livres), eu tinha um sistema eficiente para alocar e encontrar páginas. Porém, ainda havia um problema para lidar ao desalocar páginas.</p>
<p>Imagine que a política LRU decide que a cauda da lista (suponhamos que seja o índice 5) deve ser removido, porém, outra parte do AuroraDB ainda está usando os dados? Eu não posso simplesmente jogar o índice 5 fora! A solução que encontrei para isso foi pinning.</p>
<p>Cada nó adicionado na lista duplamente encadeada precisou de um “contador de pins”, que eu nomeei de pinCount. Quando uma parte do sistema pega a página para usar, ela “pina” a página, o que na prática significa que o pinCount é incrementado. E, quando ela termina de usar, ela “despina” a página, o que na prática significa que ela decrementa o pinCount.</p>
<p>Isso significa que, eu só posso escolher para substituir páginas que não esteja em uso no momento, ou seja, que o pinCount seja igual a 0. Aqui ainda precisaria haver a verificação se a página a ser removida foi alterada, com uma flag de dirty bit, para saber se devo ou não reescrever ela no disco, mas ainda não implementei essa parte.</p>
<p>Porém, os mais atentos devem ter notado um problema (que eu mesmo comentei no titulo dessa seção), sabe aquela linda teoria sobre ser O(1) e a eficiência que eu tentei manter a todo custo? Pois é, aqui isso foi perdido por causa do pinCount. Afinal, preciso ir buscando na lista qual o primeiro candidato que está com pinCount igual a 0 a partir da cauda. No pior dos cenários, eu posso ter que varrer a lista toda (uma operação O(n)) só para achar uma página que não esteja pinada. Isso transforma a LRU numa “busca linear pelo primeiro candidato livre a partir do menos recente”.</p>
<h2 id="heading-o-batismo-do-c">O Batismo do C++</h2>
<p>Nem preciso dizer que implementar isso em C++, mesmo que seguindo os padrões modernos, foi bastante trabalhoso né? Passei por alguns “ritos de passagem” enquanto implementava todas essas estruturas de dados e as sincronizava umas com as outras. Em dois momentos em especial, acabei tomando um SIGSEGV na cara — um sinal disparado pelo S.O que basicamente significa que eu estava tentando acessar memória que meu programa não tinha permissão para acessar. Isso gerou alguns dialogos engraçados com alguns amigos meus, como podemos ver abaixo:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761769032534/ebd67dc3-b92a-4ee7-8109-5da98a0217a6.jpeg" alt class="image--center mx-auto" /></p>
<p>Houveram outros erros também, como dangling pointer (basicamente, um ponteiro que fica “pendurado”, ou seja, apontando para algo que já foi liberado) e, no final, também consegui a proeza de corromper a heap. Sendo justo comigo mesmo, esse ultimo não foi erro na lógica interna do código, foi apenas eu descobrindo que chamar o destrutor diretamente no código é comportamento indefinido, mas também gerou um dialógo engraçado:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761769294465/bbba61eb-a861-4289-84a3-f408e634bc02.jpeg" alt class="image--center mx-auto" /></p>
<p>E assim, com direito a segmentation faults e corrupção de heap, o gerenciador de buffer foi (quase) finalizado. A implementação do dirty bit fica para uma próxima, mas o coração do sistema está lá.</p>
<p>Essa foi, sem dúvida, a parte mais complexa e recompensadora do AuroraDB até agora. Mas agora, quero saber de você:</p>
<ul>
<li><p>Qual foi seu maior “rito de passagem” com C++ ou gerenciamento de memória?</p>
</li>
<li><p>Já teve que lidar com um SIGSEGV misterioso que te fez questionar sua própria sanidade?</p>
</li>
</ul>
<p>Deixe sua história ai nos comentários! Seria legal saber que não estou sozinho nesse “batismo” de ponteiros.</p>
]]></content:encoded></item><item><title><![CDATA[O Byte Fantasma: Por que minha struct era maior que a soma de suas partes?]]></title><description><![CDATA[Na minha saga de construir um SGBD educacional para meu TCC, a primeira regra é: o tempo é curto. Bastante curto. Com o relógio correndo, precisei definir um escopo brutalmente focado: o Gerenciador de Armazenamento.
Por que começar por ele? Porque e...]]></description><link>https://jefferson-f.hashnode.dev/o-byte-fantasma-por-que-minha-struct-era-maior-que-a-soma-de-suas-partes</link><guid isPermaLink="true">https://jefferson-f.hashnode.dev/o-byte-fantasma-por-que-minha-struct-era-maior-que-a-soma-de-suas-partes</guid><category><![CDATA[SGBD]]></category><category><![CDATA[Programação de Sistemas]]></category><category><![CDATA[alinhamento de memoria]]></category><category><![CDATA[Do Zero]]></category><category><![CDATA[C++]]></category><category><![CDATA[Banco de Dados]]></category><category><![CDATA[Databases]]></category><category><![CDATA[padding]]></category><category><![CDATA[serialization]]></category><category><![CDATA[#engenharia de software]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[TCC]]></category><category><![CDATA[from scratch]]></category><dc:creator><![CDATA[Jefferson Ferreira]]></dc:creator><pubDate>Fri, 17 Oct 2025 20:51:45 GMT</pubDate><content:encoded><![CDATA[<p>Na minha saga de construir um SGBD educacional para meu TCC, a primeira regra é: o tempo é curto. Bastante curto. Com o relógio correndo, precisei definir um escopo brutalmente focado: o Gerenciador de Armazenamento.</p>
<p>Por que começar por ele? Porque ele é o coração de qualquer banco de dados. Todo o resto (otimizador de consulta, SQL, etc.) depende dele. Afinal, vale lembrar, o objetivo final de um banco de dados é armazenar dados.</p>
<p>Dito isso, meu primeiro componente foi o Gerenciador de Disco (que chamei de PageManager). A função dele parece simples: ser o porteiro para o disco. O que lê e escreve páginas de dados no arquivo do banco, abstraindo os detalhes sujos do acesso a disco para o resto do sistema.</p>
<p>Sendo sincero, meu primeiro pensamento foi: “Vai ser fácil. É só usar ‘.write()’ e ‘.read()’ da fstream através de structs. Certo?”</p>
<p><strong>Errado.</strong></p>
<h2 id="heading-de-onde-esta-vindo-esse-lixo">De onde está vindo esse “lixo”?</h2>
<p>Minha primeira implementação foi ingênua. Eu tinha uma <code>struct Page</code> que, na minha matemática, deveria ter 4004 bytes. Porém quando eu a escrevia n disco, o arquivo ficava maior. E pior ainda, ao ler os dados de volta, algumas vezes vinha “lixo” no meio dos dados.</p>
<p>Eu pensei que ia ser algo simples, talvez posicionamento no arquivo estava errado ou algo do tipo, mas era um problema mais sutil e que era externo ao meu código. Minha struct estava, na verdade, ocupando mais espaço. E o culpado por isso? O compilador! (e o pior, ele está certo em fazer isso.)</p>
<p>Sendo mais especifico, o culpado tem um nome mais especifico: Padding (alinhamento de memória).</p>
<p>Descobri que o compilador adiciona bytes “fantasmas” entre os atributos de uma <code>struct</code>. Ele não faz isso pra me irritar, mas sim para otimizar a performance. (é bem verdade que as vezes irrita também kkkkk).</p>
<p>Funciona assim: o processador não gosta de ler dados em endereços de memória “quebrados”. Ele é muito mais rápido lendo um <code>int</code> (4 bytes) em um endereço multiplo de 4, ou um <code>double</code> (8 bytes) em um endereço múltiplo de 8.</p>
<p>Para garantir esse alinhamento, o compilador preenche os espaços. Se você tem um <code>char</code> (1 byte) seguido de um <code>int</code> (4 bytes), o compilador provavelmente vai adicionar 3 bytes de padding após o <code>char</code> para que o <code>int</code> comece em um endereço alinhado.</p>
<p>Meu PageManager era ingenuo. Ele pegava a <code>struct</code> inteira da memória, incluindo os bytes de padding, e jogava tudo no disco. Isso é ineficiente (desperdiça espaço) e perigoso, pois o layout na memória pode ser diferente em diferentes compiladores ou arquiteturas.</p>
<h2 id="heading-serializacao-e-deserializacao">Serialização e Deserialização</h2>
<p>A solução? Serialização e Deserialização manual.</p>
<ol>
<li><p>Para Escrever: Criei um <code>char buffer[PAGE_SIZE]</code>. Em vez de escrever a <code>struct</code> inteira, copiei cada atributo, um por um, para dentro do buffer usando <code>memcpy</code>. <code>memcpy(endBuffer, &amp;</code><a target="_blank" href="http://page.id"><code>page.id</code></a><code>, sizeof(unsigned int));</code> Só então eu escrevo o buffer no disco.</p>
</li>
<li><p>Para Ler: O processo inverso. Leio a página do disco para dentro do buffer e depois desfaço o processo, copiando os bytes do buffer de volta para os atributos da minha <code>struct</code> na memória.</p>
</li>
</ol>
<p>É mais trabalhoso? Com certeza. Mas foi a forma que encontrei para garantir controle total sobre cada byte que vai para o disco. Foi uma lição e tanto logo no primeiro componente, mas é por isso que essa jornada é tão fascinante.</p>
<p>Agora, quero saber de você:</p>
<ul>
<li><p>Já teve alguma surpresa parecida com padding ou alinhamento de memória?</p>
</li>
<li><p>Se já implementou serialização/deserialização manual, qual abordagem usou? Tem alguma dica?</p>
</li>
</ul>
<p>Sinta-se a vontade para comentar!</p>
]]></content:encoded></item><item><title><![CDATA[Por que um SELECT Pode Levar Uma Eternidade? Uma Análise do Hardware]]></title><description><![CDATA[Essa semana foi MUITO mais teórica do que eu achei que seria. Em algum momento, me peguei com sentimento de falta de produtividade por não ter escrito nem uma única linha de código na semana toda. Mas, comecei a me perguntar se realmente havia sido t...]]></description><link>https://jefferson-f.hashnode.dev/por-que-um-select-pode-levar-uma-eternidade-uma-analise-do-hardware</link><guid isPermaLink="true">https://jefferson-f.hashnode.dev/por-que-um-select-pode-levar-uma-eternidade-uma-analise-do-hardware</guid><category><![CDATA[Banco de Dados]]></category><category><![CDATA[#engenharia de software]]></category><category><![CDATA[C++]]></category><category><![CDATA[performance]]></category><category><![CDATA[sistemas de baixo nivel]]></category><category><![CDATA[devlog]]></category><category><![CDATA[auroradb]]></category><dc:creator><![CDATA[Jefferson Ferreira]]></dc:creator><pubDate>Mon, 16 Jun 2025 02:03:22 GMT</pubDate><content:encoded><![CDATA[<p>Essa semana foi MUITO mais teórica do que eu achei que seria. Em algum momento, me peguei com sentimento de falta de produtividade por não ter escrito nem uma única linha de código na semana toda. Mas, comecei a me perguntar se realmente havia sido tão improdutivo assim.</p>
<p>Realmente, não codei nada do AuroraDB, mas avancei bastante na parte teórica no que diz respeito a discos magnéticos. Minha principal fonte foi o livro Database System Concepts, mais especificamente a 6° edição. Ele trata sobre o alto nivel do banco de dados, como SQL, mas do 10° capitulo em diante ele aborda o baixo nivel também e foi a partir desse capitulo que foquei o estudo ao longo dessa semana. Isso significa que vou ignorar a parte sobre alto nivel? Não, mas darei mais atenção para essa parte quando a implementação dos Parsers chegar.</p>
<h2 id="heading-disco-magnetico">Disco Magnético</h2>
<p>Agora, o momento mais teórico dos avanços da semana. O que eu estudei no que se trata do armazenamento em disco?</p>
<ul>
<li><p>Funcionamento do I/O(Entrada e saída)</p>
</li>
<li><p>Medidas de Desempenho</p>
</li>
</ul>
<h2 id="heading-funcionamento-do-io">Funcionamento do I/O</h2>
<p>Alguma vez você já se perguntou como seu computador salva os dados no seu HD? Pois bem, irei explicar resumidamente o que aprendi ao longo dessa semana no que tange esse assunto.</p>
<p>Fisicamente falando, discos são relativamente simples. Ele possui diversos pratos, com formato circular e plano. Os dois lados do disco são feitos de um material magnético e a informação fica gravada em sua superfície, sendo feitos normalmente de vidro ou metal rígido</p>
<p><a target="_blank" href="https://www.gta.ufrj.br/grad/07_1/hd/func.html"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750034666543/ce31ae6b-e201-4168-a080-dabf2c5ffcf9.jpeg" alt class="image--center mx-auto" /></a></p>
<p>Quando o disco está em uso, um motor o gira a uma velocidade constante(60, 90, 120 ou até 250 rotações por segundo). Acima da superfície do disco, mas sem encostar na mesma, há a cabeça de leitura/gravação, fixada a um braço e entender isso é importante, pois é algo essencial para entendermos o motivo para que operações de I/O em disco sejam um gargalo para as operações. O disco é dividido em trilhas e essas trilhas são divididas em setores, que é a menor unidade de informação que pode ser lida ou escrita.</p>
<p>Fazendo uma analogia, imagine uma vitrola moderna. Os <strong>pratos</strong> do HD são como os discos de vinil, girando a uma velocidade altíssima. A <strong>cabeça de leitura/gravação</strong> é como a agulha da vitrola, suspensa por um <strong>braço mecânico</strong>. E aqui está o ponto crucial: para ler uma música (um dado), a agulha precisa primeiro se mover até a trilha certa no disco e depois esperar o ponto exato da música passar por baixo dela. São esses dois movimentos físicos — a 'viagem do braço' e a 'espera pelo giro' — que criam o gargalo de performance que nós, como desenvolvedores de sistemas, precisamos entender e minimizar.</p>
<p>As operações de I/O ocorrem da seguinte forma:</p>
<ul>
<li><p><strong>Escrita:</strong> A cabeça de escrita passa bem próximo ao disco(Lembre-se, JAMAIS encostando no prato) e manipula os polos. Ela leva em conta sempre o bit imediatamente anterior para fazer a seguinte operação: Para gravar “1”, o polo é invertido(Se o bit anterior está “Norte”, ela muda esse para “Sul” e vice-versa). Para gravar “0”, o polo é mantido igual ao polo do bit anterior. Parece ser muito aleatório e arbitrária essa decisão, mas vai fazer sentido ao entendermos como funciona a leitura.</p>
</li>
<li><p><strong>Leitura:</strong> Em suma, quando há uma mudança brusca de polaridade, é gerado um pulso elétrico forte na cabeça de leitura e dessa forma, se torna possível interpretar o bit 1(afinal, a polaridade sempre vai estar invertida). E, quando não há essa inversão de polaridade, não é gerado o pulso elétrico e, por isso, se torna possível interpretar o bit 0. <strong>PS →</strong> Para o primeiro bit, é feito a leitura com base em uma série de bits de marcação que precede ele.</p>
</li>
</ul>
<h2 id="heading-medidas-de-desempenho">Medidas de Desempenho</h2>
<p><strong>Tempo de Acesso:</strong> É o tempo entre uma requisição de I/O ser emitida e o começo da transferência de dados de fato começar(Ou seja, a soma entre o <strong>seek time</strong> e a <strong>latencia rotacional</strong>). Para isso, o braço precisa se mover fisicamente até a trilha correta e então esperar que o devido setor chegue até a cabeça através da rotação do prato. O tempo necessário para o movimento do braço se chama tempo de busca(<strong>seek time</strong>) e, logicamente, ela aumenta de forma proporcional com a distância que o braço deve se mover. De acordo com o livro(PS → de 2009), tempo de busca tipicos variam entre 2 e 30 milissegundos. Para nós, pode parecer algo extremamente rápido, mas para um computador isso se traduz em uma ETERNIDADE. Para vias de comparação, uma CPU de 3.5Ghz pode executar cerca de de 3,2 milhões de operações em 1 milissegundo.</p>
<p><strong>Tempo de busca médio:</strong> É a média entre os tempos de busca. Algo interessante aqui é que, se desconsiderarmos o tempo para o braço começar a se mover, o tempo para o braço parar de se mover e que todas as trilhas possuem exatamente o mesmo número de setores, esse tempo se torna 1/3 do pior tempo possível. Agora, se formos considerar essas variáveis, o tempo médio se torna ½ do pior tempo possível.</p>
<p><strong>Tempo de latência rotacional:</strong> É o tempo que leva para o disco girar e colocar o devido setor imediatamente abaixo da cabeça. As velocidades de rotação em discos ficam entre 5400 rotações por minuto(90 rotações por segundo) até 15000 rotações por minuto(250 rotações por segundo), ou, equivalentemente, de 4 milissegundos até 11,1 milissegundos por rotação. Em média, é necessária meia rotação para que o setor correto chegue a cabeça, portanto, o tempo médio é metade do pior caso possível.</p>
<p>Omitirei as demais medidas de desempenho, pois, somente essas já são o suficiente para entendermos que operações em disco serem o gargalo de funcionamento. Entender isso é importante, pois, ao desenvolver um banco de dados nós queremos performance. Imagine se, ao tentar recuperar 500 registros de uma tabela, esse processo fosse feito incessantemente e diversas vezes no disco? Quanto tempo não iria demorar? E pior ainda, imagine que esses registros estejam fisicamente distantes uns dos outros no disco? Dado as informações passadas aqui, dá para perceber que seria uma eternidade correto?</p>
<h2 id="heading-proximos-passos">Próximos Passos</h2>
<p>O que vocês podem esperar na próxima semana?</p>
<ul>
<li>Continuar o estudo sobre técnicas de otimização ao acesso de blocos do disco. Já iniciei a leitura e anotações sobre os conceitos base, mas ainda falta ler sobre as técnicas em si.</li>
</ul>
<ul>
<li><p>Nem só de teoria vive um projeto. Pretendo implementar o começo de um <em>PageManager,</em> aplicando o conceito de RAII para fazer essa classe, cujo objetivo será: Criar, abrir e gerenciar o arquivo fisico do banco de dados</p>
</li>
<li><p>Começar a refletir sobre design, como por exemplo o layout fisico das páginas(um conceito que apresentarei semana que vem).</p>
</li>
</ul>
<h2 id="heading-contato">Contato</h2>
<p>Gostou do conteúdo ou tem alguma dúvida sobre o processo? Você pode me encontrar no <a target="_blank" href="http://www.linkedin.com/in/jefferson-ferreira-44148a219">meu LinkedIn</a></p>
<p>Ou, caso prefira, deixe seu comentário abaixo que eu responderei assim que possível!</p>
]]></content:encoded></item><item><title><![CDATA[Por que estou construindo um banco de dados do zero em C++?]]></title><description><![CDATA[Deixando o conhecimento raso para trás
Você já parou para pensar no que realmente acontece quando faz um simples SELECT? Pois é, eu também não. E essa resposta me incomodou.
O projeto AuroraBD surgiu dessa inquietação, basicamente se trata da constru...]]></description><link>https://jefferson-f.hashnode.dev/por-que-estou-construindo-um-banco-de-dados-do-zero-em-c</link><guid isPermaLink="true">https://jefferson-f.hashnode.dev/por-que-estou-construindo-um-banco-de-dados-do-zero-em-c</guid><category><![CDATA[sistemas de baixo nivel]]></category><category><![CDATA[auroradb]]></category><category><![CDATA[C++]]></category><category><![CDATA[Banco de Dados]]></category><category><![CDATA[portfolio]]></category><category><![CDATA[devlog]]></category><category><![CDATA[aprendizado]]></category><dc:creator><![CDATA[Jefferson Ferreira]]></dc:creator><pubDate>Mon, 09 Jun 2025 05:55:27 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-deixando-o-conhecimento-raso-para-tras">Deixando o conhecimento raso para trás</h1>
<p>Você já parou para pensar no que <em>realmente</em> acontece quando faz um simples SELECT? Pois é, eu também não. E essa resposta me incomodou.</p>
<p>O projeto AuroraBD surgiu dessa inquietação, basicamente se trata da construção de um banco de dados totalmente do zero. A proposta desse projeto não é fazer frente aos SGBD’s que já existem no mercado, até porque sejamos realistas, eu estou indo nessa empreitada sozinho. A proposta é construir um banco de dados para fins de aprendizado.</p>
<h2 id="heading-de-onde-surgiu-a-ideia">De onde surgiu a ideia?</h2>
<p>Mais do que um mero projeto “feito por fazer”, a ideia por trás do AuroraBD surgiu como algo além disso. Para contextualizar, a ideia veio através de um <a target="_blank" href="https://www.youtube.com/watch?v=2G0V-bVg-AY">vídeo</a> do Augusto Galego onde ele separa o que é conhecimento raso de conhecimento profundo e destaca a importância por trás de tal conhecimento aprofundado sobre o <strong>como</strong> as ferramentas funcionam. Foi nesse video que comecei a me perguntar: Eu tenho esse tipo de conhecimento? E não, eu ainda não tenho.</p>
<ul>
<li><p>Como um SELECT simples é processado?</p>
</li>
<li><p>Como as queries são otimizadas?</p>
</li>
<li><p>Como os dados são armazenados no disco?</p>
</li>
<li><p>E o famoso ACID? Como ele é de fato implementado?</p>
</li>
</ul>
<p>Foram exatamente essas perguntas, e muitas outras, que eu me propus a responder.</p>
<h2 id="heading-por-que-c">Por que C++?</h2>
<p>Bom, eu queria me propor a realmente entender um pouco mais do funcionamento de baixo nível da máquina, então pensei… Vamos ao nosso bom e velho C++. Mas admito que ele não foi minha primeira escolha, a principio eu cheguei a cogitar uma abordagem dentro do paradigma funcional, o qual eu carrego um apreço bem grande, talvez um Haskell ou OCaml, mas cheguei a conclusão de que eu queria ter a experiência de mexer de fato com o baixo nível e essas ferramentas não me permitiriam tal liberdade. Cheguei a considerar por um mísero momento Rust, mas eu ainda não enlouqueci de vez rsrs.</p>
<p>Mas, que fique claro, que o aprendizado de C++, embora vá acontecer, não é o foco principal desse projeto. O foco é de fato entender o funcionamento por trás de um banco de dados.</p>
<p>Neste blog, vou documentar cada passo, erro e aprendizado que eu tiver com esse projeto. Se você também tem curiosidade sobre esses fundamentos, te convido a me acompanhar nessa jornada.</p>
]]></content:encoded></item></channel></rss>