Introdução ao projeto
- A NewCodes é um serviço de curadoria de blogs técnicos de empresas
- Arquitetura Spring Boot + PostgreSQL
- Implementação de autocomplete de busca: recomendações baseadas em Term, busca com decomposição de jamo, busca por consoantes iniciais, recomendação de páginas de empresas
Descoberta de problemas de desempenho
- Acúmulo de 110 mil dados na tabela Term
- O tempo de resposta da API aumentou para mais de 1000ms
- Meta: responder em até 100ms
1ª tentativa: adicionar índices (1000ms → 700ms)
- Criação de índice para otimizar busca por prefixo com LIKE usando
varchar_pattern_ops
- Criação de índice com a opção
CONCURRENTLY, sem interrupção do serviço
- Aplicação de índices nas colunas term, decomposed_term e chosung
2ª tentativa: índice com função LOWER (700ms → 110ms)
- Identificação de problema de full scan causado pelo uso da função
LOWER()
- Criação de índice baseado em função (Functional Index)
- Reestruturação do índice no formato
LOWER(nome_da_coluna) varchar_pattern_ops
3ª tentativa: JOIN → EXISTS (110ms → 100ms)
- O INNER JOIN entre Corporation e Article era um gargalo de desempenho
- Alteração para subconsulta EXISTS, reduzindo o escopo de varredura
- Otimização para verificar apenas a “existência de dados”
4ª tentativa: desnormalização e covering index (100ms → 90ms)
- Adição da coluna
total_frequency para remover operações de agregação
- Substituição de operações GROUP BY e SUM por valores pré-calculados
- Redução de I/O com covering index
- Inclusão de term e total_frequency no índice com a cláusula
INCLUDE
5ª tentativa: JDBC Template (90ms → 80ms)
- Remoção do overhead de JPA/Hibernate
- Execução direta de consultas com JDBC Template
- Em consultas simples, omitir a camada ORM foi eficaz
Resolução de problema de Rate Limiting no Nginx
- Configuração inicial: limite de 2 vezes por segundo, burst 10
- Ocorrência de falha em requisições devido ao debouncing de 100ms
- Melhoria: alteração para 10 vezes por segundo, burst 20
- Mudança do status code de 444 → 429
Redução do tamanho dos dados de resposta
- Remoção dos nomes de campos JSON e mudança para resposta baseada em array
- Distinção de tipos por número (0: Corporation, 1: Theme, 2: Term)
- Redução do tempo de transmissão na rede
Processamento paralelo com CompletableFuture
- Execução simultânea e independente das consultas de Corporation, Theme e Term
- Em comparação com a execução sequencial, consome apenas o tempo máximo de resposta
- Adição de ExecutorService e tratamento de exceções
Resultado final
- Inicialmente 1000ms → finalmente 80ms (servidor de desenvolvimento), 40ms (servidor de produção)
- Melhoria de desempenho de cerca de 90%
Principais aprendizados
- Importância de definir claramente o problema e a direção
- Equilíbrio entre uso de IA e revisão do desenvolvedor
- Necessidade de projetar com a arquitetura completa em mente
- Escolha do tipo de índice: simples/composto/covering index
- Atenção à invalidação de índice ao usar funções
- Entendimento do funcionamento interno do JPA
- Análise do plano de execução de consultas com EXPLAIN
Direções para melhorias futuras
- Uso da estrutura de dados Trie
- Cache de termos pesquisados com frequência
- Uso de CDN (em caso de serviço global)
27 comentários
Sou um dos administradores.
No momento, a discussão nos comentários está se acalorando ao se misturar, de forma complexa, com aspectos que não têm relação com o conteúdo técnico da publicação, então deixo este aviso.
Discussões técnicas e feedback são sempre bem-vindos.
As opiniões podem ser diversas, mas, ao escrever comentários, peço que mantenham a cortesia básica com os outros e foquem em uma discussão centrada na lógica; em vez de se concentrar em indivíduos ou em seu histórico, espero que o foco esteja no conteúdo desta publicação em si.
Por favor, revisem mais uma vez o guia para comentários em Como usar o site.
Como referência, em relação às publicações sinalizadas, já estamos tomando medidas no sistema e mantendo os registros necessários, e continuaremos aprimorando continuamente as políticas de moderação e o sistema relacionado.
Além disso, se tiverem opiniões ou feedback sobre a moderação, sintam-se à vontade para entrar em contato por e-mail.
Sim, entendi.
Acho que o tom dos comentários está meio estranho. Será que, em comparação com quando o post foi publicado pela primeira vez, o título ou o conteúdo foram alterados? Não me parece estranho, por si só, que um texto desse tipo tenha sido publicado.
Também há opiniões como "Esse tipo de texto não seria publicado em um blog técnico corporativo.", mas definir a meta de desempenho e repetir melhorias para alcançá-la é algo que aparece com frequência em blogs técnicos de empresas.
Por exemplo, entre os textos que vi anteriormente, há o seguinte.
Concordo com o que você disse.
Mas acredito que a situação atual seja uma crítica à postura do usuário que fez abuso. Ficar discutindo o nível da postagem e coisas do tipo parece fugir do ponto principal.
As reações negativas provavelmente se devem ao histórico de abuso do autor. Acho desnecessário ficar acrescentando comentários sobre como é o conteúdo.
É justamente essa parte que me deixa em dúvida.
Se todos tivessem reagido com algo como "Essa pessoa é um abusador!", eu provavelmente não teria feito esse comentário. Mas, como a maioria dos comentários discutia o nível de qualidade do texto original, é isso que me parece estranho. Se o problema era realmente o abuso, então ficar falando da qualidade do texto não seria um comentário adicional completamente desnecessário?
Concordo.
Até mesmo ao ver a frase "Comecei a fazer isso sozinho no exército desde maio de 2025!", dá para perceber que nem é um blog corporativo...
Claro, é difícil negar que o conteúdo compartilhado seja "um trabalho que obviamente precisa ser feito",
e também é verdade que "não há nenhuma história sobre diferencial e o conteúdo está no nível de um estudo pessoal",
mas o GeekNews era um espaço com um clima de que esse tipo de coisa não deveria ser compartilhado?
Não pode compartilhar a experiência de ter feito trabalhos que naturalmente precisam ser feitos?
Experiências sem diferencial não podem ser compartilhadas?
Experiências em nível de estudo pessoal não podem ser compartilhadas?
Talvez tenha parecido isso mesmo. Eu deixei o comentário abaixo por dois motivos. Primeiro, o post publicado antes no Show GN foi marcado por abuso. Um dia depois, a pessoa resumiu o próprio texto do velog e publicou um novo post, mas a questão é: o conteúdo em si realmente era algo digno de ser publicado? Se me perguntarem se dava para ver as reflexões e o esforço do próprio autor, como outras pessoas também apontaram, busca é uma área em que a tecnologia já está razoavelmente consolidada de forma geral; então, mais do que a parte técnica, o conteúdo do blog me pareceu contornar o assunto e acabar funcionando como uma extensão da divulgação do próprio serviço, e foi por isso que deixei aquele comentário.
Parece que o principal fator foi terem pegado implicância por causa da flag de abuso.
Como o próprio conteúdo do blog já é uma extensão da divulgação do serviço deles, e isso também vale para blogs técnicos de outras empresas, acho que rejeitar apenas por isso é um critério bastante sensível.
E, quanto à questão de este texto mostrar ou não as preocupações e o esforço do próprio autor, me parece que, ao ter a hipótese de que aplicar um índice melhoraria o desempenho negada, ele examinou o plano de execução e, considerando a lógica de negócio, foi ajustando a query e o esquema, repetindo melhorias até alcançar a meta de desempenho, o que certamente conta como preocupação e esforço suficientes.
Também fui ao blog ler o texto original. Senti uma certa discrepância entre o título e o conteúdo de fato. As funcionalidades implementadas e a direção das melhorias já existem e já foram implementadas/incorporadas em vários projetos open source; o que você fez foi sofisticar a busca que antes havia sido implementada de forma inicial e simples no seu próprio serviço, mas pelo título passa a impressão de que houve uma grande melhoria de algoritmo... O post anterior também foi sinalizado como promoção, então acho que talvez valha a pena pensar um pouco mais na forma de escrever.
Se isso deu essa impressão, peço desculpas. Acho que cada pessoa tem expectativas diferentes sobre o conteúdo ao ver o título. Ainda assim, o correto é tentar escrever títulos mais claros, para que o conteúdo esperado seja o mais parecido possível para todos. Vou tomar mais cuidado com isso.
Além disso, peço que vejam este texto separadamente do anterior. No texto anterior, tentei dar upvote usando duas contas que não estavam em uso e, por isso, ele foi sinalizado. Isso foi claramente um erro meu, e gostaria de esclarecer que não foi um problema com o texto em si.
Fico curioso se vocês chegaram a considerar usar um índice GIN em vez de um índice
lower(). Já que de qualquer forma vocês usaram SQL bruto comJdbcTemplate, que tal aproveitar e considerar FTS também?O método assíncrono com
CompletableFuture.supplyAsync()também, se não for especificado umExecutorServiceseparado, usa ocommonPooldoForkJoinPool, entãose o número de acessos simultâneos crescer a ponto de lotar o
commonPoolusado no lugar da thread de requisição (número de núcleos da CPU - 1), pode ser que ele não consiga dar conta.Essa parte provavelmente pode ser resolvida de forma mais limpa mudando para um modelo reativo ou atualizando a versão da JVM para adotar threads virtuais.
Olá! Antes de tudo, muito obrigado mesmo por deixar um comentário com feedback.
Concluímos que um índice GIN não é necessário nesse caso! Atualmente, na API de recomendação de autocompletar de termos de busca, precisamos apenas do próprio
term. Não precisamos saber a quais artigos essetermpertence.Por outro lado, na API de busca, estamos usando um índice semelhante ao GIN. Utilizamos o paradeDB, uma extensão do Postgres, para usar um índice BM25.
Isso não aparece em detalhes no post, mas no momento estamos usando um
ExecutorServicedefinido separadamente. Ainda assim, como você mencionou, também vamos considerar no futuro uma abordagem reativa ou threads virtuais!!Peço desculpas em relação a este texto e ao texto anterior.
Foi um erro meu, além de uma atitude tola, ter dado upvote usando 2 contas que eu não usava.
Como era um projeto no qual eu vinha me dedicando há muito tempo, agi de forma errada na esperança de que ele chamasse mais atenção das pessoas.
Mas, mesmo que eu tivesse esse motivo, isso não justifica a violação das regras.
Por causa dos upvotes que dei de forma irresponsável, o texto de alguém provavelmente teve que perder posição, e isso deve ter prejudicado a ordem do site.
Além disso, acho que publicar outro texto novo logo no dia seguinte de ter sido marcado como flagged pode muito bem ter dado margem a mal-entendidos.
Sendo sincero, como não havia nenhuma restrição específica de uso do site, pensei se talvez fosse permitido publicar de novo imediatamente. Isso foi falta de reflexão da minha parte.
Agora, pensando melhor, independentemente de haver punição ou não, eu deveria ter me contido.
Tentando pensar pelo lado oposto, se alguém tivesse feito exatamente a mesma coisa em um espaço de que eu gosto, eu também não veria isso com bons olhos.
Desde que comecei a desenvolver, sempre pratiquei a ideia de que "compartilhar" era algo incondicionalmente bom.
Mas, com esse episódio, percebi que existem espaços certos para compartilhar e momentos certos para fazê-lo.
Também senti que, se eu entrei recentemente em um espaço pelo qual outras pessoas têm carinho e interesse, o certo é, antes de tudo, respeitar os outros ao máximo.
Por isso, eu deveria ter lido primeiro as regras de uso e observado o clima do site, evitando qualquer comportamento que fosse contra isso.
Reconheço meu erro e apresento esta explicação, ainda que dessa forma.
Da próxima vez, vou procurar usar o site de maneira mais madura.
:+1:
Li com bastante interesse. No começo pensei: será que é só um texto dizendo que colocaram um índice? Mas foi legal ver que vocês não pararam nisso, tentaram vários métodos diferentes e compartilharam o processo. No futuro, como você mencionou, seria interessante testar uma trie, ou até melhorar dando um peso maior para termos em tendência que tiveram muitas buscas recentes, por exemplo!
Uma coisa que me deixou curioso é que vocês consultavam tanto
termquantodecomposed termcom condiçãoor, mas fiquei pensando se não bastaria consultar sódecomposed term, já que ele seria um superconjunto. Mesmo que a query seja “neng”, ela seria decomposta como “nieong”, então imagino que encontraria “Naver”. E, no caso de otermreal ser “neng”, também deveria ser encontrado da mesma forma.Como você mencionou, consultar apenas com o decomposed term já é suficiente. Como isso passou a existir,
termera uma condição desnecessária, mas acho que não considerei isso. Graças a você, corrigi. Obrigado!Não deixa de dar a sensação de que isso é meio... deslocado, como algo que não é exatamente o tipo de conteúdo que as pessoas que vêm ao GeekNews querem ver, né?
Realmente não entendo o que haveria de tão grandioso nisso.
Não estamos falando de 1 milhão ou 10 milhões de registros, e o próprio título já deixa claro que a escala é de pouco mais de 100 mil. Nesse contexto, não parece meio estranho esperar algum tipo de otimização mirabolante em vez de simplesmente fazer bem o básico? Fico curioso para saber o que exatamente estavam esperando de tão grandioso.
Não sei se um texto mostrando, passo a passo, como ajustar o básico com cuidado, partindo de um estado em que o banco de dados não estava devidamente otimizado, deveria ser tratado como se fosse puro clickbait. Na minha opinião, esse tipo de clima excludente de que "se não for a melhor coisa do mundo, então é errado até postar aqui" é prejudicial.
> Não é um pouco estranho, por si só, esperar algum tipo de otimização grandiosa em vez de se ater ao básico ali?
As pessoas enxergam até onde conhecem.
Para facilitar o entendimento, estou pensando agora no exemplo de criar um fórum.
Criar um fórum foi algo muito recomendado para desenvolvedores iniciantes como primeiro portfólio.
Pensando de forma simples, é fácil.
Você publica um post e, se ele aparecer na lista, acabou. Se for feito de forma realmente simples, talvez nem precise de um banco de dados no backend.
Mas as pessoas enxergam até onde conhecem.
Se você for levar um fórum a sério, começa pelo banco de dados e vai para função de comentários, login, e, evoluindo o login, autenticação OAuth ou JWT; mesmo numa função simples de escrever posts, entram anexos de imagem e vídeo, suporte a formatação de texto e segurança, começando por XSS.
Mesmo com o mesmo texto, a imagem que cada leitor forma pode variar bastante de acordo com o conhecimento prévio que ele tem.
Eu entendi que tipo de autocompletar o kunggom imaginou ao ver o título.
Mas cada leitor viveu uma vida diferente e, no fim, as funcionalidades que imaginaram provavelmente serão bem diferentes entre si.
Também entendo com que intenção você escreveu o comentário.
Eu também concordo com essa opinião, mas acredito que você sabe que isso não combina muito com a situação da pessoa que escreveu o texto agora.
Você poderia explicar um pouco melhor a parte de que isso seria “algo que não combina muito com a situação de quem escreveu o texto”?
Pelo texto original, está explicitado que o projeto em questão é “um projeto pessoal, e não um serviço com tráfego enorme ou que precise gerar receita”. Portanto, se houvesse algum tipo de otimização mais grandiosa, seria possível supor que isso viria apenas de curiosidade pessoal ou algo do tipo, e não por uma razão prática. Assim, não me parece estranho que esse nível de esforço técnico não tenha sido empregado, e o que eu não consigo entender é por que, entre algumas pessoas, a reação foi tão fortemente negativa. Até porque os números citados no título não entram em contradição com o conteúdo do texto.
Não acho que o kunggom seja um desenvolvedor sem conhecimento de base a ponto de não conseguir entender nem a analogia com quadro de avisos que eu mencionei.
Acho que a diferença de opinião aqui vem, antes de tudo, da percepção sobre usuários que fazem abuso, então vou falar pela última vez sobre isso.
O que eu esperava era busca semântica.
Busca semântica não é um tema nada fora da realidade no atual boom de IA, e acredito que você saiba que mesmo uma pessoa sozinha consegue implementar isso com tranquilidade.
Desde o início, quando clicamos em um título, não é como se fizéssemos isso entendendo o contexto de quem escreveu o texto, mas estou dizendo que, mesmo que não seja um serviço com tráfego enorme ou que precise gerar receita, isso ainda é algo perfeitamente viável de implementar.
E eu estou falando apenas do título agora.
> Segundo o texto original, esse projeto ~
Estou falando da imagem que imaginei ao ver o título, então essa parte não é necessária na nossa conversa agora.
> A situação da pessoa que escreveu o texto agora
Acho que já entendi como o kunggom vê a pessoa que escreveu o texto. Parece que você a enxerga como um desenvolvedor iniciante, alguém que precisa ser compreendido independentemente do que escreva.
Como eu disse ontem, se a pessoa não tivesse sido marcada, eu também concordaria com isso, mas a partir do momento em que manipulou recomendações do próprio texto, essa discussão perde o sentido.
> E se você realmente acha que é um problema uma pessoa flagrada por abuso voltar a escrever no dia seguinte
Foi isso que você disse acima.
Se mesmo depois de ser marcada a pessoa tem liberdade para escrever, também existe liberdade para criticá-la por isso.
Como você mesmo disse, se acha problemático falar de forma um pouco mais rigorosa nos comentários com alguém que foi marcado, em vez de apontar isso nos comentários, sugiro que leve essa questão à moderação.
Então você quis dizer que estava pensando em compartilhar uma experiência de otimização para um tipo de serviço que recomenda candidatos de busca de forma inteligente, mesmo quando a entrada é feita de qualquer jeito, usando embeddings e coisas do tipo.
Se fosse isso, talvez fosse algo mais compatível com o que se esperaria de um título como "recomendação de termos de busca", mas entendo o que você esperava.
Entendo a posição crítica em relação ao abuso, mas a última frase muda discretamente o ponto central, saindo de uma falta de maturidade técnica para um problema de violação das regras da comunidade, e isso me decepciona um pouco por parecer uma tentativa desajeitada de inverter o argumento com um "vou refutar você com a sua própria lógica e suas próprias afirmações".
Se você tivesse criticado apenas o abuso desde o começo, sua argumentação teria sido mais convincente. Se realmente fosse esse o caso, então mesmo que o texto de fato contivesse o que você afirma ter esperado originalmente, como otimização de embeddings vetoriais em DBMS modernos, ou mesmo que tivesse sido usado um "título humilde", ainda assim teria surgido uma reação hostil em relação ao histórico recente de abuso do autor, e quanto a isso eu não teria nenhuma objeção. Porque isso tem pouca relação com o conteúdo técnico em si.
O que eu contesto é por que isso se manifesta na forma de apontamentos sobre "falta de maturidade técnica". Se o abuso é um comportamento inaceitável, então naturalmente ele merece reprovação independentemente do conteúdo. Nesse caso, haveria motivo para incluir críticas ao conteúdo? No entanto, os comentários aqui, sem exceção, passam majoritariamente a nuance de que o conteúdo é tecnicamente imaturo. O próprio crawler também fez para mim uma analogia com algo como "fazer um fórum de iniciante em desenvolvimento" e disse "você vê tanto quanto sabe", não foi? Se é assim, não deveríamos suspeitar que o abuso seja, na verdade, um ponto secundário, acrescentado depois?
Pelo teor do seu comentário, se o texto original realmente fosse do tipo de conteúdo que o crawler esperava, você ainda assim o teria criticado apenas pelo abuso em si. Ou será que, mesmo tendo havido abuso, quando o conteúdo agrada ao seu gosto você não sente necessidade de exercer a liberdade de falar "com rigor"?
Por isso pergunto novamente: se você realmente está criticando o autor original por causa do abuso, por que escreveu um comentário focado principalmente em críticas ao conteúdo, e não ao abuso em si?
Há alguma base para a parte de que eu teria “atrapalhado a liberdade de expressão de outras pessoas”? Isso eu realmente não consigo entender. Não é como se eu tivesse algum poder de moderação neste espaço para impedir que outros escrevam posts ou comentários. Na prática, você mesmo conseguiu escrever um comentário longo assim sem problema algum.
Se simplesmente deixar um comentário contra a opinião de outra pessoa significa impedir a liberdade de expressão alheia, então seria preciso considerar que você também está agora violando a minha liberdade de expressão. Se não for assim, então isso não seria logicamente dois pesos e duas medidas?
E, como o próprio crawler reconheceu, no fim das contas, se era abuso ou não não era algo importante nos critérios de julgamento.
Isso não contradiz a afirmação “como eu disse ontem, se não tivesse sido flagged, eu também concordaria com isso, mas a partir do momento em que houve manipulação de recomendações no post escrito, essa discussão perde o sentido”?
Parece que o foco da discussão e a alegação continuam mudando, então eu gostaria que você mantivesse uma linha só.
Eu considero que o ato de condenar em grupo — ou de dar essa impressão — textos que têm relação suficiente com o tema da comunidade e não são AI slop, dizendo algo como “este texto é abaixo do nível adequado para a nossa comunidade”, talvez seja ainda mais prejudicial ao crescimento e à manutenção da comunidade do que manipulação de votos. Isso porque pode fazer com que a imagem externa dessa comunidade pareça excludente e, por causa disso, atrapalhar bastante a entrada de potenciais novos usuários.
Claro, isso não quer dizer que não se deva fazer críticas. Mas, no mínimo, acho esse clima um pouco estranho. Em comum, havia apenas a decepção de que o conteúdo não era aquilo que cada um esperava; análises e feedbacks que parecessem construtivos foram minoria.
E, se vocês realmente acham problemático que uma pessoa flagrada abusando do sistema volte a postar no dia seguinte, que tal aproveitar esta oportunidade para sugerir formalmente à moderação a inclusão de uma regra relacionada a isso? Pelo que sei, já existe algum nível de sanção, mas parece que vocês consideram isso insuficiente.
Eu, na verdade, não entendo. Você está vendo isso como se tivesse sido postado para criticar de forma organizada e em grupo? A sua própria alegação é que parece estar tratando de forma negativa as opiniões que cada indivíduo pode expressar. Daqui para frente, espero que você também olhe com a mesma cordialidade para textos no nível de um diário de desenvolvimento (defini uma meta para melhorar o desenho de estrela com
printfe usei umforpara melhorar!).Parece que você também costumava se promover bastante em galerias de outros sites, como em Yugnyong-ui Sidae e Web Samguk Mussangjeon.
Vendo a postura de apresentar trabalhos inacabados só para experimentar a reação e depois abandonar facilmente o projeto, fico pensando no que isso é tão diferente deste post... por que aplicar um padrão tão rígido aos outros?
Será que no DC pode fazer o que quiser porque é um lugar onde crianças brincam, mas no GeekNews, por ser um lugar de que você gosta, não consegue tolerar que outra pessoa o "suje"?
Não estou tentando fazer um argumento muito lógico, só estou comentando porque achei curioso esse dois pesos e duas medidas, então qualquer resposta sua contra isso deve estar certa. Força aí no abuso.
Otimizaram a API de autocomplete de busca, mas com um título desses o conteúdo no fim é só otimização de consulta no banco de dados.
Se for comercial, isso já dá para fazer bem só com otimização no Oracle, e serviços de autocomplete já existem aos montes. Não falam nem do diferencial, é um conteúdo no nível de exercício pessoal.
É meio desagradável de ver.