검색엔진 sphinx 사용방법

소개


MySQL에서 데이터 검색시 데이터가 많은 경우 자료를 검색하는데 소모되는 시간이 크다.

MySQL의 데이터를 별도의 검색엔진이 인덱싱하여 인덱싱을 검색함으로서 시간 소모를 줄일 수 있다.

여기서는 오픈소스 프로그램인 Sphinx 검색 엔진을 도입해보고, Sphinx 검색엔진과 MySQL, 그리고 PHP와 함께 사용해도록 한다.




설치 환경



OS : Debian 7 64Bit

S/W : MySQL : 5.6.11 / PHP 5.4.4-14

설치할 S/W : Sphinx 2.0.8




Sphinx 설치



1. 파일 받기

Sphinx 파일은 Sphinx 홈페이지를 통해서 받도록 한다.


Sphinx 압축 파일이 아래 경로에 있다는 설정 하에 설치를 진행하며, sphinx-2.0.8-release파일을 앞으로 sphinx라고 지칭하겠다.

/home/download/sphinx.tar.gz 



2. 압축 해제

cd /home/download

tar xvfz sphinx.tar.gz



3. 설치 환경 설정

cd /home/download/sphinx

./configure --prefix=/usr/local/sphinx


--prefix 옵션은 설치시 설치되는 파일의 경로를 지정하는 것으로 여기서는 /usr/local/sphinx에 설치하도록 환경 설정을 바꾼다.



만약 설치 진행시 설치가 안되는 경우 아래처럼 make와 gcc, g++이 설치 되지 않았을 수 있다.

아래 명령어를 통해 프로그램을 설치해준다.

apt-get install gcc

apt-get install g++

apt-get install make

apt-get install build-essential




4. 설치

make -j4 install 


-j4는 멀티 코어인 경우에 적용되면 CPU 활용률을 높여 더 빠르게 설치해도록 해주는 옵션이다.



5. 환경 설정 파일 복사

cd /usr/local/sphinx/etc 

cp sphinx-min.conf.dist sphinx.conf


Sphinx의 환경 설정 파일을 실제로 사용할 이름인 sphinx.conf명으로 복사한다.


이제 Sphinx 프로그램을 설치했다.

다음 2부에서는 간단한 예제와 함께 Sphinx와 MySQL의 연동 및 테스트를 해보도록 하겠다.


소개


1부에서 설치에 이어 2부에서는 Sphinx와 MySQL을 연동해보고 MySQL처럼 쿼리문을 통해 간단한 검색을 해보도록 하자.

참고로 내용이 많아 보이나 복잡하진 않다.



1. SQL 데이터 삽입


우선 간단한 테스트를 위해 아래의 파일을 받아 MySQL에 넣도록 한다.

(Sphinx에서 제공하는 example.sql은 데이터가 영어로만 되어 있으므로 한글 테스트가 안된다. 각자 알아서 참고 자료로 활용하도록 한다.)



test.sql




아래의 명령어를 통해 test DB에 데이터를 넣도록 한다.

만약 test DB가 없다면, 수동으로 만들어줘야 한다. 만약 다른 DB에 넣고 싶다면 test명을 바꾸면 된다.

좀더 자세한건 2013/06/21 - [Database/MySQL] - [MySQL] MySQL DB와 Table 백업 및 복원 관리 글을 읽도록 하자.


mysql -u아이디 -p test < test.sql 



참고로 SQL 예제 데이터는 아래와 같은 내용이며, 데이터는 인터넷에 떠도는 SQL 파일을 예제에 맞게 수정하였다.






2. Sphinx 설정


sphinx.conf 파일을 설정하는데 조금 많은 내용이 있다.

이제 설정파일을 어떻게 설정할지 최대한 간단하게 설명하겠다.


아래 내용중 다루지 않은 설정이 있으니 첨부된 파일이나, 더보기를 통해 확인하여 예제를 진행하도록 한다.




sphinx.conf




1. 설정파일 열기


설정 파일을 연다.

vi /usr/local/sphinx/etc/sphinx.conf 



2. source 설정


sphinx.conf 내용중 source 부분이다.

source zipcode

