Skip to content

๐ŸŽ‡ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

SeungHoon Han edited this page Nov 22, 2025 · 9 revisions

1๏ธโƒฃ RDB & MongoDB ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ

image

๋ฌธ์ œ ์ƒํ™ฉ

๋ฌธ์„œ๋ฅผ ์‚ญ์ œํ•  ๋•Œ, RDB(MySQL)๊ณผ MongoDB์— ๋ถ„์‚ฐ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•จ๊ป˜ ์‚ญ์ œ๋˜์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์— ๋‹จ์ผ ํŠธ๋žœ์žญ์…˜์„ ๋ฌถ๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉฐ(ํ•˜๋‚˜์˜ @Transactional๋กœ ์ฒ˜๋ฆฌ X), ์ฒ˜๋ฆฌ ์ˆœ์„œ๋‚˜ ์‹คํŒจ ๋ณต๊ตฌ๋ฅผ ์ž˜ ์„ค๊ณ„ํ•˜์ง€ ์•Š์œผ๋ฉด ๋‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ„ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ˜„์žฌ ๊ตฌ์กฐ:

db ์ €์žฅ ๋‚ด์šฉ
RDB(JPA ๊ธฐ๋ฐ˜) Doc, Branch, Edge์™€ Save, Commit์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ (์ œ๋ชฉ, ์„ค๋ช…, ์ƒ์„ฑ/์ˆ˜์ • ์ผ์ž etc)
MongoDB SaveContent, Block, CommitsBlockSequence (Save์™€ Commit์˜ ๋ณธ๋ฌธ, Commit์˜ ๋ธ”๋ก ๋ฆฌ์ŠคํŠธ)

์ €์žฅ ํŠธ๋žœ์žญ์…˜ ์„ค๊ณ„

์ €์žฅ์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ„ํ—˜์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค:

  • Mongo ๋จผ์ € ์ €์žฅ โ†’ ์ดํ›„ RDB ์‹คํŒจ: Mongo์— ์“ฐ๋ ˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋‚จ์Œ
  • RDB ๋จผ์ € ์ €์žฅ โ†’ ์ดํ›„ Mongo ์‹คํŒจ: RDB์— ๋‚ด์šฉ์ด ์—†๋Š” ์ €์žฅ์ด ์ƒ๊น€

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ €์žฅ ๋กœ์ง์„ ๊ณ ์•ˆํ•˜์˜€๋‹ค:

โœ” MongoDB ์ €์žฅ ํ›„ โ†’ ์„ฑ๊ณต ์‹œ ID๋ฅผ ๋ฐ›์•„ โ†’ RDB์— ์ €์žฅ

์˜ˆ์‹œ ํ”„๋กœ์„ธ์Šค: RDB์˜ @Transactional ์•ˆ์—์„œ ๋‹ค์Œ ์ฒ˜๋ฆฌ ์‹คํ–‰

try {
    1. [RDB] MongoID๋ฅผ ์ œ์™ธํ•œ Save ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์šฐ์„  RDB์— ์ €์žฅ

    2. [MongoDB] SaveContent๋ฅผ MongoDB์— ์ €์žฅ โ†’ saveContentId ๋ฐ˜ํ™˜

    3. [RDB] ๋ฐ˜ํ™˜๋ฐ›์€ saveContentId๋ฅผ ๊ธฐ์กด Save(RDB)์— ์—…๋ฐ์ดํŠธ

    4. ์ตœ์ข…์ ์œผ๋กœ ๋ชจ๋“  ์ €์žฅ ์„ฑ๊ณต โ†’ ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹

} catch (์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ) {
    - [MongoDB ์ˆ˜๋™ ๋กค๋ฐฑ] ์‹คํŒจํ•œ Mongo ์ €์žฅ ๋‚ด์šฉ ์ง์ ‘ ์‚ญ์ œ (saveContentId ๋“ฑ)
    - ์˜ˆ์™ธ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ ์ ˆํ•œ CustomException ๋ฐœ์ƒ ํ›„ 
    - ์ „์ฒด ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ
}

์‚ญ์ œ ํŠธ๋žœ์žญ์…˜ ์„ค๊ณ„

