DB/DB 공통 관리

4. Sphinx, MySQL 에서 스핑크스 직접 join하기 (sphinxSE)

서버엔지니어 2023. 3. 26.
728x90

이전 시간까지 스핑크스를 설치했고, sphinxQL을 이용하여 주로 사용하는 쿼리를 테스트 해봤으며, PHP 연동까지도 해봤습니다.

이번 장은 sphinxSE를 사용하는 방법을 설명하고자 합니다.
이글 부터 보시는 분들은 꼭 #1 부터 읽어주시기 바랍니다.

sphinxSE는 searchd(스핑크스 데몬)와 대화하여 텍스트 검색을 가능하게 하는 스토리 엔진입니다. 
스핑크스와 sphinxSE는 mariaDB에 내장된 전체 텍스트 검색(full text) 보다 빠르고 사용자 정의 가능한 대안으로 사용됩니다. 

 

MariaDB에서 sphinxSE 설치하기


# mysql에 스핑크스 엔진 설치
mysql> INSTALL SONAME 'ha_sphinx'; 
mysql> SHOW ENGINES\G
+--------------------+---------+--------------+------+------------+
| Engine             | Support |  Transactions | XA   | Savepoints |
+--------------------+---------+--------------+------+------------+
| CSV                | YES     | NO           | NO   | NO         |
| MRG_MyISAM         | YES     | NO           | NO   | NO         |
| MyISAM             | YES     | NO           | NO   | NO         |
| SEQUENCE           | YES     | YES          | NO   | YES        |
| SPHINX             | YES     | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | NO           | NO   | NO         |
| MEMORY             | YES     | NO           | NO   | NO         |
| Aria               | YES     | NO           | NO   | NO         |
| FEDERATED          | YES     | YES          | NO   | YES        |
| InnoDB             | DEFAULT | YES          | YES  | YES        |
+--------------------+---------+--------------+------+------------+
 

MySQL에 접속하여 ha_sphinx를 install 하면 준비과정은 끝났습니다.
MySQL의 스토리엔진 중 스핑크스는 단독으로 사용할 수 없습니다.  반드시 sphinx가 설치되어 있어야 합니다.

참고자료로 메뉴얼 링크 걸어 놓았습니다. 참고하세요.

MariaDB의 sphinx 엔진 문서 바로가기
sphinx의 sphinxSE 관련 문서 바로기


 

MariaDB에서 sphinxSE 사용하기


# MySQL 접속]
# mysql -uroot -p  
 
# sphinx DB 생성
mysql> create databse sphinx; 
 
# sphinx DB 사용
mysql> use teset  
 
# sph_zip_code 테이블 생성
mysql> CREATE TABLE `sph_zip_code` (  
`id` BIGINT(20) UNSIGNED NOT NULL,  
    `weight` INT(11) NOT NULL,  
    `query` VARCHAR(3072) NOT NULL,  
    `group_id` INT(11) DEFAULT NULL,  
    `address` VARCHAR(100) DEFAULT NULL,  
    KEY `query` (`query`(1024))
) ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx://스핑크스설치된IP:9312/sph_zip_code';
 

테이블 생성 시 컬럼 중 id, weight, query, group_id는 반드시 만들어야 합니다. 순서도 똑같이 하세요.

그 아래에 스핑크스 환경설정에서 sql_field_string=컬럼명 설정한 것을 생성하는것은 옵션입니다. 
테스트 차원으로 address 컬럼만 만들어 보겠습니다. 
우선은 만들기만 해보고 다음 쿼리 실행할때 차이점을 설명 드릴게요.

engine = sphinx  # 스핑크스 엔진 사용한다 명시합니다.
connection='sphinx://127.0.0.1:9312/sph_zip_code'
- 127.0.0.1  # 스핑크스 설치한 서버의 IP
- 9312    # 포트(sphinx.conf 파일에서 listen=9312 라고 설정했었습니다. 그 포트 번호에요)
- sph_zip_code # sphinxconf파일에서 source명 입니다.
지금까지는 뭐하는건지 모르겠죠.
쿼리 하나하나 같이 질의하다보면 윤곽 보이실 겁니다.

