사전 dm-cache
구현체

dm-cache

gabury1고친 사람 github-actions[bot]

dm-cache 는 느린 디스크 앞에 그보다 빠른 디스크를 덧대어, 자주 쓰는 데이터를 빠른 쪽에서 처리하게 해 줍니다. 리눅스 커널에 들어 있는 기능이라 따로 띄우는 프로그램이 없습니다. 그 위의 파일 시스템은 두 디스크를 합친 디스크 한 대만 봅니다.

쉽고 빠른 이해
  • 무슨 일을 하나 — 용량이 큰 하드 디스크 앞에 작은 고속 디스크를 붙입니다. 자주 찾는 데이터만 고속 디스크에 올려 둡니다. 오늘 들어온 주문은 고속 디스크에서 읽힙니다. 몇 년 묵은 주문은 하드 디스크에 남습니다.
  • 왜 있나 — 고속 디스크는 같은 용량이면 값이 훨씬 비쌉니다. 날마다 손이 가는 데이터는 대개 전체의 일부라서, 그 일부만 고속 디스크에 두면 적은 돈으로 대부분의 요청을 빨리 처리합니다.
  • 어떻게 도나
    1. 요청이 오면 그 데이터 조각이 고속 디스크에 올라와 있는지 봅니다
    2. 올라와 있으면 고속 디스크에서, 없으면 하드 디스크에서 처리합니다
    3. 자주 찾는 조각은 뒤에서 고속 디스크로 올리고, 뜸해진 조각은 내립니다
  • 대가 — 고속 디스크에 더해, 어느 데이터가 고속 디스크에 있는지 적어 두는 작은 공간을 따로 마련해야 합니다. 쓰기를 고속 디스크에만 먼저 해 두는 방식에서는 그 디스크가 고장 나면 최근 쓰기를 잃습니다.
  • 안 맞는 때 — 요청이 데이터 전체에 고르게 흩어지면 올려 둘 데이터를 못 고릅니다. 데이터가 고속 디스크에 다 들어가면 캐시 없이 고속 디스크에 바로 두면 됩니다.

상세

dm-cache 의 dm 은 device mapper(디바이스 매퍼)의 줄임입니다. 리눅스 커널이 디스크를 다루는 기능 가운데 하나입니다. 커널에 함께 들어 있습니다. 하는 일은 캐싱입니다. 자주 쓰는 데이터를 더 빨리 읽히는 저장 장치에 복사해 두고, 다음부터는 거기서 꺼냅니다.

이 절은 밑바탕(까닭·계층), 돌아가는 모습(장치·읽기·정책), 운영(쓰기 모드·대가·고르는 때) 순서로 dm-cache 를 설명합니다.

디스크 앞에 디스크를 하나 더 두는 까닭

HDD(Hard Disk Drive, 하드 디스크 드라이브)는 원판을 돌리고 헤드를 옮겨 데이터를 읽습니다. 흩어진 데이터를 여기저기 읽으면 헤드가 옮겨 다니는 시간이 쌓입니다. 대신 같은 값으로 담는 용량이 큽니다.

SSD(Solid State Drive, 솔리드 스테이트 드라이브)는 움직이는 부품 없이 반도체에 데이터를 담습니다. 흩어진 데이터도 HDD 보다 훨씬 빨리 읽습니다. 대신 같은 용량이면 값이 더 비쌉니다.

서버가 날마다 손대는 데이터는 대개 전체의 일부입니다. 자주 손이 가는 쪽을 뜨거운 데이터, 뜸한 쪽을 차가운 데이터라고 부릅니다. 데이터베이스라면 오늘 들어온 주문은 계속 읽히고, 몇 년 전 주문은 거의 안 읽힙니다.

뜨거운 데이터만 SSD 에 올려 두면 SSD 를 조금만 사고도 대부분의 요청을 SSD 에서 처리합니다. dm-cache 는 이 판단을 디스크 아래에서 알아서 합니다. 애플리케이션도 파일 시스템도 어느 데이터가 SSD 에 있는지 모릅니다.

디바이스 매퍼의 타깃 하나

먼저 블록 장치를 짚습니다. 블록 장치는 정해진 크기의 조각 단위로 읽고 쓰는 저장 장치입니다. 리눅스에서는 디스크 한 대도, 그 디스크를 나눈 파티션 하나도 각각 블록 장치 하나로 보입니다.

디바이스 매퍼는 커널 안에서 가상의 블록 장치를 만들어 주는 틀입니다. 가상 장치로 들어온 요청을 정해 둔 규칙에 따라 진짜 장치로 넘깁니다. 그 규칙 한 가지가 타깃(target)입니다.

