DB 인덱슀 μƒμ„±μ˜ μ‚¬μ΄λ“œ μ΄νŽ™νŠΈ(with Mysql)

2023. 11. 25. 10:53ㆍDatabase/DB μŠ€ν„°λ””

μ‹€μ œλ‘œ DBλ₯Ό λ‹€λ£¨κ²Œ λ˜λŠ” μƒν™©μ—μ„œ 빠질 수 μ—†λŠ” 것이 λ°”λ‘œ μΈλ±μŠ€μ΄λ‹€.

인덱슀λ₯Ό ν†΅ν•˜λ©΄ νŠΉμ • μ‘°κ±΄μ—μ„œ λ°μ΄ν„°μ˜ 쑰회 μ„±λŠ₯이 λΉ„μ•½μ μœΌλ‘œ μ¦κ°€ν•œλ‹€λŠ” 것은 λˆ„κ΅¬λ‚˜ μ•Œκ³  μžˆλŠ” 사싀이닀.

κ·ΈλŸ¬λ©΄μ„œλ„, 인덱슀λ₯Ό λ‚¨λ°œν•˜λ©΄ μ•ˆλœλ‹€λŠ” 것 λ˜ν•œ λ‹€λ“€ μ•Œκ³ μžˆλ‹€.

이 λ•Œ λ“±μž₯ν•˜λŠ” 논리가 λ°”λ‘œ μΈλ±μŠ€κ°€ μ°¨μ§€ν•˜λŠ” 곡간적 λΉ„μš©κ³Ό 데이터 μ‚½μž… μ‹œ μΆ”κ°€μ μœΌλ‘œ λ°œμƒν•˜λŠ” μ‹œκ°„μ  λΉ„μš©μ— λŒ€ν•œ 비쀑을 λ¬΄μ‹œν•  수 μ—†λ‹€λŠ” 것이닀.

그런데 막상 μ΄λ ‡κ²Œ 겁만 μ£Όκ³  μ‹€μ œλ‘œ κ·Έ 정도가 μ–΄λŠ 정도인지 μ•Œλ €μ£ΌλŠ” μžλ£ŒλŠ” 많이 μ—†μ–΄μ„œ 이에 λŒ€ν•΄ μ‹€μ œλ‘œ 확인해보렀고 ν•œλ‹€.

ν…ŒμŠ€νŠΈ ν™˜κ²½μ€ Mysql이고, μ•„λ¬΄λŸ° λ¦΄λ ˆμ΄μ…˜μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” λ…λ¦½λœ ν…Œμ΄λΈ”μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•˜μ˜€λ‹€.

-- ν…Œμ΄λΈ” 생성
create table item ( 
	id BIGINT NOT NULL AUTO_INCREMENT,
	name VARCHAR(25) NOT NULL,
	is_for_sale BOOLEAN NOT NULL,
	price BIGINT NOT NULL,
	created_at TIMESTAMP NOT NULL,
	PRIMARY KEY(id)
);

-- 더미 데이터 μ‚½μž…
DELIMITER //

CREATE PROCEDURE insert_dummy_data()
BEGIN
  DECLARE i INT DEFAULT 0;
  WHILE i < 100000 DO
    INSERT INTO item (name, is_for_sale, price, created_at)
    VALUES (
      CONCAT('Item', FLOOR(1 + (RAND() * 99999))), -- 랜덀 μ•„μ΄ν…œ 이름 생성
      RAND() < 0.5, -- 랜덀으둜 TRUE λ˜λŠ” FALSE
      FLOOR(1 + (RAND() * 99999)), -- 랜덀 가격
      DATE_ADD(NOW(), INTERVAL -FLOOR(RAND() * 730) DAY) -- ν˜„μž¬λ‘œλΆ€ν„° μ΅œλŒ€ 2λ…„ μ „μ˜ 랜덀 λ‚ μ§œ
    );
    SET i = i + 1;
  END WHILE;
END //

DELIMITER ;

CALL insert_dummy_data();

 

1. μΈλ±μŠ€κ°€ μ°¨μ§€ν•˜λŠ” 곡간적 λΉ„μš©

μœ„ 쿼리λ₯Ό 보면 μ•Œ 수 μžˆλ“―μ΄, ν˜„μž¬ ν…Œμ΄λΈ”μ—λŠ” 총 10만개의 데이터가 μΆ”κ°€λ˜μ–΄μžˆλ‹€. 이 λ•Œ ν•΄λ‹Ή ν…Œμ΄λΈ”μ΄ μ°¨μ§€ν•˜λŠ” μš©λŸ‰μ€ λŒ€λž΅ 5.52MB 정도이닀. μ•„λž˜ 쿼리λ₯Ό 톡해 확인 κ°€λŠ₯ν•˜λ‹€.