mysql> select * from sph_zip_code;
Empty set (0.01 sec)
생성한 테이블을 select하면 비어있습니다.

 

where 조건을 태워보죠

mysql> SELECT * FROM sphinx.sph_zip_code WHERE `query` = '"운산";mode=ext2'; 
 
+-------+--------+--------------------+----------+-----------------------------------------------------+
| id    | weight | query              | group_id | address                                             |
+-------+--------+--------------------+----------+-----------------------------------------------------+
| 15004 |   4536 | "서산";mode=ext2   |        0 | 충남 서산시 서산대산우체국사서함                              |
| 15003 |   4535 | "서산";mode=ext2   |        0 | 충남 서산시 서산우체국사서함                                 |
| 15007 |   4535 | "서산";mode=ext2   |        0 | 충남 서산시 읍내동 서산경찰서                                |
| 14989 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 동문동 서산우체국                                |
| 15005 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 예천동 대전지방법원서산지원                         |
| 15006 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 동문동 KT서산지사                               |
| 15008 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 읍내동 서산시청                                 |
| 15011 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 예천동 대전지방검찰청서산지청                       |
| 15016 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 대산읍 대죽리 (주)엘지화학대산공장                   |
| 15029 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 읍내동 서산읍내현대아파트                          |
| 15031 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 석남동 서산센스빌아파트 (101~117동)                |
| 15039 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 읍내동 서산롯데캐슬아파트 (101~114동)               |
| 15040 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 음암면 부장리 서산수림미소가아파트 (101~115동)        |
| 15099 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 운산면 갈산리                                   |
| 15105 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 운산면 고산리                                   |
| 15159 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 대산읍 대산리                                   |
| 15163 |   4533 | "서산";mode=ext2   |        0 | 충남 서산시 대산읍 운산리                                   |
| 14996 |   4532 | "서산";mode=ext2   |        0 | 충남 서산시 갈산동                                        |
| 15012 |   4532 | "서산";mode=ext2   |        0 | 충남 서산시 대산읍 삼성토탈(주)                              |
| 15013 |   4532 | "서산";mode=ext2   |        0 | 충남 서산시 대산읍 대죽리 (주)씨텍                           |
+-------+--------+--------------------+----------+-----------------------------------------------------+
 

드디어 실체가 나왔습니다.

id : 스핑크스에서 seq(PK) 값입니다. 
weight : 가중치 값입니다. 
첫번째 row를 보면 "운산" 을 검색한 결과에 매칭되는 점수가 4536점 이다 라고 생각하시면 됩니다. 
query : 질의한 쿼리를 보여줍니다. 
address : 테이블 생성 시 옵션 컬럼이라고 했었죠.  
테이블 생성시 add1, add2 등 컬럼을 만들었다면 결과에 해당되는 데이터를 자동으로 매핑되어 확인 할 수 있습니다.
결국 query="어쩌구"를 통해서 스핑크스에 인덱싱된 데이터를 가져올 수 있는 구조입니다. 

# mysql에서 아래의 쿼리는
mysql> SELECT * FROM sphinx.sph_zip_code WHERE `query` = '"운산";mode=ext2';
 
#sphinxQL에서 아래의 쿼리와 동일합니다. 
sphinxQL> select * from sph_zip_code where match('"운산"');
쿼리 문법은 아래에서 자세히 나올 거니까 우선은 '이런식으로 검색하는구나' 정도로만 이해하고 넘어갑시다.

 

 

shpinxSE 구조도


지금까지 우리가 한 행위를 구조화 해보도록 하겠습니다. 



기존 MySQL 이 설치되어 있는 상태로 (왼쪽 파란 영역)
ⓐ zip_code 테이블을 스핑크스 태우고 싶었습니다. 

그래서 스핑크스를 설치했고 (오른쪽 주황색 영역)
sphinx.conf 파일을 생성하여 indexer 유틸로 ⓑ스핑크스 인덱스 테이블(sph_zip_code)을 생성시켰죠.
ⓑsph_zip_code에 데이터 잘 들어갔는지 확인 하기 위해서 ⓔ sphinxQL 접속도 해봤고
ⓔ sphinxQL 에서 match 쿼리를 사용하여 검색 쿼리도 테스트 해봤습니다. 
php에서 ⓔ sphinxQL를 바로 connection 하여 사용할 수 있는 것도 확인했고요. 

