오픈소스 검색엔진 뭐 사용할까? 가벼운 스핑크스(Sphinx) 사용해보자 (인트로)
Sphinx #1 :: 스핑크스 설치 및 환경설정
sphinx #2 :: 스핑스크 쿼리 테스트 (sphinxQL + PHP연동)
sphinx #3 :: MySQL에서 스핑크스 직접 join하기 (sphinxSE)
sphinx #4 :: 스핑크스 주기적 인덱스 방법 (delta + crontab)

 

스핑크스의 인덱스 순서는 아래와 같습니다. 

  1. 환경설정 : sphinx.conf
  2. 인덱스 실행 : indexser --all
  3. 데몬 실행 : searchd
  4. 인덱스 재 실행 : indexser --all -- rotate (searchd 실행중인 경우에는 --ratate 옵션 부여)

그럼 인덱스 실행은 얼마나 자주 해야 하는걸까요?
실시간으로 응대해야 한다면 전혀 다루지 않았던 RT형식으로 해야 할 겁니다.
제가 구현하고자하는 통합검색에는 실시간까지는 필요 없고 그래도 최대 1~5분 주기로는 인덱스 작업을 해줘야 합니다.

그러나 테이블 하나가 100만 건만 된다고 하더라도 인덱스하는 시간이 수분~수십분이 걸립니다.
100만건 되는 테이블이 다수개라면 서비스 자체가 할 수 없게 됩니다.

이를 해결하기 위해서 아래와 같이 인덱스 실행 스케쥴을 잡습니다.

  1. 최초 한번은 전체 인덱스 실행한다.
  2. 특정 주기(1~5분)마다 부분 인덱스를 실행한다.
  3. 부분 인덱스 테이블과 전체인덱스 테이블을 합친다.
  4. 특정 주기(1일~1주일) 마다 전체 인덱스를 실행한다.
  5. 2번 ~ 4번을 반복한다.

바로 구현해보도록 하겠습니다. 

 

 

Delta index updates

만약 10,000,000개의 데이터가 저장된 게시물이 있고, 
하루에 1,000개의 새 게시물이 발생되는 게시판이 있다고 가정해봅시다.

이런 경우 "main + delta" 체계를 구현할수 있도록 스핑스크에서 제공하고 있습니다.

 

우선 mysql에 테이블을 생성합니다.
새로 추가된 row를 알 수 있도록 해주는 테이블을 하나 생성합니다. 

# mysql에서 진행합니다.  

# 스핑크스 DB 이동 ( [sphinx #3 :: sphinxSE 사용하기] 편에서 만들었었죠 )
mysql> use sphinx;   

