[Clickhouse] ๋ฒกํฐ ๊ฒ์ (Vector Search)
clickhouse๋ ์์ฒด์ ์ผ๋ก ๋ฒกํฐ ๊ฒ์์ ์ํ ํจ์๋ค์ ์ ๊ณตํ๋ค.
๋ฌผ๋ก ์ ๋ฌธ ๋ฒกํฐ DB๋ค์ฒ๋ผ ๋์ ์ฝ๊ธฐ ์ฒ๋ฆฌ๋๊ณผ ๋ ์ดํด์ ๋ณด์ฅ์ ์ ๊ณตํ๋๊ฑด ์๋๋ผ์, ์ค์๊ฐ ์๋น์ค์ฉ์ผ๋ก๋ ์ฌ์ฉํ ์ ์๋ค.
ํ์ง๋ง ๋ฐ์ดํฐ์ ์ด ์์ฒญ๋๊ฒ ํฌ์ง ์๊ณ ๊ธฐ์กด ๋ฐ์ดํฐ์ ์ด clickhouse์ ์๋ค๋ฉด, ์คํ-๊ฒ์ฆ์ฉ์ผ๋ก ๊ฐ๋ณ๊ฒ ์ฐ๊ธฐ์๋ ์ถฉ๋ถํ๋ค.
clickhouse๋ ์ธ๋ฑ์ค๋ฅผ ์ฐ์ง ์๋ ์ ํํ ๋ฒกํฐ ๊ฒ์๊ณผ, ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํ๋ ์ค์ฐจ ์๋ ๋ฒกํฐ ๊ฒ์์ ์ ๊ณตํ๋ค.
๋ฒกํฐ ์ ์ฌ๋ ํจ์์ ์ ํํ ๋ฒกํฐ ๊ฒ์
clickhouse๋ ํจ์์ ํํ๋ก ์ ์ฌ๋ ์ฐ์ฐ์ ์ ๊ณตํ๋ค. ๋ฐ๋ก ๋ญ ์ค์นํ ํ์๋ ์๋ค.
select
cosineDistance(
[0.5736801028251648, 0.2516217529773712, -0.6825592517852783],
[0.5636801028251648, 0.3516217529773712, -0.16825592517852783]
) as cosine,
dotProduct(
[0.5736801028251648, 0.2516217529773712, -0.6825592517852783],
[0.5636801028251648, 0.3516217529773712, -0.16825592517852783]
) as dot,
L2Distance(
[1, 2, 3],
[2, 3, 4]
) as euclid
๊ทธ๋ฅ ์ด๋ ๊ฒ ๋ฒกํฐ๊ฐ 2๊ฐ ๋ฃ์ผ๋ฉด ๋น๊ตํด์ฃผ๋ ์์ด๋ค.
์ ํํ ๋ฒกํฐ ๊ฒ์์ ์ํํ๋ ๋ฐฉ๋ฒ๋, ์ ์ ์ฌ๋ ํจ์๋ฅผ ๊ฐ์ ธ๋ค๊ฐ ์ ๋ ฌ์ ๋ถ์ด๋ ๊ฒ์์ ๋ฒ์ด๋์ง ์๋๋ค.
๊ฐ๋จํ๋ค.
create table default.vector_test (
id String, -- uuidv7
color String, -- Red, Blue, Yellow, Black, White, Green
vector Array(Float) -- 256 vector
)
Engine = MergeTree
Order by id
๊ทธ๋ฅ ์ ๋นํ ๋ฒกํฐ ์ ์ฅ์ฉ ์ปฌ๋ผ ์ถ๊ฐํ ๋ค์์
INSERT INTO default.vector_test
SELECT
generateUUIDv7() as id,
['Red', 'Blue', 'Yellow', 'Black', 'White', 'Green'][toUInt8(rand() % 6) + 1] as color,
arrayMap(x -> randNormal(0.5, 0.3), range(256)) as vector
FROM numbers(5000000)
SETTINGS max_insert_threads=4;
๋ฒกํฐ ๊ฐ ๋ฃ์ด์ฃผ๊ณ
select *
from ํ
์ด๋ธ
where ...
order by
L2Distance(
vector,
[2, 3, 4, ...]
) asc
์ด๋ฐ ์์ผ๋ก, Distance ๊ฒฐ๊ณผ๋ก ์ ๋ ฌ์ ๊ฑธ๋ฉด ๋๋ค. l2์ ์์์๋ก ๊ฐ๊น์ฐ๋ asc๋ฅผ ๊ฑธ๊ณ , dot/cosine์ ํด์๋ก ๊ฐ๊น์ด ๊ฒ์ด๋ desc๋ฅผ ๊ฑธ๋ฉด ๋๊ฒ ๋ค.
๊ทธ๋ผ ์ด๋ฐ ์์ผ๋ก ๋์ํ๋ค. ํ์ค์บ ๊ธฐ๋ฐ์ด๊ธด ํ์ง๋ง, clickhouse ์์ฒด๊ฐ ์๋ ์ด๋ฐ ๋ถ๋ถ์ ์ต์ ํ๊ฐ ์ ๋์ด์๋ค๋ณด๋ ์ธ๋ฑ์ค ์์ด ๋์๋ ์ด์ง๊ฐํ ์ฌ์ด์ฆ์์๋ ์ถฉ๋ถํ ๋น ๋ฅด๋ค.
์ ํํ ๊ฒ์์ ๋ฌธ์
์ ์ ํํ ๊ฒ์์ ๋จ์ ์, ๋ฐ์ดํฐ ํฌ๊ธฐ๊ฐ ๋น๋ํด์ง์๋ก ์ฑ๋ฅ ๋ถํ๊ฐ ๋ง์ฌํด์ง๋ค๋ ๊ฒ์ด๋ค. ํญ์ ํ์ค์บํด์ ๋ชจ๋ ๋ฐ์ดํฐ์ ๋ํด ๋ฒกํฐ์ฐ์ฐ์ ์ํํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋์ ์ด๋ฐ ๋ฐฉ์์ ํ๋์จ์ด๊ฐ ์๋ฌด๋ฆฌ ์ข๋๋ผ๋ ๊ท๋ชจ ํ์ฅ์ ๋ฐ๋ผ ํ๊ณ์ ๋ถ๋ชํ๊ฒ ๋๋ค.
๋ด ๊ฒฝ์ฐ์๋ 100-500๋ง๊ฐ ์ ๋์์๋ ์์ํ๊ฒ ์ ๋์์ง๋ง, 2000๋ง๊ฐ์ฏค ๋๋ ๊ฑฐ์ 10์ด ๊ฐ๊น์ด ๊ฑธ๋ ธ๋ค.
๋ฒกํฐ ์ธ๋ฑ์ค์ ๊ทผ์ฌ์น ๊ฒ์
๋คํํ clickhouse๋ HNSW ๋ฒกํฐ ์ธ๋ฑ์ค ๊ธฐ๋ฐ์ ๊ทผ์ฌ์น ๊ฒ์ ๋ํ ์ ๊ณตํ๋ค.
์์ ํ ์ผ์นํ์ง๋ ์์ง๋ง, ๊ทธ ๋๊ฐ๋ก ๋น ๋ฅด๊ณ ํจ์จ์ ์ธ ๋ถ๋ถ๊ฒ์์ ๊ฐ๋ฅ์ผ ํ๋ ๊ฒ์ด๋ค.
์ด ๊ธฐ๋ฅ์ ์์ ํ๋์ง ์์๊ณ , ๋ถ์กฑํ ๋ถ๋ถ์ด ์ ๋ฒ ๋ง๋ค.
ํ์ฌ 25 ๋ฒ์ ๊ธฐ์ค์ผ๋ก๋ ๋ฒ ํ ๋ฒ์ ์ํ์ ์๋ค.
์๋ฌดํผ, ์ ์ ํด๋ฆฌ๋ ๋ฒกํฐ์ ๋ํด ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๊ณ ์ถ๋ค๋ฉด, ์ด๋ฐ ์์ผ๋ก ํ ์ ์๋ค.
SET allow_experimental_vector_similarity_index=true;
ALTER TABLE default.vector_test
ADD INDEX vector_index vector TYPE vector_similarity('hnsw', L2Distance, 'f64', 0, 0);
vector_similarity์ ๋งค๊ฐ๋ณ์๋ DB ๋ฒ์ ์ ๋ฐ๋ผ ์ ์ ์์ด ๊ณ์ ๋ฐ๋๋ค. ์ด๋ค ๋ฒ์ ์์๋ ์ฐจ์์ ๋ฐ๊ธฐ๋ ํ๊ณ ์๋ฐ๊ธฐ๋ ํ๊ณ , ๋งค๊ฐ๋ณ์ ์ซ์๋ ์๋ฆฌ๊ฐ๋ฆฌํ๋ค. ๋ฌธ์๋ฅผ ๋ณด๊ณ ์ฌ์ฉํ ์๊ฐ ์๋ค.
์๋ฌดํผ ๋ง๋ค๊ณ ๋๋ฉด ์ธ๋ฑ์ค๊ฐ ์ ์ฉ์ด ๋ ํ ๋ฐ...
์ฌ์ค ์ ๋์ง ์๋๋ค.
clickhouse์ vector index๊ฐ ๊ทธ๋ ๊ฒ ์์งํ ํํ๋ก ๊ตฌํ๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ด ๋ฒกํฐ ์ธ๋ฑ์ค๋ ๋ค๋ฅธ ์ธ๋ฑ์ค๋ค๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก SKIP Index ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํ๋๋ค. ์ ๋ชจ๋ฅธ๋ค๋ฉด ์ด์ ๋ํ ๊ตฌ์กฐ ์ดํด๊ฐ ์ ํ๋์ด์ผ ํ๋ค.
https://blog.naver.com/sssang97/223926075870
์๋ฌดํผ ์ค์ํ ๊ฒ์, ๋ฒกํฐ ์ธ๋ฑ์ค๋ ํ ๋จ์๊ฐ ์๋๋ผ granule ๋จ์์ ์ ๋งคํ ์ธ๋ฑ์ค ๋จ์๋ผ๋ ๊ฒ์ด๋ค.
๊ทธ๋์ ์ด๊ฒ๋ ์ผ์นํ๋ ๋ธ๋ก์ ์ฐพ์๋ด๋ ์ธ๋ฑ์ค๊ฐ ์๋๋ผ, ์ผ์นํ์ง ์๋ ๋ธ๋ก์ ๋ฒ๋ฆฌ๋ ์ธ๋ฑ์ค๋ก ๊ตฌํ๋๋ค.
๊ทธ๋ ๋ค๋ณด๋ ์ด๊ฑด ํญ์ ํจ์จ์ ์ด์ง ์์์ DB๊ฐ ๋ฒกํฐ ์ธ๋ฑ์ค๋ฅผ ์ ํํ์ง ์์ ์๋ ์๋ค. ๊ธฐ์กด ๋ฐฉ์๋๋ก ์ค์บํ๋๊ฒ ์ ๋ฆฌํ๋ค ํ๋จํ๋ฉด ๊ณผ๊ฐํ๊ฒ ๋ฒ๋ฆฌ๊ณ ์ฐ์ง ์๋๋ค.
EXPLAIN์ ๊ฑธ์ด๋ณด๋ฉด, ์ธ๋ฑ์ค๋ฅผ ์ด๋ป๊ฒ ํ๋์ง๋ฅผ ํ์ธํด๋ณผ ์ ์๋ค.
๋ด ๊ฒฝ์ฐ์๋ ์คํตํ ๋น์จ์ด ๋์ง ์์์ ์ฐ๋๋ง๋ํ ๊ตฌ์ฑ์ด ๋์ด๋ฒ๋ฆฐ ์ํฉ์ด๊ธด ํ๋ค.
์ฌ์ ํํฐ๋ง๊ณผ ์ฌํ ํํฐ๋ง
๋ฒกํฐ ๊ฒ์์ ์ํํ ๋ ๊ฐ์ฅ ๊ณจ์น์ํ ๋ถ๋ถ ์ค ํ๋๊ฐ ํํฐ๋ง์ด๋ค. ๋ฒกํฐ ์ธ๋ฑ์ค์ ๊ตฌ์กฐ์ ์ฌ์ ํํฐ๋ง์ ๊ตฌํ์๋ ์๋นํ tradeoff๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ฌํ ํํฐ๋ง์ ์ฝ๋ค. 100๊ฐ ๊ฐ์ ธ์ค๋ฉด ๊ทธ ์ค์ ๋ง์ง ์๋ ํํฐ๋ง ์กฐ๊ฑด์ ๋ง์ง ์๋ 20๊ฐ(์์)๋ฅผ ๋ฒ๋ฆฌ๊ณ 80๊ฐ๋ง ๋์ ธ์ฃผ๋ฉด ๋๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
clickhouse์ ํํฐ๋ง ๋์์ ์ข ๋ฏธ๋ฌํ ๋ถ๋ถ์ด ์๋ค. ๋ช ์์ ์ด์ง ์๊ณ , ํ์ฌ ํ ์ด๋ธ ์ํ์ ๋ฐ๋ผ์ ์ฌ์ /์ฌํ ํํฐ๋ง์ ์๋์ผ๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ด๋ค. (๋์ค์ ๋ฌ๋ผ์ง ์ ์์)
๋ง์ฝ ํํฐ๋ง ์กฐ๊ฑด์ ์ฌ์ฉ๋๋ ๊ฐ์ด ๊ธฐ๋ณธํค, ๊ทธ๋ฌ๋๊น ํํฐ์
ํค์ ์ผ๋ถ๋ผ๋ฉด, ๊ทธ๊ฑด ์ฌ์ ํํฐ๋ง์ด ๋ ์ ์๋ค.
ํํฐ๋ง์ผ๋ก ๊ฑฐ๋ฅธ ๋ค์์ ๋ฒกํฐ ๊ฒ์์ ์ํํ๋ค๋ ๊ฒ์ด๋ค.
๋ฐ๋ฉด ํํฐ๋ง ์กฐ๊ฑด์ ์ฌ์ฉ๋๋ ํ๋๊ฐ Skip Index์ ๊ฑธ๋ ค์๊ฑฐ๋, ์๋ฌด๊ฒ๋ ์๋ค๋ฉด ๊ทธ๊ฑด ์ฌํ ํํฐ๋ง์ ์ ํํ๋ค.
๊ด๋ จ๋ ํ๋๊ทธ๋ vector_search_filter_strategy์ด๋ค. ๊ธฐ๋ณธ๊ฐ์ auto๋ผ์ ์๋์ผ๋ก ํํฐ๋ง ๋ฐฉ์์ ์ ํํ๋ค.
๊ทธ๋์ ๊ธฐ๋ณธํค๋ก ์ธ๋ฑ์ค๋ฅผ ๊ฑธ์ด๋ณด๋ฉด

์ค์ ๋ก 999๊ฐ๋ฅผ ์ค์บํด์์ 999๊ฐ์ ๋ํด์๋ง ๋ฒกํฐ ๊ฒ์์ ์ํํ๋ค๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ฐธ์กฐ
https://clickhouse.com/docs/knowledgebase/vector-search
https://clickhouse.com/docs/engines/table-engines/mergetree-family/annindexes
https://clickhouse.com/docs/sql-reference/functions/distance-functions
https://clickhouse.com/docs/operations/settings/settings#vector_search_filter_strategy