상품 정보를 고쳤는데 검색에 안 나왔다. 화면에서는 바뀌어 있었다.
Table of contents
Open Table of contents
증상 — 화면은 바뀌고 검색은 그대로였다
저장소가 둘이다.
상품 정보 DB
검색 검색 엔진
화면은 DB를 보고 검색은 엔진을 보므로 고치면 DB만 바뀐다.
엔진 쪽은 배치가 반영한다.
0 4 * * * php batch/reindex.php
하루 한 번이라 고친 것이 다음 날 나오는 것은 알고 있었다.
문제는 다음 날에도 안 나온 것이었다. 반영 주기로 설명되던 현상이 주기를 넘겨도 그대로였다.
원인 — 배치가 읽던 뷰
배치가 무엇을 읽는지부터 봤다.
// batch/reindex.php
$rows = $this->db->query("SELECT * FROM product_search_view")->result_array();
product 가 아니라 product_search_view 를 읽고 있었다.
CREATE VIEW product_search_view AS
SELECT p.*, c.name AS category_name
FROM product p
JOIN category c ON p.category_no = c.no
WHERE p.use_yn = 'Y' AND p.stock > 0;
WHERE 로 두 가지를 빼고 JOIN 이 하나를 더 빼고 있었다.
고친 상품은 분류가 없어서 JOIN 에서 빠졌다. 원본에는 값이 정상으로 들어갔고 색인에만 안 들어간 것이다.
원본만 확인하고 배치가 읽는 자리를 안 본 것이 시간을 썼다. 넣는 곳과 읽는 곳이 다르면 그 사이에 무엇이 끼어 있는지부터 봐야 했다.
조인이 조용히 행을 뺐다
뷰가 언제 왜 만들어졌는지는 아는 사람이 없었다.
조건 셋 중 둘은 맞았다. 판매를 안 하는 상품이 검색에 안 나오는 것은 의도한 것이다.
JOIN 은 아니었다. 분류가 없으면 검색에서도 빼는 것이 의도였는지 물어보니 그런 뜻이 아니라고 했다.
LEFT JOIN category c ON p.category_no = c.no
LEFT JOIN 으로 바꾸니 분류 이름이 비어도 상품은 색인에 들어간다.
WHERE 에 적힌 조건은 읽는 사람 눈에 띄지만 JOIN 이 거는 조건은 안 띈다. 빼려는 것이 있으면 WHERE 에 적어야 다음 사람이 알아본다.
왜 안 드러났는지 봤다
배치 로그에는 색인 건수만 남고 있었다.
색인 완료: 382,110건
DB에는 412,004건이 있었고 3만 건 차이를 아무도 안 봤다.
$dbCount = $this->db->count_all('product');
$idxCount = count($docs);
log_message('info', "DB {$dbCount}건 중 색인 {$idxCount}건 (제외 " . ($dbCount - $idxCount) . "건)");
두 숫자를 한 줄에 같이 내게 했다.
382,110 만 있으면 성공으로 읽히지만 옆에 412,004 가 있으면 차이가 눈에 든다. 같은 로그인데 숫자 하나를 더 붙이는 것으로 성격이 달라졌다.
변경 내용 — 이유별 제외 건수
빠진 것을 이유별로 세게 했다.
$excluded = [
'use_yn=N' => 0,
'stock=0' => 0,
'no_category' => 0,
];
그러면 로그가 이렇게 나온다.
DB 412,004건 중 색인 382,110건
제외 29,894건 (use_yn=N: 24,102, stock=0: 5,780, no_category: 12)
no_category 가 12건이라는 것이 이번 건이 걸린 자리였다.
숫자에 이유가 붙으니 맞는지 판단할 수 있게 됐다. stock=0 이 5,780건인 것을 보고 품절 상품도 검색에는 나와야 한다는 이야기가 그때 나왔다.
대응 — 반영 시점과 두 방향 대조
하루 한 번이 늦다는 이야기도 있었다.
0 4 * * * 전체 색인
*/5 * * * * 바뀐 것만 색인
바뀐 것만 5분마다 반영하게 했다.
SELECT * FROM product_search_view WHERE mod_date > ?;
마지막으로 색인한 시각 이후 바뀐 것만 가져오니 전체보다 훨씬 적다.
다만 지운 것은 이 방식으로 못 찾는다. 행이 사라지면 mod_date 로 걸릴 것 자체가 없기 때문이다.
UPDATE product SET use_yn = 'N', mod_date = NOW() WHERE no = ?;
지우지 않고 표시만 바꾸니 mod_date 로 잡히고 색인에서는 빼는 대상이 된다.
마지막으로 양쪽이 같은지 맞춰 보는 것을 뒀다.
$dbIds = $this->db->column("SELECT no FROM product_search_view");
$idxIds = $this->search->allIds();
$missing = array_diff($dbIds, $idxIds);
$extra = array_diff($idxIds, $dbIds);
if ($missing) log_message('error', '색인에 없는 상품: ' . count($missing));
if ($extra) log_message('error', '색인에만 있는 상품: ' . count($extra));
missing 과 extra 를 같이 보는 것이 핵심이었다.
매일 밤 돌렸더니 처음에 extra 가 800건 나왔다. 지운 상품이 색인에 남아 검색에 계속 나오고 있던 것인데 한 방향만 봤으면 영영 안 드러났을 것이다.
정리
- 넣는 저장소와 읽는 저장소가 다르면 반영 시점과 조건이 둘 다 문제가 된다
- 배치가 무엇을 읽는지 확인한다
- 뷰나 중간 표가 조건을 걸고 있을 수 있다
JOIN이 조용히 행을 빼고WHERE와 달리 눈에 안 띈다- 뺄 것이 있으면
LEFT JOIN으로 두고WHERE에 적는다 - 원본 건수와 색인 건수를 한 줄에 같이 낸다
- 숫자 하나를 더 붙이는 것으로 같은 로그의 성격이 달라진다
- 무엇이 왜 빠졌는지 이유별로 센다
- 바뀐 것만 자주 반영하되 지운 것은 표시로 남겨야 잡힌다
- 대조는 두 방향을 다 본다
- 색인에만 남은 것이 실제로 800건 있었다