타깃은 여럿입니다. 디스크 전체를 암호화하는 dm-crypt도 타깃입니다. dm-cache 도 그중 하나입니다. 이름 앞의 dm 은 이 틀 위에서 도는 타깃이라는 표시입니다.

그래서 dm-cache 로 만든 장치는 겉보기에 평범한 블록 장치 하나입니다. 그 위에 어떤 파일 시스템이든 올릴 수 있습니다. dm-cache 는 파일을 모르고 블록 주소만 봅니다.

장치 세 개

dm-cache 한 벌은 장치 세 개로 이뤄집니다. 장치마다 담는 것과 흔히 쓰는 매체가 다릅니다.

장치 담는 것 흔히 쓰는 매체
오리진 장치 데이터 전부 HDD
캐시 장치 뜨거운 블록의 복사본 SSD
메타데이터 장치 어느 블록이 캐시에 있는지와 그 블록의 상태 SSD

오리진(origin) 장치는 원래 있던 HDD 입니다. 데이터의 원본은 전부 여기 있습니다. 캐시 장치에 있는 블록은 오리진에 있는 블록의 복사본입니다.

캐시 장치는 크기가 같은 캐시 블록으로 나뉩니다. 블록 크기는 dm-cache 장치를 만들 때 정합니다. 오리진도 같은 크기의 칸으로 나눠 봅니다. 오리진의 한 칸이 캐시 블록 하나에 대응합니다.

캐시 블록은 앞에서 본 블록 장치의 조각보다 큽니다. 조각 여러 개를 묶은 크기입니다. 요청이 캐시 블록보다 작아도 캐시에 올리고 내리는 단위는 캐시 블록 한 칸입니다. 이 문서에서 그냥 「블록」이라고 하면 이 한 칸을 가리킵니다.

메타데이터 장치는 그 대응표를 담습니다. 어느 오리진 블록이 어느 캐시 블록에 올라 있는지 적습니다.

캐시에 올라 있는 블록에 쓰기가 캐시에만 오면 오리진의 원본과 내용이 달라집니다. 오리진과 달라진 블록이 dirty 블록입니다. 메타데이터 장치는 블록마다 dirty 인지도 함께 적습니다.

이 표가 메모리가 아니라 장치에 있기 때문에 재부팅한 뒤에도 캐시를 이어 씁니다. 표가 없으면 캐시 장치에 무엇이 들었는지 알 길이 없어 캐시를 처음부터 다시 채워야 합니다.

flowchart TD
    FS["파일 시스템"] --> V["dm-cache 가 만든 가상 블록 장치"]
    V -.->|"대응표를 보고 고친다"| M["메타데이터 장치 · SSD"]
    V -->|"읽기 · 쓰기"| C["캐시 장치 · SSD"]
    V -->|"읽기 · 쓰기"| O["오리진 장치 · HDD"]

파일 시스템은 맨 위의 가상 블록 장치 하나만 봅니다. 읽기와 쓰기 요청은 캐시와 오리진 가운데 한쪽으로 갑니다. 어느 쪽으로 보낼지는 dm-cache 가 메타데이터 장치의 대응표를 보고 정합니다. 그림의 점선이 이 대응표를 보는 길입니다. 데이터는 이 길로 오가지 않습니다.

읽기가 지나가는 길

요청한 블록이 캐시 장치에 있으면 캐시 히트, 없으면 캐시 미스라고 부릅니다. 히트가 나면 SSD 에서 읽어 돌려줍니다. 미스가 나면 HDD 에서 읽어 돌려줍니다.

미스가 났다고 그 블록을 곧바로 SSD 로 올리지는 않습니다. 한 번 읽히고 다시 안 읽힐 블록까지 올리면 SSD 가 쓸모없는 복사본으로 금방 찹니다. 올릴지는 다음 소절의 정책이 정합니다.

블록을 SSD 로 올리는 일이 승격(promotion)입니다. 승격은 요청을 처리하는 길과 떨어져 뒤에서 이뤄집니다.

flowchart TD
    R["읽기 요청"] --> Q{"캐시 장치에 있나"}
    Q -->|"있다 · 캐시 히트"| S["SSD 에서 읽어 돌려준다"]
    Q -->|"없다 · 캐시 미스"| H["HDD 에서 읽어 돌려준다"]
    H --> P{"정책이 보기에 자주 찾는 블록인가"}
    P -->|"그렇다"| U["뒤에서 SSD 로 승격한다"]
    P -->|"아니다"| E["HDD 에만 둔다"]