# 테이블 생성
mysql> CREATE TABLE `config_max_id` ( 
	`source_name` varchar(30) NOT NULL COMMENT '스핑크스 소스이름',  
    `max_id` int(10) unsigned NOT NULL COMMENT '해당테이블 max PK 값',  
    `edit_date` datetime DEFAULT NULL COMMENT '수정일',  
    PRIMARY KEY (`source_name`),  
    KEY `idx_sph_type` (`source_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='스핑크스 delta 제어 테이블';

 

다음으로 스핑스크 환경설정(shphinx.conf)을 수정 및 추가합니다. 

]$ vi /etc/sphinx/sphinx.conf


#############################################################################
## source settings
#############################################################################

source sph_zip_code
{
        type                    = mysql
        sql_host                = mysql ip주소(localhost)
        sql_user                = 계정명
        sql_pass                = 패스워드
        sql_db                  = DB명
        sql_port                = 3306
        sql_query_pre = SET NAMES utf8
  sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre=  REPLACE INTO sphinx.config_max_id SELECT 'sph_zip_code', MAX(seq), now() FROM test.zip_code

        sql_query = \
                SELECT seq, zip_code, add1, add2, add3, address \
                FROM zip_code \
                WHERE seq <= (SELECT max_id FROM sphinx.config_max_id WHERE source_name='sph_zip_code' )

        sql_field_string        = zip_code
        sql_field_string        = add1
        sql_field_string        = add3
        sql_field_string        = address
}


source sph_zip_code_delta : sph_zip_code {
       sql_query_pre = SET NAMES utf8
       sql_query_pre = SET SESSION
       query_cache_type=OFF
       sql_query = \
             SELECT seq, zip_code, add1, add2, add3, address \
             FROM test.zip_code \
             WHERE seq > (SELECT max_id FROM sphinx.config_max_id WHERE source_name='sph_zip_code' )
}

#############################################################################
## index settings
#############################################################################
index sph_zip_code
{
       source = sph_zip_code
       path = /var/lib/sphinx/data/sph_zip_code
       docinfo = extern
       charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
       ngram_len = 1
       min_infix_len = 2

       ngram_chars = U+4E00..U+9FBB, U+3400..U+4DB5, U+20000..U+2A6D6, U+FA0E, U+FA0F, U+FA11, U+FA13, U+FA14, U+FA1F, U+FA21, U+FA23, U+FA24, U+FA27, U+FA28, U+FA29, U+3105..U+312C, U+31A0..U+31B7, U+3041, U+3043, U+3045, U+3047, U+3049, U+304B, U+304D, U+304F, U+3051, U+3053, U+3055, U+3057, U+3059, U+305B, U+305D, U+305F, U+3061, U+3063, U+3066, U+3068, U+306A..U+306F, U+3072, U+3075, U+3078, U+307B, U+307E..U+3083, U+3085, U+3087, U+3089..U+308E, U+3090..U+3093, U+30A1, U+30A3, U+30A5, U+30A7, U+30A9, U+30AD, U+30AF, U+30B3, U+30B5, U+30BB, U+30BD, U+30BF, U+30C1, U+30C3, U+30C4, U+30C6, U+30CA, U+30CB, U+30CD, U+30CE, U+30DE, U+30DF, U+30E1, U+30E2, U+30E3, U+30E5, U+30E7, U+30EE, U+30F0..U+30F3, U+30F5, U+30F6, U+31F0, U+31F1, U+31F2, U+31F3, U+31F4, U+31F5, U+31F6, U+31F7, U+31F8, U+31F9, U+31FA, U+31FB, U+31FC, U+31FD, U+31FE, U+31FF, U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9, U+A000..U+A48C, U+A492..U+A4C6
}

index sph_zip_code_delta : sph_zip_code
{
       source = delta
       path = /var/lib/sphinx/data/sph_zip_code_delta
}

 

빨간 부분이 수정 또는 추가된 부분입니다.
source sph_zip_code 에서 sql_query 실행하기 전에 zip_code의 max(seq) 값을 위에서 config_max_id 테이블에 update 시켜놓았습니다.

sph_zip_code_delta 는 1~5분 마다 실행되도록 cron에 설정할 예정이고, 
이는, config_max_id 테이블에 저장되어 있는 max(seq) 보다 큰 데이터만 인덱스를 실행하게 됩니다.

 

전체 인덱스를 실행합니다.

]$ indexer --all -rotate

이렇게 하면 sph_zip_codesph_zip_code_delta 둘 다 실행됩니다.

 

다음으로 부분 인덱스를 실행합니다.

# 부분 인덱스 실행
]$ indexer sph_zip_code_delta --rotate

# 원형과 delta를 병합합니다.
]$ indexer --merge sph_zip_code sph_zip_code_delta --rotate

 

 

crontab 등록

크론에는 당연히 두개를 등록해야 합니다.
한개는 전체 인덱스 설정이고,
다른 하나는 부분 인덱스 설정입니다. 

 

[전체 인덱스]

]$ mkdir /etc/sphinx/bin
]$ vi /etc/sphinx/bin/all_update.sh



스크립트 파일 생성후 아래와 같이 전체 인덱스 실행 명령어 기술하고 저장합니다.

#!/bin/bash 
indexer --all --rotate

 

스크립트 파일 권한 부여 후 테스트 해보시고 잘 동작 잘 되는지 테스트합니다.   

# 권한 부여
]$ chmod 755 /etc/sphinx/bin/all_update.sh   

#실행 테스트
]$ /etc/sphinx/bin/all_update.sh

 

[부분 인덱스]

]$ vi /etc/sphinx/bin/partition_update.sh
#!/bin/bash 

# zip_code
indexer sph_zip_code_delta --rotate
indexer --merge sph_zip_code sph_zip_code_delta --rotate

스크립트 파일 생성후 아래와 같이 부분 인덱스 실행 명령어 기술하고 저장합니다.


직접 crontab 등록해도 되는데 왜 스크립트 파일로 만들어서 귀찮게 하는걸까요?
이유는 인덱싱 해야 할 테이블이 zip_code 하나만 있는게 아니기 때문입니다. 
게시물 테이블, 상품 등록 테이블 등 다수개의 테이블을 각각 sphinx.conf에 main, delta 쌍으로 등록해야 하고 각각을 indexer로 등록해야 하는데 crontab에는 해당 스크립트 파일 하나만 걸어주고 추가되는 인덱스들은 해당 스크립트에 명령어를 추가 하려고 합니다.

# 권한 부여 
]$ chmod 755 /etc/sphinx/bin/partition_update.sh    

#실행 테스트 
]$ /etc/sphinx/bin/partition_update.sh

 

[crontab]

]$ vi /etc/crontab

# [스핑크스] 전체 인덱스 (매일 1시 실행)
0 1 * * * root /etc/sphinx/bin/all_update.sh

# [스핑크스] 부분 인덱스 (매일 6시~23시 중 5분마다 실행)
*/5 6-23* * * * root /etc/sphinx/bin/partition_update.sh

 

 

마무리

제가 드리고 싶은 이야기는 다 한 것 같습니다.
사이트내의 간략한 통합검색 같은 기능은 가벼고 쉬운 스핑크스로도 충분하다고 생각이 듭니다. 

사용하다가 부족하다 싶으면 다른 검색엔진 들고 찾아뵙도록 하겠습니다.

다들 꼭 성공하세요~ 

 


[참고]
https://sacstory.tistory.com/entry/Debain-%EA%B2%80%EC%83%89-%EC%97%94%EC%A7%84-Sphinx-%ED%95%9C%EA%B8%80-%EC%84%A4%EC%A0%95

http://sphinxsearch.com/

http://sphinxsearch.com/docs/manual-2.2.11.html#extended-syntax

 


본 글은 나의 다른 블로그(폐쇄예정) 에서 작성한 글을 이관한 포스팅입니다. (2018년 12월 6일 작성)