안 쓰는 이미지를 찾는 스크립트를 만들었다. 찾았으니 지우는 것도 넣자는 이야기가 나왔다.
Table of contents
Open Table of contents
찾는 것까지 만들어 두었다
만든 것은 이랬다.
$files = glob('/data/upload/*/*.jpg');
$used = $db->column("SELECT DISTINCT image FROM product WHERE image IS NOT NULL");
$unused = array_diff(array_map('basename', $files), $used);
echo count($unused) . "개\n";
3,882개
3,882개가 안 쓰이는 것으로 나왔다.
찾기와 지우기를 한 스크립트에 두면 결과를 사람이 보는 단계가 없어진다. 찾는 조건이 틀렸을 때 그것을 알아챌 자리가 사라지고 돌리는 순간 결과가 확정된다.
검증 — 지우기 전에 맞는지 봤다
목록에서 몇 개를 골라 실제로 안 쓰이는지 확인했다.
$ grep -rn "a8f2c1.jpg" --include=*.php --include=*.sql .
한 개가 게시글 본문에 있었다. product.image 만 보고 있었는데 다른 곳에서도 쓰고 있었다.
product.image
board.content 안의 <img>
member.profile_image
event.banner
네 곳이었다. 하나만 보고 안 쓰인다고 판단한 것이었고 지우는 것을 바로 넣었으면 쓰는 이미지를 지웠다.
눈으로 세면 빠지므로 스키마에서 찾았다.
SELECT table_name, column_name FROM information_schema.columns
WHERE table_schema = 'shop'
AND (column_name LIKE '%image%' OR column_name LIKE '%img%'
OR column_name LIKE '%photo%' OR column_name LIKE '%banner%'
OR column_name LIKE '%file%');
information_schema.columns 에서 찾으니 열한 개가 나왔다. 본문에 들어 있는 것은 따로 뒤졌다.
SELECT DISTINCT ... FROM board WHERE content REGEXP '<img[^>]*src=';
찾는 조건이 넓어지자 안 쓰인다고 나오는 개수가 크게 줄었다.
조회와 삭제를 나눴다
같은 스크립트에 두지 않기로 했다.
find-unused-images.php 목록만 낸다
delete-images.php 목록 파일을 받아서 지운다
$ php find-unused-images.php > /tmp/unused.txt
$ wc -l /tmp/unused.txt
$ head -20 /tmp/unused.txt
$ php delete-images.php /tmp/unused.txt
사이에 사람이 본다. 한 스크립트에 두면 그 단계가 없어진다.
목록 파일을 받아도 그대로 지우지 않게 했다.
$list = file($argv[1], FILE_IGNORE_NEW_LINES);
echo count($list) . "개를 지웁니다.\n";
echo "예시:\n";
foreach (array_slice($list, 0, 5) as $f) echo " $f\n";
if (readline("계속합니까? (yes) ") !== 'yes') exit;
crontab 에서 돌 때는 이 확인을 건너뛰는 --yes 를 따로 뒀다. 물어보는 자리에서 멈추면 자동 실행이 영영 안 끝난다.
옮기기와 되돌리기
지우는 대신 옮겼다.
rename($src, '/data/trash/' . date('Ymd') . '/' . basename($src));
unlink 가 아니라 rename 이라 30일 뒤에 진짜로 지운다. 잘못 지웠으면 그 안에 알게 된다.
실제로 알게 됐다. 옮기고 이틀 뒤에 이미지가 안 보인다는 문의가 왔다.
$ ls /data/trash/20141024/ | grep b41f
b41f2a.jpg
옮긴 것 중에 있어서 되돌렸다. event.banner_mobile 이라는 다섯 번째 자리가 있었는데 네 곳을 찾고도 하나를 더 놓친 것이다. 바로 지웠으면 못 되돌렸다.
옮기기만 하고 되돌리는 방법을 안 만들었다가 문의가 왔을 때 손으로 옮겼다.
// restore-images.php
$f = $argv[1];
$src = "/data/trash/" . $argv[2] . "/" . $f;
$dst = "/data/upload/" . substr($f, 0, 6) . "/" . $f;
if (!file_exists($src)) { exit("없습니다: $src\n"); }
rename($src, $dst);
echo "되돌렸습니다: $dst\n";
restore-images.php 를 만들어 두니 그다음부터는 문의가 와도 1분에 끝났다. 옮기는 것을 만들 때 되돌리는 것도 같이 만들어야 했다.
규칙으로도 적어 뒀다.
정리 스크립트 규칙
1. 찾는 것과 지우는 것을 나눈다
2. 지우기 전에 목록을 사람이 본다
3. 지우지 않고 옮긴다. 30일 뒤 삭제
4. "쓰이는 곳"은 스키마에서 찾는다. 눈으로 세지 않는다
이 규칙을 로그 정리와 임시 파일 정리에도 같은 방식으로 썼다.
정리
- 보는 것과 지우는 것을 한 스크립트에 두면 사람이 보는 단계가 없어진다
- 안 쓰인다는 판정을 한 컬럼만 보고 하지 않는다
- 쓰는 자리를
information_schema에서 찾는다. 눈으로 세면 빠진다 - 본문에 들어 있는 것은 따로 뒤진다
- 조회용과 삭제용을 나누고 목록 파일로 잇는다
- 삭제용에도 확인을 넣고
crontab용은--yes로 건너뛰게 한다 unlink대신rename으로 옮기면 물릴 수 있다- 옮긴 것을 되돌리는 것도 같이 만들어 둔다
- 규칙으로 적어 두면 다른 정리 작업에도 쓸 수 있다