Primrose Docs
  • Home
    • ⛓️Blockchain
      • Avalanche
        • What is AVAX?
      • Ethereum
        • Ethereum Cancun Upgrade Explained(draft)
        • go-ethereum: gas estimate
        • Blockchain Transaction Lifecycle
        • Mempool
        • Gas optimization in Solidity, Ethereum
      • Solidity DeepDive
        • Meta transaction
        • solidity: patterns
        • UUPS vs Transparent
        • Solidity Interface
        • Smart contract storage
        • ERC-2981 Contract
        • Solidity modifier
        • Solidity delete keyword
        • How To Make NFTs with On-Chain Metadata - Hardhat and JavaScript
        • How to Build "Buy Me a Coffee" DeFi dapp
        • How to Develop an NFT Smart Contract (ERC 721) with Alchemy
        • Upgradeable Contract
        • Smart Contract Verification
      • Common
        • Eigenlayer
        • MultiSig(draft)
        • Chain-Based Proof-of- Stake, BFT-Style Proof-of-Stake
        • Byzantine Fault Tolerance
        • Zero-knowledge
        • Hierarchical Deterministic Wallet
        • Maker DAO
        • Defi
        • Uniswap
        • IBC
        • Cosmos
        • Gossip Protocol
        • Tendermint
        • UTXO vs Account
        • Blockchain Layer
        • Consensus Algorithm
        • How does mining work?
        • Immutable Ledger
        • SHA256 Hash
        • Filecoin
        • IPFS - InterPlanetary File System
        • IPFS와 파일코인
        • Livepeer
        • Layer 0
      • Bitcoin
        • BIP for HD Wallet
        • P2WPKH
        • Segwit vs Native Segwit
    • 📖Languages
      • Javascript/Typescript
        • Hoisting
        • This value in Javascript
        • Execution Context
        • About Javscript
        • tsconfig.json
        • Nest js Provider
        • 'return await promise' vs 'return promise'
      • Python
        • Pythonic
        • Python: Iterable, Iterator
        • Uvicorn & Gunicorn
        • WSGI, ASGI
        • Python docstring
        • Decorator in Python
        • Namespace in Python
        • Python Method
      • Go
        • GORM+MySQL Connection Pool
        • Context in golang
        • How to sign Ethereum EIP-1559 transactions using AWS KMS
        • Mongo DB in golang(draft)
        • Golang HTTP Package
        • Panic
        • Golang new/make
        • golang container package
        • errgroup in golang
        • Generic Programming in Golang
        • Goroutine(draft)
    • 📝Database
      • MongoDB in golang
      • Nested loop join, Hash join
      • DB Query plan
      • Index
      • Optimistic Lock Pessimistic Lock
    • 💻Computer Science
      • N+1 query in go
      • Web server 를 구성할 때 Thread, Process 개수를 어떻게 정할 것인가?
      • CAP
      • Socket programming
      • DNS, IP
      • URL, URI
      • TLS과 SSL
      • Caching(draft)
      • Building Microservices: Micro Service 5 Deploy Principle
      • Red Black Tree
      • AOP
      • Distributed Lock
      • VPC
      • Docker
      • All about Session and JWT
      • Closure
      • Singleton Pattern
      • TCP 3 way handshake & 4 way handshake
      • Race Condition
      • Process Address Space 
      • Call by value, Call by reference, Call by assignment
      • Zookeeper, ETCD
      • URL Shortening
      • Raft consensus
      • Sharding, Partitioning
    • 📒ETC
      • K8S SIGTERM
      • SQS
      • Git Branch Strategy: Ship / Show / Ask
      • Kafka
      • Redis Data Types
      • CI/CD
      • How does Google design APIs?
      • Minishell (42 cursus)
      • Coroutine & Subroutine
      • Redis
Powered by GitBook
On this page
  • Go에서 N+1 Query 문제 방지 방법
  • Eager Loading 사용
  • Join을 사용
  1. Home
  2. Computer Science