{

type = mysql


sql_host = localhost

sql_user = DB아이디

sql_pass = DB패스워드

sql_db = DB명

sql_port = 3306 # optional, default is 3306

sql_sock = /tmp/mysql.sock


sql_query_pre = SET NAMES utf8

sql_query = SELECT sn, zipcode, state, city, suburb, address FROM zipcode 


sql_field_string = zipcode

sql_field_string = state

sql_field_string = city

sql_field_string = suburb

sql_field_string = address

}



source 부분에서는 인덱스명을 지정하고, 어떤 데이터를 인덱싱 할 것이지 등등 설정하는 부분이다.


source명 : zipcode (테이블명으로 하는 것이 편하다.)

sql_query_pre : 한글 데이터를 위해 utf8로 설정한다.

sql_query : 어떤 데이터를 인덱싱 하기 위해서는 MySQL로부터 데이터를 가져와야 한다. 자신이 Sphinx를 통해 검색 할 데이터 내용, 검색 결과에 표시할 내용을 포함하도록 MySQL 쿼리문을 작성하면 된다. 단, 첫번째 필드는 절대로 중복되지 않는 Primary Key 또는 Unique Key 값을 줘야 한다. 따라서 이런 필드가 없다면 DB에 만들어야 한다.

sql_field_string : Sphnx가 데이터를 다루기 위해 필요한 선언이라고 생각하면 된다. 즉, 데이터 타입을 지정해주는 것으로 int, string, float 등 이라고 생각하면 된다.

sql_field_string 외에 아래와 같은 데이터 타입이 있다.

sql_attr_uint

sql_attr_bigint

sql_attr_timestamp

...

...



일단은 정상적인 실행이 되는지 확인하기 위한 차원이므로 데이터 형태에 따른 차이와 그에대한 자세한 내용은 다음 3부에서 다루도록 하겠다. (중요하기 때문에 필수적으로 봐야 할 것이다.)




2. index 설정

index 설정에는 한글 설정, 검색 방법 등등의 설정을 다루게 된다.


sphinx.conf 내용중 index 부분이다.

index zipcode

