배달 어플 서비스 데이터를 몽고에 저장한다고 가정하고 스키마를 작성해보자

<aside> 1️⃣ 식당 컬렉션에 리뷰 데이터를 배열 필드로 저장

// DB 생성
> use delivery
// 첫번째 식당 데이터 삽입
> db.shops.insertOne({
...   _id: 1,
...   name: "Tommy Steak House",
...   desc: "Greatest Steak House Ever.",
...   phone: "000-1234-1234",
...   reviews: [
...     {
...       review_id: 1,
...       user: "James",
...       review: "So Good!!",
...       date: new Date(),
...       rating: 10
...     },
...     {
...       review_id: 2,
...       user: "Tommy",
...       review: "Not Bad.",
...       date: new Date(),
...       rating: 7
...     },
...     {
...       review_id: 3,
...       user: "Kevin",
...       review: "냠냠긋!!",
...       date: new Date(),
...       rating: 5
...     },
...   ]
... })
// 랜덤으로 두번째 식당의 리뷰 데이터를 생성할 함수 구현
> const generateRandomString = (num) => {
... 	const characters ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
... 	let result = '';
... 	const charactersLength = characters.length;
... 	for (let i = 0; i < num; i++) {
... 		result += characters.charAt(Math.floor(Math.random() * charactersLength));
... 	}
...   
... 	return result;
... }

> generateRandomString(50)
bmJcVSOWWNWRjGDIdYusORSonbXIauYnQleGkPVNrOYUnJXdmT
// 두번째 식당 데이터 삽입
> db.shops.insertOne({
... 	_id: 2,
... 	name: "Kevin's Pizza",
... 	desc: "Italian Style Pizza",
... 	phone: "000-0000-1111",
... 	reviews: []
... })

> for (i = 0; i < 800; i++){
... 	review = {
... 		review_id: i,
... 		user: generateRandomString(5),
... 		review: generateRandomString(10),
... 		date: new Date(),
... 		rating: Math.floor(Math.random() * 10)
... 	}
... 	db.shops.updateOne(
... 		{
... 			_id: 2
... 		},
... 		{
... 			$push: {
... 				reviews: review
... 			}
... 		}
... 	)
... }

// 저장한 식당 정보 확인
> db.shops.findOne({ _id: 2 })
{
  _id: 2,
  name: "Kevin's Pizza",
  desc: 'Italian Style Pizza',
  phone: '000-0000-1111',
  reviews: [     
    {
      review_id: 0,
      user: 'nXvqK',
      review: 'zpTESxycLu',
      date: ISODate("2023-05-16T16:21:48.924Z"),
      rating: 4
    },
    {
      review_id: 1,
      user: 'NJAOQ',
      review: 'HkxZaaHWGC',
      date: ISODate("2023-05-16T16:21:48.950Z"),
      rating: 2
    },
    {
      review_id: 2,
      user: 'mvFRI',
      review: 'BIllMpFCMW',
      date: ISODate("2023-05-16T16:21:48.978Z"),
      rating: 2
    },
    ...
		// 리뷰 800개
		...
	]
}
// 각 식당 도큐먼트 사이즈 계산
> Object.bsonsize(db.shops.findOne({ _id: 2 }))
71997
> Object.bsonsize(db.shops.findOne({ _id: 1 }))
380

문제점!!

고객은 여러 음식점을 클릭해서 보게 됨 → 음식점 클릭 시 해당 음식점의 정보를 조회하기 전, 내부적으로 캐시에 있는지 확인 → 캐시에 없으면 캐시에 올린 다음 document를 읽음 → Working Set이 shop collection 전체가 되어버림 → 캐시 사이즈가 shop collection보다 작으니, 중간중간 cache eviction 발생 → 이 때 사용자는 서비스의 속도가 크게 저하되는 것을 느낌

Ch010_05. 배달 어플 주문 리뷰 서비스 Modeling-1.png

✔️ 캐시 사용량이 뚝 떨어지는 구간 = cache flush가 발생하는 구간 ✔️ 계단식으로 올라가는 구간 = disc로부터 데이터를 읽어 캐시에 적재하는 구간

Ch010_05. 배달 어플 주문 리뷰 서비스 Modeling-2.png

여러 사용자가 몰리게 되면 캐시 사용량이 늘어나지 못하고, 데이터를 읽어오느라 속도가 저하됨

</aside>

<aside> 2️⃣ Subset Pattern을 사용한 모델링

Working Set을 줄이는 방법 👉 사용자의 패턴을 분석해 Document의 각 사이즈를 줄여준다

다 읽어올 필요가 없는 필드는 따로 컬렉션으로 빼서 저장 → 도큐먼트 크기를 줄임

<aside> ✅ Subset Pattern

도큐먼트의 일부를 다른 컬렉션에 한번 더 저장하도록 스키마를 디자인하는 것

</aside>

// 기존 컬렉션 삭제
> db.shops.find()
> db.shops.drop()
// shops 컬렉션에 식당 데이터 삽입
> db.shops.insertOne({
... 	_id: 2,
... 	name: "Kevin's Pizza",
... 	desc: "Italian Style Pizza",
... 	phone: "000-0000-1111",
... 	reviews: []
... })
// reviews 컬렉션에 리뷰 데이터 삽입
> for (i = 0; i < 800; i++){
... 	review = {
... 		review_id: i,
... 		user: generateRandomString(5),
... 		review: generateRandomString(10),
... 		date: new Date(),
... 		rating: Math.floor(Math.random() * 10)
... 	}
... 	db.reviews.insertOne(review)
... 	db.shops.updateOne(
... 		{
... 			_id: 2
... 		},
... 		{
... 			$push: {
... 				reviews: review
... 			}
... 		}
... 	)
... }

// 저장된 리뷰 데이터 확인
> db.reviews.find()
[
  {
    _id: ObjectId("6463b095744ecd33df4213c0"),
    review_id: 0,
    user: 'BjwFz',
    review: 'ZtcOdYFNFm',
    date: ISODate("2023-05-16T16:34:29.111Z"),
    rating: 9
  },
  {
    _id: ObjectId("6463b095744ecd33df4213c1"),
    review_id: 1,
    user: 'aHcLp',
    review: 'xEtMmSFEjM',
    date: ISODate("2023-05-16T16:34:29.181Z"),
    rating: 5
  },
  {
    _id: ObjectId("6463b095744ecd33df4213c2"),
    review_id: 2,
    user: 'onsRO',
    review: 'CPdtwfYTbJ',
    date: ISODate("2023-05-16T16:34:29.223Z"),
    rating: 8
  },
	...
]
// 식당에는 10개의 리뷰 데이터만 저장
db.shops.updateOne(
... 	{
... 		_id: 2
... 	},
... 	{
... 		$push: {
... 			reviews: {
... 				$each: [review],
... 				$slice: -10
... 			}
... 		}
... 	}
... )

// 업데이트된 식당 데이터 확인
> db.shops.find()
[
  {
    _id: 2,
    name: "Kevin's Pizza",
    desc: 'Italian Style Pizza',
    phone: '000-0000-1111',
    reviews: [
      {
        review_id: 791,
        user: 'UuXJH',
        review: 'byPvsCZJIT',
        date: ISODate("2023-05-16T16:35:00.871Z"),
        rating: 4
      },
      {
        review_id: 792,
        user: 'sogOw',
        review: 'MaokoKihND',
        date: ISODate("2023-05-16T16:35:00.910Z"),
        rating: 5
      },
      // 이하 8개 리뷰 데이터
			...
    ]
  }
]

</aside>