그림에서 요청에 답하는 것은 가운데 두 상자까지입니다. 맨 아래의 승격은 답을 돌려준 뒤에 따로 일어납니다.

무엇을 올릴지 고르는 정책

SSD 는 HDD 보다 작아서 모든 블록을 올릴 수 없습니다. 어느 블록을 올리고 어느 블록을 내릴지 정하는 규칙이 정책(policy)입니다. dm-cache 는 정책을 갈아 끼울 수 있게 본체와 떼어 두었습니다.

정책은 블록마다 얼마나 자주 찾히는지 셉니다. 자주 찾히는 블록은 승격합니다. 캐시가 차면 뜸해진 블록을 SSD 에서 내립니다.

SSD 에서 블록을 내리는 일이 강등(demotion)입니다. 캐시 일반에서 축출이라고 하는 일과 같습니다. 강등할 때 블록은 캐시에서만 빠지고 원본은 오리진에 그대로 있습니다.

승격과 강등을 묶어 이주(migration)라고 합니다. 이주에는 디스크 시간이 듭니다. 블록 하나를 올리려면 HDD 에서 읽어 SSD 에 써야 합니다.

이주가 몰리면 애플리케이션의 읽기와 쓰기가 쓸 디스크 시간이 줄어듭니다. 그래서 이주에 쓸 수 있는 양에 상한을 둡니다.

정책 가운데 cleaner 는 새로 올리는 일을 멈추고 dirty 블록을 오리진으로 내려보내기만 합니다. 캐시를 떼어 내기 전에 이 정책으로 바꿔 캐시를 깨끗하게 비웁니다.

쓰기를 다루는 모드 셋

모드는 쓰기를 어느 장치에 먼저 하느냐를 정합니다. 세 모드를 캐시에 이미 올라 있는 블록에 쓰기가 온 경우로 견줍니다.

모드 쓰기가 가는 곳 끝났다고 알리는 때 dirty 블록이 생기나
writeback 캐시 장치 캐시 장치에 쓴 뒤 생긴다
writethrough 캐시 장치와 오리진 장치 오리진 장치에도 쓴 뒤 안 생긴다
passthrough 오리진 장치 오리진 장치에 쓴 뒤 안 생긴다

writeback 은 쓰기를 캐시 장치에만 해 두고 오리진에는 나중에 내려보냅니다. 이런 방식이 write-back 입니다. 쓰기도 SSD 속도로 끝나는 대신 dirty 블록이 생깁니다. 모드를 따로 적지 않으면 이 모드입니다.

writethrough 는 오리진에도 쓴 뒤에야 끝났다고 알립니다. 이런 방식이 write-through 입니다. 쓰기는 HDD 속도로 끝나는 대신 캐시가 언제나 오리진과 같습니다. 캐시의 덕은 읽기에서만 봅니다.

passthrough 는 읽기와 쓰기를 전부 오리진에서 처리합니다. 캐시에 올라 있던 블록에 쓰기가 오면 그 복사본을 무효화합니다. 캐시 내용이 오리진과 맞는지 확신이 없을 때 켜는 모드입니다. 캐시가 깨끗해야 이 모드로 옮길 수 있습니다.

writeback 에서 블록 하나가 거치는 상태를 그리면 아래와 같습니다.

stateDiagram-v2
    오리진에만있음: 오리진에만 있음
    깨끗함: 캐시에 올라옴 · 깨끗함
    더러움: 캐시에 올라옴 · dirty
    오리진에만있음 --> 깨끗함: 승격
    깨끗함 --> 더러움: 쓰기
    더러움 --> 깨끗함: 오리진으로 내려보냄
    깨끗함 --> 오리진에만있음: 강등

dirty 블록은 곧바로 강등되지 못합니다. 오리진으로 내려보내 깨끗해진 뒤에야 캐시에서 내려갈 수 있습니다.

치르는 것

dm-cache 를 붙이면 디스크 한 대가 장치 세 개가 됩니다. 대가는 네 가지입니다.

캐시 장치가 고장 나면 — writeback 에서 dirty 블록의 최신 판은 캐시 장치에만 있습니다. 캐시 장치가 고장 나면 오리진에는 옛 판만 남습니다. 그래서 캐시 장치를 RAID(Redundant Array of Independent Disks, 여러 디스크를 한 대처럼 묶어 쓰는 방식)로 두 벌 겹쳐 두기도 합니다.