select * from information_schema.TABLES where table_name = 'item';

이제, name μΉΌλŸΌμ— 인덱슀λ₯Ό μΆ”κ°€ν•˜λŠ” 상황을 κ°€μ •ν•œ λ’€, 인덱슀λ₯Ό μΆ”κ°€ν•΄μ£Όκ³ , 

create index item_name_idx on item (name);

κ·Έ ν›„ μ•„λž˜ 쿼리λ₯Ό 톡해 ν•΄λ‹Ή μΈλ±μŠ€κ°€ μ°¨μ§€ν•˜λŠ” μš©λŸ‰μ„ 확인해보면, λŒ€λž΅ 3.52MB 정도이닀.

select 
	stat_value * @@innodb_page_size as index_size 
from mysql.innodb_index_stats 
where stat_name = 'size' and table_name = 'item' and index_name = 'item_name_idx';

ν•˜λ‚˜μ˜ μΉΌλŸΌμ„ 톡해 인덱슀λ₯Ό μƒμ„±ν•˜λ”λΌλ„, ν…Œμ΄λΈ” μ‚¬μ΄μ¦ˆμ˜ 절반이 λ„˜λŠ” 크기λ₯Ό μΈλ±μŠ€κ°€ 차지함을 μ•Œ 수 μžˆλ‹€.

κ·Έλ ‡λ‹€λ©΄ μ΄λ²ˆμ—λŠ” λ°μ΄ν„°μ˜ 뢄포가 True, False 뿐인 Boolean νƒ€μž…μ— 인덱슀λ₯Ό μƒμ„±ν•˜λ©΄ μ–΄λ–»κ²Œ 될까?

이λ₯Ό μœ„ν•΄ μ΄λ²ˆμ—λŠ” item_is_sale_idxλΌλŠ” μ΄λ¦„μ˜ 인덱슀λ₯Ό μƒˆλ‘­κ²Œ μΆ”κ°€ν•΄μ£Όμ—ˆλ‹€.

create index item_is_sale_idx on item (is_for_sale_idx);

κ·Έ λ’€ μœ„μ—μ„œ μ œμ‹œν•œ 인덱슀 크기λ₯Ό ν™•μΈν•˜λŠ” 쿼리λ₯Ό μ‹€ν–‰μ‹œμΌœ 확인해보면, μ΄λ²ˆμ—λŠ” 2.52MBλ₯Ό μ°¨μ§€ν•˜λŠ” 것을 μ•Œ 수 μžˆλ‹€.

name 칼럼과 is_for_sale 칼럼의 μ‚¬μ΄μ¦ˆλŠ” 각각 25byte vs 1byte둜 차이가 크게 λ‚˜λŠ” 것과, λ°μ΄ν„°μ˜ 뢄포가 0κ³Ό 1뿐이라고 해도 μ‹€μ§ˆμ μœΌλ‘œ μΈλ±μŠ€κ°€ μ°¨μ§€ν•˜λŠ” ν¬κΈ°λŠ” 많이 차이가 λ‚˜μ§€λŠ” μ•ŠλŠ”λ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ 볡합 μΈλ±μŠ€μ— λŒ€ν•΄ μ•Œμ•„λ³΄κΈ° μœ„ν•΄ (price, created_at) ν˜•μ‹μœΌλ‘œ 인덱슀λ₯Ό μƒμ„±ν•΄μ£Όμ—ˆλ‹€.

create index item_price_created_at_idx on item (price, created_at);

그리고 κ·Έ κ²°κ³ΌλŠ”, μ΄μ „μ˜ item_name_idx와 λ™μΌν•œ 3.52MB μˆ˜μ€€μ΄λ‹€. 

결둠적으둜, ν…Œμ΄λΈ”μ— λΉ„ν•΄ μΈλ±μŠ€κ°€ μ°¨μ§€ν•˜λŠ” ν¬κΈ°λŠ” κ²°μ½” λ¬΄μ‹œν•  수 μ—†λŠ” 정도인 κ²ƒμœΌλ‘œ λ“œλŸ¬λ‚¬λ‹€. λ¬Όλ‘  칼럼 μˆ˜κ°€ 적은 ν…Œμ΄λΈ”λ‘œ ν…ŒμŠ€νŠΈν•˜μ—¬ 더 λ§Žμ€ ν…Œμ΄λΈ”μ΄μ—ˆλ‹€λ©΄ 차이가 더 많이 λ‚¬κ² μ§€λ§Œ κ·ΈλŸΌμ—λ„ λΆˆκ΅¬ν•˜κ³  κ½€ λ¬΄κ²λ‹€λŠ”κ±΄ 체감이 λ˜λŠ” μˆ˜μ€€.

 