궁극적으로 사용하고 싶었던 것은 sphinxSE 였습니다. 
MySQL 데이터베이스에 ⓒsph_zip_code를  sphinx 엔진 타입으로 생성하여 ⓑsph_zip_code를 링크? 시켰고,
데이터 확인을 위해
ⓓmysql 접속을 하여  query='"운산";mode=ext2'" 를 사용하여
ⓒ를통해 ⓑ의 데이터를 가지고도 왔습니다.
이제 좀 감이 잡히시나요? 
안잡히시면... 저의 표현력을 탓하면서 계속 반복해서 읽어주세요 ㅠ.ㅠ

 

 

inner join 사용하기

 

sphinxSE를 사용하는 이유는 mysql의 다른 테이블과 join 해서 바로 사용하려고 하는게 아닐까 싶습니다. 
저는 그렇습니다. ㅎ

바로 inner join 한번 해봅시다.

mysql> SELECT a.*, b.* FROM sphinx.sph_zip_code a, test.`zip_code` b 
WHERE a.id = b.seq   
    AND `query` = '서산;mode=ext2'   
    AND chang_date > 20040517 LIMIT 10;   
 
+-------+--------+------------------+----------+----------------------------------------------------------------------------+-------+----------+--------+--------+-----------+-----------+-----------+------+--------+---------------------------------------+------------+----------------------------------------------------------------------------+
| id    | weight | query            | group_id | address                                                                    | seq   | zip_code | sub_no | add1   | add2      | add3      | add4      | add5 | number | bilding                               | chang_date | address                                                                    |
+-------+--------+------------------+----------+----------------------------------------------------------------------------+-------+----------+--------+--------+-----------+-----------+-----------+------+--------+---------------------------------------+------------+----------------------------------------------------------------------------+
| 15016 |   5533 | 서산;mode=ext2   |        0 | 충남 서산시 대산읍 대죽리 (주)엘지화학대산공장                             | 15016 | 356715   | 014    | 충남   | 서산시    | 대산읍    | 대죽리    | NULL | NULL   | (주)엘지화학대산공장                  | 20060401   | 충남 서산시 대산읍 대죽리 (주)엘지화학대산공장                             |
| 15012 |   5532 | 서산;mode=ext2   |        0 | 충남 서산시 대산읍 삼성토탈(주)                                            | 15012 | 356711   | 004    | 충남   | 서산시    | 대산읍    | NULL      | NULL | NULL   | 삼성토탈(주)                          | 20050212   | 충남 서산시 대산읍 삼성토탈(주)                                            |
+-------+--------+------------------+----------+----------------------------------------------------------------------------+-------+----------+--------+--------+-----------+-----------+-----------+------+--------+---------------------------------------+------------+----------------------------------------------------------------------------+ 
6 rows in set (0.00 sec)
 

실 사용되는 테이블인 몇백만 row의 text, varchar등을 스핑크스 태우고 join해서 사용하는 서비스 프로그램에도 속도 짱짱하게 잘 나옵니다.

그런데 query='"운산";mode=ext2'" 에서 mode=ext2 는 무엇일까요?

스핑크스의 검색 모드는 총 6가지를 지원합니다. 

SPH_MATCH_ALL : 모든 쿼리 단어와 일치
SPH_MATCH_ANY : 모르겠음.
SPH_MATCH_PHRASE  : 완벽하게 일치를 필요로 하는 문구
SPH_MATCH_BOOLEAN : 부울표현식으로 쿼리 
SPH_MATCH_EXTENDED : 스핑크스 내부 쿼리 언어의 표현대로 쿼리
SPH_MATCH_EXTENDED2 : extened의 확장검색모드 (@, |, MAYBE, ! 등 연산자를 사용할 수 있다)
 

위의 6가지를 sphinxSE에서 사용하기 위해서 약식 mode를 사용합니다. 