{

source = zipcode

path = /usr/local/sphinx/var/data/zipcode

docinfo = extern

charset_type = utf-8

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

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명 : source명과 동일하게 해준다.

path : 인덱스한 파일이 저장되는 장소다. 위치는 상관없지만 데이터명은 쉽게 알아보기 위해 source명과 동일하게 처리해 주도록 한다.

charset_type : 한글 설정을 위해 utf-8로 해준다.

charset_table : sphinx.conf.dist 파일을 열어보면 utf-8 기본값으로 이미 위처럼 설정되어 있다. 그대로 복사해 사용한다.

ngram_len : 한글 검색을 위해 ngram을 설정해야 한다. 1값으로 설정한다.

ngram_chars : ngram을 검색하기 위해 나눌 단위를 설정한다. 한국어, 중국어, 일본어 모두 검색이 가능하도록 CJK를 사용하며 된다.

(CJK http://sphinxsearch.com/wiki/doku.php?id=charset_tables#cjk_ngram_characters)



n-gram이란?



3. rt 설정

rt를 이용하면 인덱싱된 데이터를 실시간으로 수정 할 수 있다는데, 사용해 본적이 없으며, 본 글에서 rt부분을 다루지 않으므로 패스한다. 
(그냥 기본으로 두거나, 삭제하거나 주석처리한다.)


sphinx.conf 내용중 type이 rt인 index 부분이다.

index zipcodert

{

type = rt

rt_mem_limit = 32M


path = /usr/local/sphinx/var/data/zipcodert

charset_type = utf-8


rt_field = state





그외 sphinx.conf 내용




2. Sphinx 인덱싱


MySQL로부터 검색 및 검색 결과를 줄 데이터를 Sphinx에서 검색이 되도록 인덱싱을 하도록 한다.
명령어는 indexer를 사용하면 된다.

indexer 명령어를 사용하기 위해 아래 위치로 이동한다.
만약 어느 위치에서든 indexer 명령어를 사용하려면 다음 글을 참고하도록 한다. 

2013/06/22 - [Linux Debian 7] - [Debain] PATH 설정


cd /usr/local/sphinx/bin 


인덱싱을 한다.

./indexer --all 


아래와 같은 인덱싱 결과가 나타난다.




그외 아래와 같은 옵션이 있다.


--all : 설정된 모든 리스트를 인덱싱 한다.

--rotate : searchd 프로세스가 서비스 중일때 사용한다.
--merge : 인덱싱된 파일을 합친다.

자세한 사용은 나중에 설명하겠다.



3. SphinxQL 사용


MySQL처럼 Sphinx도 SphinxQL이라는 것이 있다.
이제 SphinxQL을 사용해서 검색이 정상적으로 되는지 확인하도록 하자.

우선 Sphinx에 접속하기 위해 searchd를 실행시키도록 한다. searchd가 중단되어 있다면 접속이 거부된다.

cd /usr/local/sphinx/bin

./searchd 


접속하도록 한다.

mysql -h0 -P9306 



SphinxQL은 MySQL의 쿼리문과는 조금 다르나 아주 기본적인 SELECT 문은 같으므로 일단 아래처럼 한다.

참고할 점은 FROM 뒤에 오는 Table명은 아까 sphinx.conf 설정에 사용했던 index명을 사용하면 된다는 점이다.



SELECT * FROM zipcode; 



이제 아래처럼 검색 결과가 나오면 Sphinx가 정상적으로 실행된 것이다.






Sphinx 3부에서는 SphixQL과 설정파일의 상세한 설정에 대해 설명하겠다.


소개



2부에서는 간단한 예제를 통해 Sphinx를 활용한 검색을 해봤다. (실제로 검색은 아니고 출력일 뿐이었다!!)


이제 SphinxQL을 통해 검색을 하고, 원하는 결과값을 가져오도록 환경설정값을 변경하도록 해보자.

여기서는 2부에서 사용한 test.sql 예제를 가지고 검색해보도록 한다.




간단한 검색



MySQL에서 검색시에는 필드명='찾을값' 형식으로 사용했었다.

하지만 SphinxQL에서는 MATCH() 라는 것을 사용한다.


아래처럼 검색을 하도록 하자.

SELECT * FROM zipcode WHERE MATCH('홍성');





검색한 결과가 나타났다.
이처럼 MATCH()를 사용하면 되는데, 꼭 '' 사이에 검색어를 넣도록 한다.

결과를 보면 20개의 검색결과 값을 가져온 것을 볼 수 있다.
20개 이상의 결과값을 원하면 LIMIT를 사용하면 된다.

SELECT * FROM zipcode WHERE MATCH('홍성') LIMIT 100; 



이제 2개 이상의 단어 검색을 해보자

SELECT * FROM zipcode WHERE MATCH('홍성 갈산'); 




이처럼 여러개를 검색 할 수 있다. 하지만 아래처럼 띄어 쓰기 없이 다시 입력해 보자

SELECT * FROM zipcode WHERE MATCH('홍성갈산'); 





결과값이 동일하다는 것을 알 수 있다.

이것이 바로 n-gram 검색방식의 특징이다. 

'홍성갈산', '갈산홍성', '홍산갈성', '갈홍산성' 등등 글자 순서를 변경해도, 동일한 검색 결과를 가져오는 것을 알게 될 것이다.







구문 검색 (Parse Search)



일단 아래처럼 검색해보자

SELECT * FROM zipcode WHERE MATCH('서산');




검색해보면 서울도 나오고, 전북도 나온다.

전북이 나온 Row를 보면 

전북 전주시 완학동 81 라고 되어있다. 즉 서와 산자가 총 4개 포함되어 있기 때문에 상위 결과값으로 나온것이다. 

1위로 나온 Row는 5자가 포함되어 있음을 학인 할 수 있다. 

이처럼 일치하는 검색이 몇개이고, 얼마나 연관성이 있는지에 따라 weight값이 결정이 되면 가장 높은 weight값 순으로 정렬되어 검색 결과가 나오는 것이다.


그럼 이제 구문으로 검색하기 위해 검색어 양 옆에 따옴표를 붙이도록 하자.

SELECT * FROM zipcode WHERE MATCH('"서산"'); 





원하는 값이 나오는 것을 알 수 있다.
이처럼 쌍따옴표로 검색어를 감싸주면 구문으로 검색이 된다.
응용으로 아래처럼 검색어 2개를 각각 따옴표로 처리해주고, 쉼표 없이 검색해주면 원하는 값을 쉽게 찾을 수 있게된다.

SELECT * FROM zipcode WHERE MATCH('"서산" "고북"'); 







필드 검색 (Field Search)


공주라고 검색해보자

SELECT * FROM zipcode WHERE MATCH('"공주"');




모든 필드에서 공주라는 검색어를 찾게된다.


만약 내가 원하는 것이 suburb에 있는 공주라고 한다면 필드명을 명시해주면 된다.

필드 검색시에는 필드명 앞에 @를 붙이고 한칸 뛰면 된다.

SELECT * FROM zipcode WHERE MATCH('@suburb "공주"'); 




만약 검색할 필드가 2개 이상이라면 아래처럼 가로안에 필드명을 적고 쉼표로 구분해 주면 된다. 주의할점은 쉼표뒤에 공백이 없어야 한다는 점이다.

SELECT * FROM zipcode WHERE MATCH('@(city,suburb) "공주"'); 




필드별 우선 검색


여러 필드를 검색했을때, 검색된 단어가 가장 많은 데이터부터 나오게 된다.

만약 검색시 title에 일치된 값이 다른 필드에서 일치된 값보다 더 중요한 검색 결과값을 가지고 있기 때문에 상위 결과값으로 출력해 줘야 한다면 필드별로 weight값을 주면 된다.


일단 아래처럼 검색해보자

SELECT title, publisher FROM book WHERE MATCH ('"어린이"');





검색 결과에서 title에 어린이 라는 값이 없음에도 publisher에서 어린이가 있기에 상위로 들어왔다.

책을 찾고 싶은데, 제공사의 책 결과값을 가져오는 것은 검색에 비효율적이다.


아래처럼 weight값을 줘보자.

SELECT title, publisher FROM book WHERE MATCH ('"어린이"') OPTION FIELD_WEIGHTS=(title=2,publisher=1);





필드 title에 2값을 주고, publisher에는 1값의 weight 값을 주었다. (1값은 기본값으로 명시하지 않아도 된다.)

따라서 만약 title에 어린이가 일치하면 weight값이 publisher에서 일치하는 값보다 커지게 되어 검색 결과에 상위로 나온다.


이제 검색시 책 위주로 검색을 하도록 한 것이다.



와일드카드 검색 (Wildcard Search)



만약에 일부분을 검색해야 한다면, 와일드카드를 사용하면 된다.

예를 들면 아래처럼 검색 한다면 오로지 7000값만을 찾게 된다.

SELECT title,price FROM book WHERE MATCH('@price 7000'); 





하지만 와일드카드인 *을 사용해서 아래처럼 검색하면 다음과 같은 결과를 얻을 수 있다.

SELECT title,price FROM book WHERE MATCH('@price *7000'); 





바로 앞에 *을 붙이면 검색어와 일차하는 값과 더불어 *가 붙은 부분에서 일치하는 모든 결과값을 가져오게 된다.

하지만 와일드카드를 사용하려면 sphinx.onf 파일에 설정을 따로 해줘야 한다.

설정을 해주지 않는다면, *값을 인식하지 못하게 된다.


설정은 다음과 같이 한다.


index 인덱스명

{

...

...

charset_table = 0..9..........

...


enable_star=1

min_infix_len=2

#min_prefix_len=2


...

...


enable_star는 와일드카드를 사용한다는 것으로 1값이면 사용한다는 뜻이다.

min_infix_len과 min_prefix_len의 길이는 최소 몇개의 값으로 쪼갤 것인가이다.

예를 들면 test를 2로 쪼개면, te, es, st, tes, est, test 등으로 최소 크기인 2부터 인덱싱을 해두는 것이다.

따라서 만약 *st 라고 검색하면 test가 검색되는 것이다.

infix와 prefix의 차이점은 다음 사이트를 참고한다. 

http://sphinxsearch.com/docs/manual-2.0.8.html#conf-min-prefix-len


참고로, enable_star=1값으로 하면 모든 필드에 적용된다. 만약, 특정한 필드만 와일드카드를 사용한다면 아래처럼 설정 할 수 있다.


infix_fields = price, 등등 필드명

prefix_fields = content, 등등 필드명 


주의할 점으로는 min_infix_len와 min_prefix_len은 동시에 사용 할 수 없으므로 둘중 하나만 선택해야 한다. 

(단, infix_fields 처럼 필드별 설정을 한다면 동시사용 가능)



정렬



MySQL에서 처럼 정렬 기능이 필요 할때 ORDER BY 구문을 사용하면 된다.

하지만 조금 다른점은 반드시 ASC나 DESC 둘중 하나를 반드시 써줘야 한다. 보통 MySQL에서는 ASC를 생략하고 쓰는데, Sphinx에서는 생략을 할 수 없다는 것이다.


이제 아래처럼 정렬해보자.

SELECT * FROM shop ORDER BY price DESC;





가격이 높은 순으로 잘 정렬되어 있다.

그럼 이제 정렬할 필드를 s_price로 바꿔 다시 검색해보자.

SELECT * FROM shop ORDER BY s_price DESC;





분명 price와 s_price는 필드명만 다를뿐 같은 값을 가진 필드이지만, 정렬된 결과값은 서로 다르다.

이제 이런 이유를 잠시 설명해보기 위해 sphinx.conf에 설정된 값을 볼 필요가 있다.



source shop

{

...

...


         sql_query_pre = SET NAMES utf8

         sql_query = SELECT sn, price, price AS s_price, pack_count, item_name, npc_name FROM shop WHERE sphinx_yn='Y'


         sql_attr_bigint         = price

         sql_field_string        = s_price

         sql_field_string        = pack_count

         sql_field_string        = pack_count

         sql_field_string        = item_name

         sql_field_string        = npc_name


...

...

}


Sphinx에서는 크게 2개의 타입이 있는데 Attribute와 Field다.

Attribute : Full-Text 기능 없음. 정렬, 필터 기능 있음.
Field : Full-Text 기능을 지원.

설정을 보면 2개의 타입 sql_arttr_bigint와 sql_field_string이 각각 price와 s_price의 타입으로 선어되어 있다.
price는 Attribute로, s_price는 Field로 선언된 것이다.

따라서 정렬을 할때는 sql_attr_bigint 타입으로 선언된 price로 해야 되는 것이다.
그리고 아래처럼 price로 검색을 한다면 없는 필드라고 에러가 출력된다.

SELECT * FROM shop WHERE MATCH('@price 1000000'); 





하지만, Full-Text를 통한 검색이 안되는 것뿐이다. 즉, MATCH() 함수를 통한 검색이 안되는 것이다.

아래 명령어처럼 일반적인 MySQL 쿼리문처럼 검색을 하면, 가능하다.
따라서 구간 검색이라던지, 날짜 등의 정렬 등이 필요한 경우에 Attribute 타입을 사용하는 것이다.

SELECT * FROM shop WHERE price>= 72 and price <= 200; 





이 방법을 사용함으로서, 특정 구간의 값들이나, 정렬등을 통해 결과값을 구할 수 있다.
따라서 검색할 대상이 어느 필드인지, 검색하지않고 결과값에만 나와도 되는 필드인지, 검색시 어떤 유형의 검색을 허용 할지에 따라, 타입을 잘 골라야 할 것이다.

예를 들면 날짜 데이터의 경우 검색할 필드가 아니고, 결과값에만 출력이 되는 값이라면, 굳이 Attribute 타입이 아닌 Field 타입으로 해도 될 것이다.
하지만, 정렬, 구간 등의 검색이 필요하다면, sql_attr_timestamp로 선언해야 될 것이다.



이상으로 간단한 SphinxQL의 사용법을 사용해 봤다.

더 다양한 옵션과 방법은 Sphinx 홈페이지의 문서를 통해 참조하면 되겠다.


xQL Reference : http://sphinxsearch.com/docs/manual-2.0.8.html#sphinxql-reference

Full-Text Query : http://sphinxsearch.com/docs/manual-2.0.8.html#extended-syntax


참고로 여기서 다룬 xQL은 SphinxAPI를 사용하는 것보다 빠르다고 한다.

다음 4부에서는 인덱싱을 하는 방법을 다루겠다.











-------------------------------------------------------
안녕하세요 님을 위한 공간

-------------------------------------------------------


해당 DB가 없기때문에 직접 테스트는 하지 못하였으며, 정확한 상황을 알 수 없기 때문에 추측의 글을 올립니다.

현재 블로그에 있는 conf파일과 sql문을 이용했을때 검색이 잘 되는 것으로 보아, 이 파일들을 통해 서버에 적용된 값과 비교해보셔야 될것 같습니다.

예 를 들면 블로그에 올려진 test.sql에서 shop 테이블의 내용을 "아이폰5 차량용 케이블", "아이폰5 케이블"로 고치고 블로그의 conf파일을 이용해서 테스트한 결과 제대로 검색되어 나오는 것을 확인했습니다. (아래 이미지 참조)






참고로 아래의 글을 남기셨는데요...
---------------------------------------------
스핑크스 검색엔진 db에 붙어서 쿼리를 날려보면 
아이폰5 차량용 케이블,아이폰5 케이블 이런 문자열을 가지는 레코드는 틀림없이 존재하는데 말이죠. 
---------------------------------------------
이렇게 쓰신 뜻은 단순히 select * from xxx으로 데이터가 있는지 확인 하신 거라면 앞서 말씀드린 것처럼 xQL을 사용해서 위 이미지 처럼 정확한 쿼리문을 통해 결과값이 잘 나오는지 먼저 확인해야 합니다.

만약 서버에 적용되있는 값이 위와 같은 쿼리문으로 검색이 안되다면 분명, Sphinx 설정이 잘 못 된 것입니다.
일단 블로그에 있는 기본적인 conf값과 sql로 테스트를 해보시고, 서버에 적용된 값으로 conf값을 하나씩 변경, 다시 인덱싱 하시면서 원인을 찾으시는게 빠를 듯 합니다.
하지만 쿼리문이 결과값을 잘 가져온다면, API에서 문제가 생긴다는 것인데 API에 보내는 쿼리문을 로고로 찍어서 다시 한번 확인하시는게 좋겠습니다.


PS. xQL이 아닌 API 방식을 도입하신것 같은데, API 부분은 경험이 없어 도움을 못드리겠네요. xQL로 오시는 것이 어떠신지 ^^;;


소개


3부에서는 검색을 통해 결과값을 가져왔다.

이제 4부에서는 데이터를 인덱싱하는 방법과, 데몬 등록을 통한 주기적인 인덱싱 설정을 통해, Sphinx의 인덱스 데이터를 유지 하는 방법을 다루겠다.


명령어를 PATH에 등록하지 않았다면 아래 링크를 통해서 등록을 해주거나, 

2013/06/22 - [Linux Debian 7] - [Debain] 어느 위치에서든 프로그램 실행시키도록 PATH 설정하기

아래처럼 명령어가 있는 디렉토리에가서 명령어를 실행하면 된다.

cd /usr/local/sphinx/bin

./indexer --all --rotate --merge --quient --silent --config




전체 인덱싱



전체 인덱싱은 말 그대로, 환경설정에 있는 모든 인덱스를 인덱싱 하는 것이다.

indexer --all


만약, SphinxQL을 사용하기 위해, 혹은 서비스 제공을 위해 searchd 프로세스를 실행시켰다면, 항상 --rotate를 추가로 붙여주면 된다. 그렇지 않다면 에러가 발생할 것이다.

indexer --all --rotate 


전체 인덱싱은 모든 인덱스의 데이터를 인덱싱하기때문에 인덱스의 크기와 데이터의 크기에 의해 시간이 결정될 것이다.

(main + delta 방식인 경우 전체 인덱싱 하고나서 merge 명령어를 해줘야 delta에서 인덱싱된 데이터를 Sphinx에 반영하게 된다.)




부분 인덱싱



환경설정에 설정된 인덱스 리스트가 많다면, 전체 인엑스보다는 특정 인덱스만을 인덱싱 함으로서 시간을 줄일 수 있다.

indexer 인덱스명 


예를 들면, 예제에서 제공된 sphinx.conf에는 zipcode, book, delta, shop이라는 4가지의 인덱스가 있다.

이 이름을 통해 인덱싱을 하면 되는 것이다.

indexer delta




인덱스 업데이트 (Delta Index Updates)



인덱스 업데이트는 3가지가 있다.

위에 나온 것처럼 데이터가 적은경우 전체 또는 부분 인덱싱을 통해 인덱스 전체를 처음부터 다시  생성하는 방법과 라이브 인덱싱, 그리고 Delta 방식 인덱싱이 있다.

여기서는 Delta 업데이트 방법을 소개한다.


Delta 업데이트는  Main + Delta 방식으로, 먼저 주된 인덱스를 Main이 갖게하고, MySQL에 추가된 데이터를 Delta에서 인덱싱 하도록 한다.

그리고나서 Delta의 데이터를 Main에 건네주어 데이터를 합친다.

따라서 추가된 데이터만 인덱싱하면 되므로 인덱스 전체를 인덱싱 할 필요가 없다.


예제로 첨부한 sphinx.conf에 있는 인덱스들 중에 shop 인덱스가 Delta 업데이트 방식을 사용한다.

 source shop

 {

         type                    = mysql


         sql_host                = localhost

         sql_user                = debian

         sql_pass                = anne$1929

         sql_db                  = test

         sql_port                = 3306  # optional, default is 3306

         sql_sock                = /tmp/mysql.sock


         sql_query_pre = SET NAMES utf8

         sql_query = SELECT sn, price, price AS s_price, pack_count, item_name, npc_name FROM shop WHERE sphinx_yn='Y'


         sql_attr_bigint         = price

         sql_field_string        = s_price

         sql_field_string        = pack_count

         sql_field_string        = item_name

         sql_field_string        = npc_name


 #       sql_query_info          = SELECT * FROM shop WHERE sn=$id

 }


 source delta : shop

 {

         sql_query_pre = SET NAMES utf8

         sql_query = SELECT sn, price, price AS s_price, pack_count, item_name, npc_name FROM shop WHERE sphinx_yn='N'

         sql_query_post = UPDATE shop SET sphinx_yn='Y' WHERE sphinx_yn='N'

         sql_query_killlist = SELECT sn FROM shop WHERE sphinx_yn='D'

 }



 index shop

 {

         source                  = shop

         path                    = /usr/local/sphinx/var/data/shop

         docinfo                 = extern

         charset_type            = utf-8

         charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F


          ...

          ...


 }

 index delta : shop
 {
         source                  = delta
         path                    = /usr/local/sphinx/var/data/dalta
         docinfo                 = extern
         charset_type            = utf-8
         charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F

          ...
          ...

 }



설정을 보면 main + delta에서 main에 해당하는 것이 shop다. 그리 delta에 해당하는 것이 delta이다.

자심 아래의 쿼리 일부분을 보면 아래처럼 되어 있다.


main의 sql_query : ... WHERE sphinx_yn='Y'

delta의 sql_query : ... WHERE sphinx_yn='N'

delta의 sql_query_post : UPDATE shop SET sphinx_yn='Y' WHERE sphinx_yn='N'


즉, MySQL에서 main이 인덱싱하는 부분은 Y값이고 delta에서 인덱싱 하는 부분은 N값 부분이다.

그리고 delta가 인덱스를  마치면 sql_query_post를 통해 N값을 Y값으로 바꿔줌으로서 다시는 delta 인덱싱할때 인덱스 범위에 잡히지 않도록 해준다.

이로서 추가된 데이터를 인덱스하는 것이다.


다음은 이 과정을 명령어로 나타낸 것이다. (서비스중이면 --rotate 추가할 것)


1. delta 인덱싱

indexer delta 


2. main + delta 인덱스 데이터 합치기

indexer --merge shop delta 



만약 MySQL에서 일부 데이터가 변경된 것을 Sphinx에 반영하려면 변경된 데이터의 sphinx_yn값을 delta에서 인덱싱 할 수 있도록 Y값을 N값으로 변경만 해주면 데이터를 업데이트 할 수 있다.


만약, MySQL의 데이터중 일부가 삭제 됐다면, 전체, 혹인 부분 인덱싱을 해야한다. 하지만 삭제하기전에 Sphinx에서 데이터를 먼저 빼도록 할 수 있다.

바로 delta의 sql_query_killlist가 그 역할을 한다.

delta의 sql_query_killlist : SELECT sn FROM shop WHERE sphinx_yn='D'

sphinx_yn의 값을 D로 해두면, killlist의 인덱스 범위내로 들어온다. 이때, 인덱스의 기준이 되는 id값을 넘겨주기만 하면 된다. (여기 예제에서는 id가 sn값이다.)

이제 delta를 인덱싱하고 merge지를 하면 데이터가 사라져있다.




시작 등록



리눅스가 재시작 했을 경우 Sphinx가 자동으로 서비스를 시작하게 만들기 위해서 간단한 쉘스크립트를 등록 해야한다.


먼저, 시작할 스크립트는 init.d에 위치해 있어야 하므로 디렉토리를 이동한다.

cd /etc/init.d

vi searchd



파일 내용은 아래처럼 적는다. (만약 환경 설정이 기본디렉토리에 없다면, --config 옵션으로 환경설정 위치를 잡아주도록 한다.)

#!/bin/bash

/usr/local/sphinx/bin/searchd



시스템이 파일에 접근 가능토록 권한을 설정한다.

chmod 755 searchd



이제 데비안을 재시작해도 searchd가 자동으로 시작된다.

만약 서비스로도 만들고 싶다면 다음 글을 참고 한다.

2013/05/04 - [Linux Debian 7] - [Debian] 부팅시 script 및 서비스 시작하도록 등록하기




Crontab 등록



일정 시간마다 업데이트를 해야 할 경우 crontab에 등록하여 업데이트를 실행시킬 수 있다.


1. 전체 업데이트

먼저 전체 업데이를 만들기 위해 Shellscript를 만들도록 하자.

cd /usr/local/sphinx/bin

vi sphinx_total_updater.sh


sphinx_total_updater.sh 내용에는 아래처럼 전체 업데이트 명령어를 넣도록 한다.

#!/bin/bash

/usr/local/sphinx/bin/indexer --all --rotate

sleep 2

/usr/local/sphinx/bin/indexer --merge shop delta --rotate 




2. 부분 업데이트

부분적으로 업데이트를 하기 위해 Shellscript를 만들도록 하자.

cd /usr/local/sphinx/bin

vi sphinx_partial_updater.sh


sphinx_partial_updater.sh 내용에는 아래처럼 부분 업데이트 명령어를 넣도록 한다.

#!/bin/bash

/usr/local/sphinx/bin/indexer delta --rotate

sleep 2

/usr/local/sphinx/bin/indexer --merge shop delta --rotate 



3. Crontab 등록

crontab -e 


만든 스크립트 경로를 통해 크론을 실행하도록 한다. (아래의 예는 매 분마다 실행한다.)

* * * * * /usr/local/sphinx/bin/sphinx_total_updater.sh

* * * * * /usr/local/sphinx/bin/sphinx_partial_updater.sh 


일정한 시간 설정 등은 다음 링크를 참고하도록 한다.

2013/01/09 - [Linux] - [Linux] crontab




이상으로 Sphinx 검색 엔진을 통해 데이터를 인덱싱하고, 업데이트 및 삭제 그리고, 리눅스가 알아서 업데이트 되도록 했다.

다음 5부에서는 웹서비스를 위해 PHP와의 연결 부분을 다루겠다.


1. 소개


4부까지 했다면, 현재 Sphinx 검색 엔진이 설치되어 있고, 예제 test.sql 데이터를 통해 검색도 잘 될 것이다.

PHP에서 MySQL 접속을 통해 사용했던 것처럼 사용하면 되므로 아주 간단하다.




2. 접속


아래와 같은 mysql_connect를 사용하여 접속하는 예제이다.


아쉽게도 현재 Sphinx는 ID와 Password를 설정 할 수 없다고 한다.

따라서 ID와 Password는 비워두고 접속하면 된다.


<meta charset="utf-8">

<?php



 $hostname = "127.0.0.1:9306";

 $username = "";

 $passwd = "";

 $dbname = "test";

 $connect = mysql_connect($hostname, $username, $passwd) or die("Failed");

 $result = mysql_select_db($dbname, $connect);

 echo "접속 여부? : ".$result;


 mysql_query("set names utf8");     // 결과값이 한글인 경우 사용


 $sql="SELECT title FROM book LIMIT 10";

 $rs =  mysql_query($sql, $connect);   // sql문 실행


 while($info=mysql_fetch_array($rs)){

         echo $info['title']."<br>";

 }


 ?>



웹브라우저를 통해 아래처럼 접속하면 간단하게 검색된 결과가 나온다.




이제 검색 엔진 서비스가 모두 완료되었다.

MySQL이 like 문으로 힘들게 데이터를 검색하는 것보다 Sphinx를 사용하면 무척 빠르고, 정확한 결과값을 내준다.

따라서 데이터가 많다면, 검색 엔진을 사용하는 것이 큰 이익이 될 것이다.


위로 스크롤