2. 인덱슀둜 인해 λ°œμƒν•˜λŠ” μ‹œκ°„μ  λΉ„μš©

μΈλ±μŠ€λŠ” 기본적으둜 B+TreeλΌλŠ” 자료ꡬ쑰λ₯Ό μ‚¬μš©ν•œλ‹€. ν•΄λ‹Ή μžλ£Œκ΅¬μ‘°λŠ” 항상 κ· ν˜•μž‘νžŒ μƒνƒœ(λͺ¨λ“  λ¦¬ν”„λ…Έλ“œκΉŒμ§€μ˜ 탐색 λΉ„μš©μ΄ 동일)λ₯Ό μœ μ§€ν•˜λŠ” νŠΉμ„±μ΄ μžˆλŠ”λ°, 이둜 인해 DBλŠ” μΌκ΄€λœ 탐색 μ„±λŠ₯을 λ°œνœ˜ν•  수 μžˆλ‹€.

λ‹€λ§Œ μ΄λŸ¬ν•œ νŠΉμ„±μ„ μœ μ§€ν•˜κΈ° μœ„ν•΄ 데이터(인덱슀)의 μΆ”κ°€ 및 제거, μˆ˜μ •μ— 따라 트리의 ꡬ쑰가 κ³„μ†ν•΄μ„œ μˆ˜μ •λ˜λŠ” 자료 ꡬ쑰이기도 ν•˜λ‹€. 이 λ•Œλ¬Έμ— 인덱슀 μΆ”κ°€ μ‹œ DML의 μ„±λŠ₯이 μ €ν•˜λœλ‹€. 

μ΄λ²ˆμ—λŠ” 이 μ„±λŠ₯이 κ³Όμ—° μ–Όλ§ˆλ§ŒνΌ μ €ν•˜λ˜λŠ”μ§€ 직접 쿼리λ₯Ό μ‹€ν–‰ν•΄λ³΄λ©΄μ„œ ν™•μΈν•΄λ³΄κ³ μž ν•œλ‹€.

μš°μ„  ν…Œμ΄λΈ” λ² μ΄μŠ€λŠ” μœ„μ—μ„œ μ΄μš©ν–ˆλ˜ ν…Œμ΄λΈ” 베이슀λ₯Ό κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜λ„λ‘ ν•˜κ³ , 더미 데이터λ₯Ό μΆ”κ°€ν•˜λŠ” 쿼리의 μ†Œμš” μ‹œκ°„μ„ μΈ‘μ •ν•˜λŠ” μ‹μœΌλ‘œ 확인해보겠닀.

기쑴에 λ„£μ–΄λ’€λ˜ 데이터λ₯Ό λͺ¨λ‘ μ‚­μ œν•˜κ³  λ‹€μ‹œ 10만개의 데이터λ₯Ό μƒμ„±ν•΄μ£Όμ—ˆλ‹€. 이 λ•Œ μΈλ±μŠ€λŠ” PKλ₯Ό μ œμ™Έν•˜κ³ λŠ” μ„€μ •ν•˜μ§€ μ•Šμ€ μƒνƒœ.

1λΆ„ 14μ΄ˆκ°€ κ±Έλ Έλ‹€

κ·Έ λ’€ 데이터λ₯Ό μ „λΆ€ 날리고, name μΉΌλŸΌμ— 인덱슀λ₯Ό κ±Έμ–΄ μ€€ λ‹€μŒ λ‹€μ‹œ 더미 데이터 생성 쿼리λ₯Ό μž‘λ™μ‹œμΌ°λ‹€.

1λΆ„ 13초... 음? 더 λΉ λ₯΄λ‹€

μ•Œλ˜ 것과 달리, μ„±λŠ₯이 λΉ„μŠ·ν•˜λ‹€. (사싀 더 λΉ λ₯΄λ‹€)

λ‹€λ§Œ 더 λΉ λ₯΄λ‹€κ³  결둠을 λ‚΄λŠ” 것은 말이 μ•ˆλ˜κ³ , 거의 차이가 μ—†λ‹€κ³  λ³΄λŠ”κ²Œ λ§žλŠ” 것 κ°™λ‹€. 좔가적인 μž‘μ—…μ΄ μ΄λ£¨μ–΄μ§€λŠ” 것은 자λͺ…ν•˜μ§€λ§Œ ν˜„μž¬μ˜ ν…ŒμŠ€νŠΈ ν™˜κ²½μœΌλ‘œλŠ” κ·Έ 것이 뢀각될 μ •λ„λŠ” μ•„λ‹Œλ“―.