몽고DB - Dirty Cache 문제 해결

2025. 2. 21. 00:09IT/몽고DB

MongoDB에서 Dirty Cache(더티 캐시) 문제는 일반적으로 데이터가 캐시에 남아 있는 동안 변경되었거나, 동기화되지 않아 최신 데이터를 반영하지 못하는 상황을 의미합니다. 이를 해결하기 위한 방법은 다음과 같습니다.



1. Write Concern 설정 강화


MongoDB는 기본적으로 비동기적인 쓰기를 지원하는데, 데이터 일관성을 높이기 위해 Write Concern을 강화하면 Dirty Cache 문제를 줄일 수 있습니다.

db.collection.insertOne(
  { name: "John", age: 30 },
  { writeConcern: { w: "majority", j: true } }
);

• w: "majority": 다수의 노드에서 데이터가 반영될 때까지 대기
• j: true: 디스크에 커밋될 때까지 대기


2. Read Concern 설정 강화

Dirty Read를 방지하기 위해 Read Concern을 설정할 수 있습니다.

db.collection.findOne(
  { name: "John" },
  { readConcern: { level: "majority" } }
);

• local: 기본값, 가장 빠르지만 Dirty Read 가능
• majority: 다수의 노드에서 확인된 데이터만 읽음
• linearizable: 가장 강력한 읽기 일관성 보장 (성능 저하 가능)


3. 캐시 무효화 전략 적용


1) TTL 인덱스 사용 : 특정 시간이 지나면 캐시 데이터를 자동으로 삭제하는 방법입니다.

db.collection.createIndex({ updatedAt: 1 }, { expireAfterSeconds: 60 });


2) 캐시 키 기반 무효화 : 데이터가 변경될 때 Redis나 메모리 캐시의 해당 키를 삭제하는 방식입니다.

redis.del("user:1234"); // 데이터 변경 시 캐시 삭제


3) Versioning 적용 : 문서에 버전 필드를 추가하여 최신 버전을 유지할 수 있습니다.

db.collection.updateOne(
  { _id: ObjectId("...") },
  { $set: { data: "new value", version: 2 } }
);


4) Secondary Read 문제 해결
 - Replica Set 환경에서 secondary 노드를 읽을 때 데이터 동기화가 지연될 수 있습니다.
 - 이 경우 readPreference를 조정하면 해결할 수 있습니다.

db.collection.find().readPref("primaryPreferred");

• primary: 기본 노드에서만 읽음 (가장 강력한 일관성)
• primaryPreferred: 기본 노드가 사용 가능하면 기본 노드에서 읽고, 그렇지 않으면 Secondary 사용
• secondary: Secondary 노드에서만 읽음 (더티 캐시 가능성 있음)

분산 캐시 시스템 활용


MongoDB 단독 캐시보다는 Redis, Memcached 같은 별도 캐시 시스템을 활용하여 데이터 일관성을 유지하는 방법도 있습니다.
  • 데이터가 변경되면 MongoDB + Redis 모두 업데이트 또는 삭제
  • Write-Through 캐시 패턴 적용: 데이터를 업데이트하면 먼저 캐시에 반영 후 MongoDB 업데이트
  • Cache Aside 패턴 적용: 캐시 데이터가 없을 경우만 DB에서 조회 후 캐시에 저장


트랜잭션 사용 (멀티 문서 작업)


MongoDB는 단일 문서 내 원자성을 보장하지만, 다중 문서 변경 시 Dirty Cache 문제가 발생할 수 있습니다.
이를 방지하려면 트랜잭션을 사용해야 합니다.

const session = db.getMongo().startSession();
session.startTransaction();

try {
  session.getDatabase("mydb").collection("users").updateOne(
    { _id: ObjectId("...") },
    { $set: { name: "Updated User" } }
  );

  session.commitTransaction();
} catch (error) {
  session.abortTransaction();
}

 

반응형