SPH_MATCH_ALL : mode=all
SPH_MATCH_ANY  : mode=any
SPH_MATCH_PHRASE  : mode=phr
SPH_MATCH_BOOLEAN : mode=bool
SPH_MATCH_EXTENDED : mode=ext
SPH_MATCH_EXTENDED2  : mode=ext2
 

 

다양한 예제

 

[컬럼조회]
# address 컬럼에서 운산을 찾아라
mysql> SELECT * FROM sphinx.sph_zip_code WHERE `query` = '@address "운산";mode=ext2';
 

[검색 가중치]
# 운산을 찾는다, add1컬럼에서 가중치 10으로 address컬럼에서 가중치 5로 잡아라.
mysql> SELECT * FROM sphinx.sph_zip_code 
WHERE `query` = '"운산";mode=ext2;fieldweights=add1,10,address,5';
 

[정렬]
# address 컬럼으로 desc정렬해라 ("relevance", "attr_desc", "attr_asc", "time_segments", or "extended")
mysql> SELECT * FROM sphinx.sph_zip_code WHERE `query` = '"운산";mode=ext2;sort=attr_desc:address';
 
mysql> SELECT * FROM sphinx.sph_zip_code WHERE `query` = '"운산";mode=ext2;sort=extended:@weight desc, group_id asc';
 

[limit]
# 정렬하고 30 row만 출력해라.
mysql> SELECT * FROM sphinx.sph_zip_code 
WHERE `query` = '"운산";mode=ext2;sort=attr_desc:address;limit=30';
limit 과 sort는 적절히 잘 사용해야 합니다.
기존적으로 정렬은 weight desc이고, limit 20 입니다.
적중률이 엄청 높은 1991년 자료이다 라고 하면 옳은 데이터 일수도 있고, 좀 그런 데이터가 될수도 있기 때문입니다.

 

[근접성]
# 충남 운산 2개의 단어 모두를 포함하는 3단어 미만 범위의 데이터 가져오기
mysql> SELECT * FROM sphinx.sph_zip_code WHERE `query` = '@condition "충남 운산"~3";mode=ext2;'; 
+-------+--------+--------------------------------------+
| id    | weight | address                              |
+-------+--------+--------------------------------------+
| 15099 |   3555 | 충남 서산시 운산면 갈산리            |
| 15105 |   3555 | 충남 서산시 운산면 고산리            |
| 15094 |   3554 | 충남 서산시 운산면                   |
| 15095 |   3554 | 충남 서산시 운산면 가좌리            |
| 15096 |   3554 | 충남 서산시 운산면 상성리            |
| 15097 |   3554 | 충남 서산시 운산면 소중리            |
| 15098 |   3554 | 충남 서산시 운산면 원벌리            |
| 15100 |   3554 | 충남 서산시 운산면 용장리            |
| 15101 |   3554 | 충남 서산시 운산면 거성리            |
| 15102 |   3554 | 충남 서산시 운산면 신창리            |
| 15103 |   3554 | 충남 서산시 운산면 용현리            |
| 15104 |   3554 | 충남 서산시 운산면 태봉리            |
| 15106 |   3554 | 충남 서산시 운산면 수당리            |
| 15107 |   3554 | 충남 서산시 운산면 수평리            |
| 15108 |   3554 | 충남 서산시 운산면 안호리            |
| 15109 |   3554 | 충남 서산시 운산면 여미리            |
| 15110 |   3554 | 충남 서산시 운산면 팔중리            |
| 15111 |   3554 | 충남 서산시 운산면 고풍리            |
| 15112 |   3554 | 충남 서산시 운산면 와우리            |
| 15113 |   3554 | 충남 서산시 운산면 원평리            |
+-------+--------+--------------------------------------+
 

[단락검색]
# 같은 단락중 충남 또는 운산 있는 row 검색
mysql> SELECT * FROM sphinx.sph_zip_code 
WHERE `query` = '@address "충남"PARAGRAPH"운산";mode=ext2;';
 

 

마무리

 

다음장에서 살펴볼 내용은 아래와 같습니다.

스핑크스 부분 index 태우는 방법 
크론 등록하여 배치성으로 index 태우는 방법
서버 reboot 했을때 searchd 자동으로 실행하게하기
기타...
 

[참고]
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

댓글