์‚ญ์ œ์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์œ„ํ—˜์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค:

  • Mongo ์‚ญ์ œ ์ค‘ ์˜ˆ์™ธ โ†’ RDB๋Š” ์ด๋ฏธ ์‚ญ์ œ ์™„๋ฃŒ ์ƒํƒœ โ†’ ๋กค๋ฐฑ ๋ถˆ๊ฐ€๋กœ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๊นจ์ง

  • ๋ฐ˜๋Œ€๋กœ Mongo๋Š” ์‚ญ์ œ๋๋Š”๋ฐ RDB ํŠธ๋žœ์žญ์…˜์ด ๋กค๋ฐฑ๋œ๋‹ค๋ฉด โ†’ MongoDB์— ๊ณ ์•„ ๋ฐ์ดํ„ฐ ๋ฐœ์ƒ


์‚ญ์ œ๋Š” ์ €์žฅ๊ณผ ๋‹ฌ๋ฆฌ RDB๋งŒ ๋จผ์ € ์‚ญ์ œ๋˜๊ณ  MongoDB์— ๊ณ ์•„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ๋”๋ผ๋„ UX ๊ด€์ ์—์„œ ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฏ€๋กœ, Mongo ์‚ญ์ œ๋ฅผ RDB ํŠธ๋žœ์žญ์…˜ ์ดํ›„๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‚ญ์ œ ๋กœ์ง์„ ๊ณ ์•ˆํ•˜์˜€๋‹ค:

โœ” ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ + ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ + ์žฌ์‹œ๋„ + ๋น„๋™๊ธฐ

  1. RDB ์‚ญ์ œ
  • Doc, Branch, Commit, Save ๋“ฑ(RDB)์€ ํ•˜๋‚˜์˜ @Transactional ๋‚ด์—์„œ ์‚ญ์ œ
  • ๋™์‹œ์— ์‚ญ์ œํ•ด์•ผํ•  Mongo ๊ด€๋ จ ID๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ DocDeleteMongoIdsDto์— ๋‹ด๊ณ 
  • eventPublisher.publishEvent()๋กœ ์‚ญ์ œ ์ด๋ฒคํŠธ ๋ฐœํ–‰
  1. MongoDB ์‚ญ์ œ
  • @TransactionalEventListener(phase = AFTER_COMMIT)๋ฅผ ํ†ตํ•ด RDB ํŠธ๋žœ์žญ์…˜์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹๋œ ํ›„์—๋งŒ Mongo ์‚ญ์ œ๋ฅผ ์‹œ๋„
  • @Retryable์€ AOP ๊ธฐ๋ฐ˜์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ๋ผ์•ผ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฒคํŠธ๋ฆฌ์Šค๋„ˆ์—์„œ ์ง์ ‘ ์‚ญ์ œํ•˜์ง€ ์•Š์Œ (๋ฆฌ์Šค๋„ˆ์—์„œ ์ง์ ‘ ํ˜ธ์ถœ ์‹œ ๋™์ž‘ ์•ˆ ํ•จ)
  1. Mongo ์‚ญ์ œ: ์žฌ์‹œ๋„
  • MongoDB๋Š” Atlas ๊ธฐ์ค€ ๊ธฐ๋ณธ์ ์œผ๋กœ Replica Set์„ ์ง€์›ํ•˜๋ฏ€๋กœ, ํŠธ๋žœ์žญ์…˜ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•จ
  • MongoTransactionManager๋ฅผ @Bean์œผ๋กœ ๋“ฑ๋กํ•ด ํŠธ๋žœ์žญ์…˜ ๋ถ„๋ฆฌ
  1. ์‚ญ์ œ ์‹คํŒจ โ†’ @Recover ์ง„์ž…
  • ์‚ญ์ œ๊ฐ€ 3ํšŒ ๋ชจ๋‘ ์‹คํŒจํ•˜๋ฉด @Recover ๋ฉ”์„œ๋“œ๋กœ ์ด๋™
  • ์ด ์‹œ์ ์—๋Š” ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ง์ ‘ RDB ์ €์žฅ ์‹œ๋„ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ (no transaction is in progress)
  • @Async ๋กœ ์œ„์ž„ํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜์„ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ณ  Mongo ์‚ญ์ œ ์‹คํŒจ ๋‚ด์—ญ(MongoDeleteFailure)์„ ์ €์žฅ
  1. Mongo ์‚ญ์ œ ์‹คํŒจ ๊ธฐ๋ก ์ €์žฅ
  • @Async + @Transactional ์กฐํ•ฉ์œผ๋กœ ์ƒˆ ํŠธ๋žœ์žญ์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ RDB์— Mongo ์‚ญ์ œ ์‹คํŒจ ๋‚ด์—ญ ์ €์žฅ
  1. ์‹คํŒจ ๋‚ด์—ญ ์žฌ์‹œ๋„: ์Šค์ผ€์ค„๋Ÿฌ ๊ธฐ๋ฐ˜
  • ์ผ์ • ์ฃผ๊ธฐ(1์‹œ๊ฐ„?)๋กœ MongoDeleteFailure ๋‚ด์—ญ ์กฐํšŒ
  • ์‚ญ์ œ ์„ฑ๊ณต ์‹œ resolved = true๋กœ soft delete ์ฒ˜๋ฆฌ