N+1 query in go

N+1, golang

N+1 Query 문제는 데이터베이스 쿼리를 비효율적으로 수행하는 패턴을 말한다.

주로 ORM(Object-Relational Mapping) 프레임워크를 사용할 때 발생하는데, 이는 N개의 레코드에 대해 각각 추가적인 쿼리를 실행하여 데이터를 가져오는 상황을 뜻한다.

이 문제는 데이터 크기에 따라 유의미한 성능 저하를 초래할 수 있다.

N+1 Query 문제를 이해하기 위해 예시를 들어보겠다.

가령, 블로그 포스트와 각각의 포스트에 속한 댓글을 가져오는 상황을 생각해보자.

  1. 먼저, 모든 블로그 포스트를 가져오는 쿼리를 실행한다:

    SELECT * FROM posts;
  2. 각 포스트에 대해 해당하는 댓글을 가져오는 추가 쿼리를 N번 실행한다:

    SELECT * FROM comments WHERE post_id = 1;
    SELECT * FROM comments WHERE post_id = 2;
    ...
    SELECT * FROM comments WHERE post_id = N;

이 경우, 총 N+1개의 쿼리가 실행되는데, 이는 데이터베이스 성능에 큰 부담을 줄 수 있다.

Go에서 N+1 Query 문제 방지 방법

Go 언어에서 N+1 Query 문제를 방지하기 위해 주로 ORM이나 SQL 빌더를 사용하는데, 대표적인 라이브러리로는 gorm, sqlx, ent 등이 있다. 여기서는 gorm을 사용한 해결 방법을 설명한다.

Eager Loading 사용

Eager Loading을 사용하면 연관된 데이터를 한 번의 쿼리로 함께 가져올 수 있다. gorm에서는 Preload 메서드를 사용하여 이를 구현할 수 있다.

type Post struct {
    ID       uint
    Title    string
    Comments []Comment
}

type Comment struct {
    ID     uint
    PostID uint
    Body   string
}

// Preload 사용하여 한 번의 쿼리로 모든 데이터를 가져온다
var posts []Post
db.Preload("Comments").Find(&posts)

이 코드에서는 Preload("Comments")를 사용하여 모든 포스트와 해당하는 댓글들을 한 번의 쿼리로 가져온다. 이렇게 하면 N+1 Query 문제를 피할 수 있다.

다만 Eager Loading 은 복잡한 연관 관계가 많은 경우 쿼리가 복잡해지고 성능에 안좋은 영향을 줄 수 있다.

Join을 사용

SQL Join을 직접 사용하여 데이터를 가져오는 방법도 있다. 이는 SQL 쿼리를 최적화할 수 있는 방법이다.

var results []struct {
    PostID    uint
    PostTitle string
    CommentID uint
    CommentBody string
}

db.Table("posts").
    Select("posts.id as post_id, posts.title as post_title, comments.id as comment_id, comments.body as comment_body").
    Joins("left join comments on comments.post_id = posts.id").
    Scan(&results)

이 코드에서는 left join을 사용하여 포스트와 댓글을 함께 가져온다.

이를 통해 필요한 데이터를 한 번의 쿼리로 가져올 수 있어 N+1 Query 문제를 방지할 수 있다.

대신 위와 같이 Raw 쿼리로 작성하게 되는 경우는 유지보수성이 낮아질 수 있으니 쿼리 빌더를 사용하는 것이 좋겠다.


N+1 Query 문제는 성능을 크게 저하시킬 수 있는 패턴으로, Eager Loading, Join, Batch Query 등의 기법을 통해 이를 방지할 수 있다.

이러한 기법들을 적절히 활용하면 데이터베이스 접근의 효율성을 높이고 애플리케이션의 성능을 최적화할 수 있다.

PreviousComputer ScienceNextWeb server 를 구성할 때 Thread, Process 개수를 어떻게 정할 것인가?

Last updated 11 months ago

💻