본문으로 바로가기

검색엔진 스핑크스 Sphinx 도입

category IT를보다/쇼핑몰 2014. 4. 22. 23:07

몇년전 링크나우 개발팀에 있을때 인물검색을 위해 스핑크스를 도입해 만족할 만한 결과를 얻었는데 그때 경험으로 차일피일 생각만하며 도입을 미루다  몇달전 현재 관리하고 있는 쇼핑몰 사이트 "상품검색"에 드디어 Sphinx 검색엔진을 연동 시켰다.

일반적인 쇼핑몰들이 그렇겠지만 상품검색 기능이 쇼핑몰 유입자들이 많이 사용하는 기능중 하나이기때문에 검색 '속도'나 '품질'등 검색기능이 상당히 중요하다. 실제 구글 애널리틱스로 [사이트검색] 지표를 확인해보면 쇼핑몰 유입자 40%이상의 유저가 상품검색 기능을  사용하고 있었다.(경쟁사와의 가격비교,해외제품 재고 확인 등 여러가지이유로..)  그렇기때문에 상품검색을 신경쓰지 않을 수가 없다.





알다시피 Sphinx는 full-text검색 엔진이다.

Sphinx 사용법이나 쿼리 날리고 결과를 가져오는 방법은 다른 블로그에도 정보가 많아 생략하고 Sphinx 공식사이트에 있는 메뉴얼을 링크정도로 남겨놓는다. 여기인데 필요하면 북마크 http://www.sphinxsearch.com/ 


링크나우 인물검색에 도입에선 메인테이블인 회원정보 테이블에 조인된 서브테이블들이 3~5개이기도하고 전체 레코드수가 100만건이 넘어 indexing 수행 시간과 실제 indexing할  field도 딱 필요한 부분만 가져오도록 하는등 신경을 썻던 기억이 있다. 이번 쇼핑몰 도입에선  도입한 적용시킨 mysql 테이블은 쇼핑몰 메인 상품테이블 1 개 그리고 해외 업체상품(외부업체)테이블 2개 였는데 한테이블에 레코드수는 각각 10만개 40만개 정도밖에 안되어 일단 운용에 부담이 없었다. 하지만 향후 다른 유통사 업체들과 제휴를 통해 상품갯수는 분명 계속 늘어날 것이다. 최근에도 제휴된 모 컴퓨터 쇼핑몰 사이트의 제품 3만여개가 등록되었다. 아무튼 갯수가 늘어날수록 Sphinx 효과는 더 빛을 발할것이라 기대한다.


도입후 가장 눈에띄는건 역시 검색속도 이다. 기존  Mysql like 검색의 경우 수행시간 1~2초  사이트가 바쁜 오후시간엔 수행시간 3~5초까지 걸리던 상품검색이 대부분 0.05초 안에 검색결과가 떨어지니 체감속도로도 만족스러운 편이다. 또한 Sphinx에 쿼리를 날릴때 가중치를 field마다 다르게 주어 '상품명' '간단설명' '상세설명' 등의 필드 검색은 물론이고 검색결과에도 원하는 검색결과를 만들 수 있어 검색 결과에도 만족 스러웠다.

앞으로 우리가 좀더 개선해야할 부분은 Sphinx 에서 지원하는 실시간 indexing 인데 현재는 반수동으로 정해진 시간에 cron에 등록된 쉘(sh)실행파일을 통해   indexing 을 하고있다.


!bin/bash 

indexer all --rotate


--rotate 옵션은  searchd 데몬이 살아있을경우  --rotate 옵션으로 indexing을 해야한다. 


이렇게 수동으로 인덱싱을 할경우 단점은 이렇다.

예를 들어 인덱싱후 등록된 주력상품이 있을경우 (주로 출근후 아침에 등록된 긴급(?)상품들) 검색결과에 나타나지 않는다. 당연히 인덱싱 후 등록된 상품임으로...

이런경우 indexing을 다시해줘야하는 레코드가 많은 경우 꽤 시간이 걸린다. 메인상품테이블 12만개,외부업체테이블 46만개 레코드를 인덱싱할경우 3~5분 걸렸다. 

역시 이런경우 실시간 indexing을 해야 관리면에서 작업자 정신건강에 좋다.(상품MD들은 검색안된다며 가끔 번갯불 콩튀기듯 요청하는데 그날 오후 로그를 보면 사용자들은 별로 관심없는게 허다하다. 검색도 매출도..ㅎㅎ)