전원이 나가면 — 대응표는 바뀔 때마다 메타데이터 장치에 적지 않습니다. 바뀐 내용을 메모리에 모아 두었다가 한꺼번에 장치에 적습니다. 이렇게 장치에 적어 두는 일을 확정이라고 합니다.

확정 전에 전원이 나가면 마지막 확정 뒤에 바뀐 대응표가 장치에 없습니다. 그 사이 캐시에 새로 쓴 블록은 어느 캐시 블록에 들었는지 기록이 없어 찾지 못합니다. 그래서 최근 쓰기 일부를 잃을 수 있습니다.

flush 는 파일 시스템이 「지금까지 쓴 것을 장치에 확실히 남겨 달라」고 보내는 요청입니다. flush 가 오면 dm-cache 는 대응표를 확정합니다. 그래서 flush 가 끝난 쓰기는 전원이 나가도 남습니다. 그 뒤의 쓰기는 장담하지 않습니다.

writethrough 와 passthrough 는 쓰기가 오리진에 닿은 뒤에야 끝났다고 알립니다. 그래서 이 걱정이 없습니다.

처음에는 빨라지지 않는다 — 새로 붙인 캐시는 비어 있습니다. 뜨거운 블록이 승격될 때까지 요청 대부분이 캐시 미스라서 HDD 속도로 처리됩니다. 캐시를 이렇게 채워 가는 과정이 캐시 예열입니다.

떼어 낼 때 순서가 있다 — dirty 블록을 오리진으로 전부 내려보낸 뒤에야 캐시를 뗄 수 있습니다. 그전에 캐시 장치를 줄이거나 지우면 최신 판을 잃습니다. 앞 소절의 cleaner 정책이 이 일을 합니다.

고르는 때

dm-cache 가 값을 하는지는 요청이 데이터의 어디에 몰리느냐로 갈립니다. 흔한 네 경우를 견주면 이렇습니다.

상황 맞나 까닭
데이터는 SSD 보다 크고 요청은 일부에 몰린다 맞다 몰리는 일부가 SSD 에 올라가 히트가 잦다
요청이 데이터 전체에 고르게 흩어진다 안 맞다 자주 찾는 블록이 따로 없어 올릴 것을 못 고른다
큰 파일을 처음부터 끝까지 한 번 읽는다 안 맞다 다시 안 읽을 블록이다. HDD 도 차례로 읽으면 헤드를 덜 옮긴다
데이터가 SSD 에 다 들어간다 안 맞다 캐시를 둘 까닭 없이 SSD 에 바로 두면 된다

첫 줄에 드는 것이 큰 데이터베이스 볼륨, 가상 머신 디스크 이미지를 모아 둔 서버 같은 경우입니다. 그 가운데 한 번에 바쁜 것은 일부입니다.

손으로 dm-cache 를 만드는 일은 드뭅니다. LVM(Logical Volume Manager, 논리 볼륨 관리자)은 디스크를 논리 볼륨이라는 단위로 쪼개고 합쳐 쓰는 리눅스 도구입니다. LVM 의 캐시 기능이 안에서 dm-cache 를 만들어 쓰므로, 대개 LVM 명령으로 붙이고 뗍니다.

같은 일을 하는 커널 기능이 둘 더 있습니다. bcache 는 디바이스 매퍼를 거치지 않는 별도의 블록 캐시입니다. dm-writecache 는 디바이스 매퍼의 다른 타깃으로, 쓰기만 캐시에 받아 두고 읽기 캐시는 하지 않습니다.

관련 항목

dm-cache 를 떠받치는 커널 구성 요소

Linux · 커널 · 디바이스 매퍼 · 블록 장치 · 블록 계층 · 파일 시스템 · 파티션

dm-cache 가 짝짓는 저장 매체

SSD · HDD · NVMe · 디스크 · RAID

dm-cache 가 고를 수 있는 쓰기 방식

write-behind · write-through · passthrough · write-around

dm-cache 가 기대는 캐시 개념

캐싱 · 캐시 히트 · 캐시 미스 · 축출 · dirty · 무효화 · 메타데이터 · 캐시 예열 · 참조 지역성 · flush

블록 장치 앞에 캐시를 두는 다른 구현

bcache · dm-writecache · flashcache · EnhanceIO · L2ARC

디바이스 매퍼 위에 선 다른 타깃

dm-crypt · dm-thin · dm-verity · dm-raid

dm-cache 를 만들고 다루는 도구

LVM · lvmcache · dmsetup · lvconvert

다른 이름: dm cache · device-mapper cache · 디바이스 매퍼 캐시