2๏ธโƒฃ ์—ฐ๊ด€๊ด€๊ณ„ CASCADE ์˜ค๋ฅ˜ ๋ฐ ํ•ด๊ฒฐ

๋ฌธ์ œ ์ƒํ™ฉ

  • ๋ฌธ์„œ(Doc) ์‚ญ์ œ ์‹œ ํ•˜์œ„ Branch, Commit, Edge๋ฅผ cascade = CascadeType.ALL๋กœ ์ œ๊ฑฐํ•˜๋„๋ก ๊ตฌ์„ฑ๋˜์–ด ์žˆ์—ˆ์Œ
  • ๊ทธ๋Ÿฌ๋‚˜ Doc ์‚ญ์ œ ์‹œ Edge์—์„œ SQL Error: 1048 ๋ฐœ์ƒํ•จ

Column 'next_commit_id' cannot be null

๋‹ค์Œ์€ ๊ธฐ์กด์˜ ๊ด€๋ จ ์ฝ”๋“œ์™€ ERD์ด๋‹ค.

  • Doc ์‚ญ์ œ ๋ฉ”์„œ๋“œ (๊ธฐ์กด)
    public void delete(Long docId, Long userId) {
        User user = getUserOrThrow(userId);
        Doc doc = getDocByIdAndUserId(docId, userId);

        List<Branch> branches = doc.getBranches();
        MongoIdsDto docDeleteMongoIds = mongoIdsCollector.collectFrom(branches);

        user.removeDocument(doc);
        eventPublisher.publishEvent(docDeleteMongoIds);
  • Edge ์—”ํ‹ฐํ‹ฐ
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "document_id", nullable = false)
    private Doc doc;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "prev_commit_id", nullable = false)
    private Commit prevCommit;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "next_commit_id", nullable = false)
    private Commit nextCommit;
  • Doc ์—”ํ‹ฐํ‹ฐ
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @OneToMany(mappedBy = "doc", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private List<Branch> branches;

    @OneToMany(mappedBy = "doc", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    private List<Edge> edges;
  • ERD
image

ํ•ด๊ฒฐ

  • ์„œ๋น„์Šค ๋ ˆ์ด์–ด์—์„œ Doc ์‚ญ์ œ ์ „ Edge๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์‚ญ์ œํ•˜๋„๋ก ์ˆ˜์ •:
    public void delete(Long docId, Long userId) {
        User user = getUserOrThrow(userId);
        Doc doc = getDocByIdAndUserId(docId, userId);

        List<Edge> edges = doc.getEdges(); // ๊ฐ„์„  ์ˆ˜์ง‘
        List<Branch> branches = doc.getBranches();

        MongoIdsDto docDeleteMongoIds = mongoIdsCollector.collectFrom(branches);
        edgeRepository.deleteAll(edges); //๊ฐ„์„  ์‚ญ์ œ

        user.removeDocument(doc);
        eventPublisher.publishEvent(docDeleteMongoIds);

์›์ธ ๋ถ„์„

ํ…Œ์ŠคํŠธ

๊ธฐ์กด doc ์‚ญ์ œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ๋Š” given์— ์ฃผ์–ด์ง„ doc์— edge ์ž์ฒด๊ฐ€ ์„ธํŒ…๋˜์ง€ ์•Š์•„ ์ด ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•˜์ง€ ๋ชปํ–ˆ๋˜ ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.
ํ•ด๋‹น ์˜ค๋ฅ˜๋Š” ๋ฐฐํฌ ํ›„ api ํ…Œ์ŠคํŠธ์—์„œ ๋ฐœ๊ฒฌํ•˜๊ณ  ์œ„์™€ ๊ฐ™์ด ๋ฐ”๋กœ ์ˆ˜์ •ํ•ด ๋‹ค์‹œ ๋ฐฐํฌํ•˜์˜€์ง€๋งŒ, ๋ณด๋‹ค ์ •ํ™•ํ•œ ์›์ธ ํŒŒ์•…์„ ์œ„ํ•ด ์˜ค๋ฅ˜๋ฅผ ์žฌํ˜„ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋กœ ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•˜์˜€๋‹ค.

  • H2->MySQL ํ…Œ์ŠคํŠธ์šฉ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“  ํ›„ ๊ธฐ์กด์˜ delete() ์„œ๋น„์Šค ๋ฉ”์„œ๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ ํ˜ธ์ถœ
  • edges์— cascade = CascadeType.ALL (ํ˜น์€ REMOVE)๊ฐ€ ๊ฑธ๋ ค์žˆ์œผ๋ฉด ํ•ญ์ƒ ๋‹ค์Œ ํ…Œ์ŠคํŠธ ํ†ต๊ณผํ•จ (orphanRemoval = true ์กด์žฌ์™€ ๋ฌด๊ด€)
    @Test
    void ๋ฌธ์„œ_์‚ญ์ œ์‹œ_์—ฃ์ง€_์‚ญ์ œ์ˆœ์„œ๋กœ_FK์ œ์•ฝ์กฐ๊ฑด_์˜ˆ์™ธ๋ฐœ์ƒ() throws JsonProcessingException {
        // given
        User user = userRepository.saveAndFlush(DocTestUtils.createUser());

        Doc doc = DocTestUtils.createDocWithEdgesForDeleteTest(user, saveContentRepository,
                commitBlockSequenceRepository, blockRepository);

        docRepository.saveAndFlush(doc);
        assertThrows(DataIntegrityViolationException.class, () -> {
            docService.delete(doc.getId(), user.getId());
            docRepository.flush(); // ์—ฌ๊ธฐ์„œ MySQL์ด FK ์œ„๋ฐ˜ ์˜ˆ์™ธ ๋˜์ง
        });
    }
}
// ์ด์ „ ๋กœ๊ทธ ์ƒ๋žต

Hibernate: 
    /* update
        for io.ejangs.docsa.domain.branch.entity.Branch */update branches 
    set
        document_id=?,
        from_commit_id=?,
        leaf_commit_id=?,
        name=?,
        root_commit_id=?,
        updated_at=? 
    where
        id=?
Hibernate: 
    /* update
        for io.ejangs.docsa.domain.doc.entity.Edge */update edges 
    set
        document_id=?,
        next_commit_id=?,
        prev_commit_id=? 
    where
        id=?
2025-08-16T18:25:11.482+09:00  WARN 7556 --- [    Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1048, SQLState: 23000
2025-08-16T18:25:11.482+09:00 ERROR 7556 --- [    Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper   : Column 'next_commit_id' cannot be null

๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด, doc ์‚ญ์ œ ์ „ ๋‘ ๋ฒˆ์˜ UPDATE ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ€๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Hibernate ๋™์ž‘ ๋ฐฉ์‹

  • Hibernate๋Š” FK ๋ฌด๊ฒฐ์„ฑ์„ ์ง€ํ‚ค๊ธฐ ์œ„ํ•ด DELETE ์ด์ „์— UPDATE๋ฅผ ๋‚ ๋ฆผ
    • doc ์‚ญ์ œ ์ „ commit์„ ์‚ญ์ œํ•˜๊ธฐ ์œ„ํ•ด ๋จผ์ € ์ด commit์„ ์ฐธ์กฐํ•˜๋Š” edge์˜ FK๋ฅผ ๋Š์Œ
    • edge์˜ next_commit_id๋ฅผ null๋กœ UPDATEํ•˜๋Š” ๊ณผ์ •์—์„œ NOT NULL์ด ๊ฑธ๋ ค์žˆ๊ธฐ์— MySQL:1048 ์˜ˆ์™ธ ๋ฐœ์ƒ

๊ฒฐ๋ก 

๋ณต์žกํ•œ ์—ฐ๊ด€๊ด€๊ณ„์—์„œ๋Š” CASCADE ์— ์ „์ ์œผ๋กœ ์˜์กดํ•˜๊ธฐ๋ณด๋‹ค, Hibernate์˜ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋‚ด๋ถ€ ๋™์ž‘ ๊ฐ€๋Šฅ์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ์ผ์ • ์ˆ˜์ค€์˜ ๋ช…์‹œ์  ์ฒ˜๋ฆฌ๋ฅผ ๋ณ‘ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ ํ™•๋ณด์— ๋” ๋ฐ”๋žŒ์งํ•  ๊ฒƒ์œผ๋กœ ์ƒ๊ฐ๋œ๋‹ค.

3๏ธโƒฃ MultipleBagException

๋ฌธ์ œ ์ƒํ™ฉ

๊ทธ๋ž˜ํ”„ ์กฐํšŒ API๋Š” UI ์ขŒ์ธก ์‚ฌ์ด๋“œ๋ฐ”์— ์ปค๋ฐ‹ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ๋ฌธ์„œ ์กฐํšŒ ์‹œ๋งˆ๋‹ค ์‹คํ–‰๋œ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๋ฌธ์„œ์˜ ๋ธŒ๋žœ์น˜, ์ปค๋ฐ‹, ์ €์žฅ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€๋‹ค.

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [io.ejangs.docsa.domain.branch.entity.Branch.commits, io.ejangs.docsa.domain.doc.entity.Doc.branches]

  • ๊ทธ๋ž˜ํ”„ ์กฐํšŒ ๋ฐฉ์‹ (๊ธฐ์กด)
    • Hibermate๋Š” ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด ๋™์‹œ์— 2๊ฐœ ์ด์ƒ์˜ bag(๋งํฌ)๋ฅผ FETCH JOIN ํ•˜์ง€ ๋ชปํ•˜๋„๋ก MultipleBagFetchException(๋งํฌ) ๋ฐœ์ƒ์‹œํ‚ด
    • branches์™€ edges ๋‘ ๊ฐœ์˜ List ์ปฌ๋ ‰์…˜์„ FETCH JOIN
    • branches ๋‚ด๋ถ€์—์„œ commits๋ผ๋Š” ๋˜ ๋‹ค๋ฅธ List ์ปฌ๋ ‰์…˜๊นŒ์ง€ FETCH JOIN์„ ์‹œ๋„ํ•˜๋ฉฐ ํ•ด๋‹น ์—๋Ÿฌ ๋ฐœ์ƒ
public interface DocRepository extends JpaRepository<Doc, Long> {

    @Query("""
        SELECT DISTINCT d
        FROM Doc d
        LEFT JOIN FETCH d.branches b
        LEFT JOIN FETCH b.commits
        LEFT JOIN FETCH d.edges
        WHERE d.id = :id
    """)
    Optional<Doc> findByIdWithBranchesAndEdges(@Param("id") Long id); 

ํ•ด๊ฒฐ๊ณผ ์„ฑ๋Šฅ๋น„๊ต

ํ…Œ์ŠคํŠธ

  • ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•
    • ์ฟผ๋ฆฌ ๊ฐœ์ˆ˜, ์‹คํ–‰ ์‹œ๊ฐ„(ms), ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰(MB) ๊ธฐ๋ก
    • ํ•˜๋‚˜์˜ ๋ฌธ์„œ(doc)์— ๋Œ€ํ•ด ๋ฐ์ดํ„ฐ ํฌ๊ธฐ๋ณ„ ์กฐํšŒ ์„ฑ๋Šฅ ์ธก์ •
      • case 1) branches=2, commits=40, edges=40
      • case 2) branches=2, commits=100, edges=100
      • case 3) branches=2, commits=200, edges=200
      • case 4) branches=3, commits=1500, edges=1501
  • ํ™˜๊ฒฝ
    • MySQL ํ…Œ์ŠคํŠธ์šฉ DB
    • Hibernate SQL ๋กœ๊ทธ ๋ฐ EXPLAIN์„ ํ™œ์šฉํ•ด ์ฟผ๋ฆฌ ์ˆ˜์™€ ์‹คํ–‰ ๊ณ„ํš ํ™•์ธ

a. DTO Projection

b. @BatchSize

๊ฒฐ๊ณผ

๊ฒฐ๋ก 

Clone this wiki locally