Sphix설정은 /usr/local/sphinx/conf/sphinx.conf 파일이 전부이고 핵심이다. 이 sphinx.conf파일에 설정된 값을 기반으로

searchd , indexer 등이 동작한다.  그래서 이파일을 지지고 볶고 해야 "한글 검색" 도 되고 좀더 custom화된 운용이 가능하다. 예를 들면 디폴드 경로가 아닌 실제 data폴더가 쌓이는 위치를 변경한다던가 "한글검색"이 가능하도록 charset을 설정한다던가 실시간indexing을 설정한다던가 indexing할 field를 쿼리를 설정한다던가 모두 이파일에서 지지고 볶고 해야한다.


지금 운용되고있는 sphinx.conf 파일은 이렇게 생겼다. 


source goods
{
        type                    = mysql

        sql_host                = localhost
        sql_user                = user
        sql_pass                = pass
        sql_db                  = db
        sql_port                = 3306  # optional, default is 3306
        sql_query_pre           = SET NAMES utf8

#mysql db에서 가져올 상품 field 
        sql_query               = \
                SELECT seq,category_code,package_code,package_cutting,package_ratio,name,hit,goodstype,sale_option,keyword,simple_comment,manufacture,price,sobi_price,emoney,price_option,icon_display,icon_display_start,icon_display_end,
icon_list,description,isremoteimage,image_list,image_main,image_detail,isforeign,deliveryterm,order_min_count,unix_timestamp(regdate) as regdate\
                FROM goods where  goodstype not in('5','6','8') and sale_option not in ('0002','0004')
        #sql_field_string,sql_attr_unit,sql_attr_bigint,sql_attr_timestamp..
        sql_attr_uint           = seq
        sql_attr_uint           = hit
        sql_field_string        = name
        sql_field_string        = goodstype
        sql_field_string        = sale_option
        sql_field_string        = category_code
        sql_field_string        = package_code
        sql_field_string        = package_cutting
        sql_field_string        = package_ratio
        sql_field_string        = keyword
        sql_field_string        = simple_comment
        sql_field_string        = manufacture
        sql_field_string        = sobi_price
        sql_field_string        = emoney
        sql_attr_uint           = price
        sql_attr_uint           = order_min_count
        sql_field_string        = deliveryterm
        sql_field_string        = price_option
        sql_field_string        = icon_list
        sql_field_string        = icon_display
        sql_field_string        = icon_display_start
        sql_field_string        = icon_display_end
        sql_field_string        = description
        sql_field_string        = isremoteimage
        sql_field_string        = image_list
        sql_field_string        = image_main
        sql_field_string        = image_detail
        sql_field_string        = isforeign
        sql_attr_timestamp      = regdate
        sql_query_info          = SELECT * FROM goods WHERE seq=$id
}
index goods
{
        source                  = goods   #위의 source 설정 
        path                    = /home/sphinx/data/goods  #sphinx 에서 indexing 한 binary 데이터가 저장위치 
        docinfo                 = extern
        charset_type            = utf-8

#한글 검색을 위한 설정 !
        charset_table = 0..9, A..Z->a..z, _, a..z,U+AC00..U+D7A3,U+1100..U+1159,U+1161..U+11A2,U+11A8..U+11F9,U+0021..U+002F,U+003A..U+0040,U+005B..U+0060,U+007B..U+007E
        ngram_chars =  U+AC00..U+D7A3

#한글 검색을위해 ngram 1로 설정
        ngram_len               =1

#sphinx 쿼리에서 "*" 구문 사용 여부  
        enable_star=1
        min_infix_len=1
}

indexer
{
        mem_limit               = 512M
}


searchd
{

       #searchd 리스닝포트 
        listen                  = 3312

#sphinx 자체 db 연결 포트 
        listen                  = 9306:mysql41
        log                     = /usr/local/sphinx/var/log/searchd.log
        query_log               = /usr/local/sphinx/var/log/query.log
        read_timeout            = 5
        max_children            = 30
        pid_file                = /usr/local/sphinx/var/log/searchd.pid
        max_matches             = 1000
        seamless_rotate         = 1
        preopen_indexes         = 1
        unlink_old              = 1
        workers                 = threads # for RT to work
     #binlog 경로
        binlog_path             = /home/sphinx/
}




실제 검색엔진 도입후 사이트 검색 사용비율 변화, 장바구니 등 사이트 전환목표율 변화, 매출변화 등의 웹사이트 사용자 분석작업은 마케팅 팀에서 지속적으로 해야하는 작업이나 아직 별다른 피드백은 없다.