본문 바로가기
코드이그나이터와 php7와 mysql

코드이그나이터4. 네이버검색 따라잡기-1. 대용량 자료 쾌속 검색(1)!

1. 오토셋 APM 인스톨러 ( apache + php7.2 + mariadb ) 설치 | https://itadventure.tistory.com/93

2. 코드이그나이터 4 ( codeigniter 4 ) 설치 | https://itadventure.tistory.com/95

3. 비주얼 스튜디오 코드 에디터 설치 & 한글 설정 | https://itadventure.tistory.com/96

4. 폴더열기 / 웹페이지 편집(1) | https://itadventure.tistory.com/97

5. 웹페이지 편집(2) | https://itadventure.tistory.com/101

6. 코드이그나이터4의 URL 규칙 | https://itadventure.tistory.com/105

7. php, 네임스페이스 [ namespace ] ?! | https://itadventure.tistory.com/118

8. 코드이그나이터의 네임스페이스, 그리고 모델 | https://itadventure.tistory.com/122

9. 코드이그나이터 뷰의 파라미터 전달 | https://itadventure.tistory.com/147

10. 코드이그나이터 뷰를 나눠 볼까요? | https://itadventure.tistory.com/174

11. MYSQL 이 뭐여? [ 마이에스큐엘은 서류철이다! ] | https://itadventure.tistory.com/175

12. MYSQL 콘솔에 접속해보자! | https://itadventure.tistory.com/178

13. MySql에 넣었다가 꺼냈다가, 뭘? | https://itadventure.tistory.com/265

14. 검색진열대 MYSQL | https://itadventure.tistory.com/267

15. 편집의 왕자 MySQL | https://itadventure.tistory.com/269

16. 코드이그나이터4, MYSQL과 손잡다. | https://itadventure.tistory.com/271

17. MySQL -> 컨트롤러 -> 뷰 트리플 패스! | https://itadventure.tistory.com/272

18. 코드이그나이터! MySQL 에 입력하다! ( insert ) | https://itadventure.tistory.com/273

19. 해킹을 막아라! MySQL인젝션 보안 | https://itadventure.tistory.com/274

20. MySQL과 친해지는 phpmyadmin | https://itadventure.tistory.com/277

21. 코드이그나이터4에서 책 정보를 편집해볼까요? | https://itadventure.tistory.com/280

22. 코드이그나이터4에서 책을 지워봅시다. DELETE! | https://itadventure.tistory.com/282

23. 코드이그나이터4, 페이징 기술 | https://itadventure.tistory.com/285

24. 코드이그나이터4. 검색! | https://itadventure.tistory.com/295

25. 코드이그나이터4. 검색어 자동 추천! | https://itadventure.tistory.com/303

26. 코드이그나이터4. 네이버 검색 따라잡기-1. 대용량 자료 쾌속 검색(1)


소제목은 '네이버 검색 따라잡기' 이지만 네이버 검색이 이런 기술을 사용을 확실히 한다는 것은 아니니 착오없으시기 바랍니다. 어디까지나 크레이의 상상력입니다 :)

지난 챕터에서는 책 제목을 실시간 검색하는 부분에 대해 다뤄보았는데요.
지금은 책이 140권밖에 없어서 비교적 속도가 빠르지만 만일 책이 몇십만권 정도 있다고 칩시다.
검색을 시도하면 어떤 일이 일어날까요?

결론적으로 말하자면 검색 한번 하는데 10초 가량이 소요되어 버립니다.
타이핑을 하나 칠때마다 서버로 검색쿼리를 날리는데 서버컴퓨터는 버벅대고 실시간 검색을 괜히 만들었나 싶어서 다시 빼야되는 상황이 발생할 수도 있습니다.

그렇게 느린 이유는 무엇일까요?

바로 검색 쿼리문에 있습니다. 지난 챕터 소스중 검색쿼리가 실행되는 일부분을 살펴보자면 아래와 같은데요.
아래 쿼리는 제목 중에서 '만들'이라는 글자가 포함된 책만 뽑아내는 쿼리입니다.

SELECT * FROM book where title like '%만들%'

이런 쿼리가 매번 검색할 때마다 실행될텐데요.
주요하게 보실 부분은 이 부분입니다.

like '%만들%'

책제목의 중간 부분에 '만들'이란 글자가 들어가는 모든 것을 찾는 것인데요.
앞이나 뒤에는 무엇이 들어가도 상관이 없어야 합니다

이 쿼리문은 한마디로 모든 자료를 다 검색합니다.
그래서 제목중에 '만들'이라는 글자가 있는 것을 모두 뽑아내는 것인데요.

자료가 10만개라면 10만개를 모두 샅샅이 찾아 봅니다.
자료 갯수에 정비례하는 엄청난 시간이 소요되지요.

만일 조건이 아래와 같았다면 조금 상황이 다를텐데요.

like '만들%'

이 조건은 '만들'이라는 글자로 시작하는 자료만 모두 찾습니다.
만일 mysql 에서 책제목에 인덱스라는 기술을 걸어 놓았다면 사실 이 검색 속도는 매우 빠릅니다.
이미 책제목이 사전순으로 나열되었기 때문에 시작지점과 끝지점을 알아내어 결과치만 반환해주면 됩니다.
그나마도 BTREE ( 2진 트리 ) 라는 알고리즘이 적용되어 매우 빠른 결과를 뱉어냅니다.

하지만 이 방법은 해결책이 아닙니다.
여기서는 단순히 이 것을 논하자는 것이 아니구요.
좀 더 획기적인 방법으로 접근해 보겠습니다.

우선 like '만들%'과 같이 검색했을때는,
'만들어 보는 아두이노'라는 책은 검색이 되겠지만
'아두이노로 만들어보자'라는 책은 검색이 되지 않을 것입니다.

왜냐하면 책 제목이 '만들'로 시작하지 않기 때문이지요.
앞의 방법은 너무 느리고 뒤의 방법은 너무 제한이 많다면 어떤 방법이 있을까요?

더 좋은 방법이 있을수도 있지만 크레이는 이런 방법을 권합니다.

이른바 단어 사전을 만드는 것입니다.
책 제목에 나오는 단어들을 각각 단어로 단어 사전에 저장해놓는데
이 때 해당 책의 번호(숫자)도 함께 모두 기록해 놓는 것이지요.

그리고 검색을 시도했을 때 검색되는 단어들의 책의 번호를 조합해
해당하는 책을 검색하는 것입니다.

설명이 좀 복잡한데요.
이를테면 아래와 같습니다.

첫번째 책 제목이 아래와 같다고 칩니다.
이 책의 번호는 1번입니다.

3D 모델링 블렌더 2.8

띄어쓰기를 기준으로 단어를 각각 나눕니다.

3D
모델링
블렌더
2.8

각각의 단어에 책 번호를 붙여서 보관합니다.

3D 1
모델링 1
블렌더 1
2.8 1

두번째 책 제목은 아래와 같고 책 번호는 2번입니다

자바스크립트로 만나는 3D 가상세계

역시 각각 단어를 나눈 다음에

자바스크립트로
만나는
3D
가상세계

앞의 단어 사전에 이어서 책 번호를 붙입니다.
이 경우 3D라는 단어가 2번 등장하니 번호가 2개 붙게 됩니다.

3D 1,2
모델링 1
블렌더 1
2.8 1
자바스크립트로 1
만나는 1
가상세계 1

이런 식으로 단어 사전을 만들다 보면 어떤 단어는 1번만 등장하고 어떤 단어는 10번 이상 등장하기도 할 겁니다.
그럴 때마다 책의 번호를 넣어주는 것입니다.

그리고 사용자가 '3D'를 검색하면 1번과 2번 책 번호를 알아내어 이 책 번호를 검색하는 것이지요.
얼핏 보면 돌아가는 방식이라 더 시간이 꽤 걸릴것 같지만 그렇지 않습니다.
특히 자료가 많을 경우 서버의 반응속도는 믿을 수 없을 정도로 빠른 속도를 보여주지요.

다만 이 때 한가지 제약은 있습니다.
책 제목이 아래와 같다면 각각의 단어가 조건이 맞아 검색되지만

3D 모델링 블렌더
가상세계 3D

아래와 같다면 검색되지는 않습니다. 각각의 단어들이 3D로 시작하지 않기 때문인데요.
뭐 이 경우는 그냥 포기하는 것으로 합시다. 속도를 위해서 '소'를 희생하는 거지요.

오메가3D와 함께 하다
13Day의 행복

이 외에도 특수문자도 마찬가지 처리를 하는 것입니다.
보통 사용자가 "<국민의 권리>"와 같이 "<" 기호나 ">" 기호를 넣어서 검색을 하는 일은 거의 없으니
"<" 나 ">" 기호도 공백 문자처럼 취급하는 것이지요.

아래 소스에서는 이러한 문자들을 공백문자처럼 취급하게 하였습니다.

"[", "]", "(", ")", ",", ":", ";", "<", ">", "&", "?"

이론적인 설명이 끝났습니다.
한번에 모두 다룰 수 없어
이 번 시간에는 우선 단어사전을 만들어보는 시간을 가져오겠습니다.
먼저 단어사전 테이블을 생성해야 합니다.

20챕터에서 phpmyadmin 에 접속하는 방법을 다루어보았었는데요.
참조해서 phpMyAdmin 페이지 접속해서 로그인을 시도해 주세요.

https://itadventure.tistory.com/277

 

MySQL과 친해지는 phpmyadmin

1. 오토셋 APM 인스톨러 ( apache + php7.2 + mariadb ) 설치 | https://itadventure.tistory.com/93 2. 코드이그나이터 4 ( codeigniter 4 ) 설치 | https://itadventure.tistory.com/95 3. 비주얼 스튜디오..

itadventure.tistory.com

그리고 library 데이터베이스를 선택, New 버튼을 누릅니다.

아래와 같이 입력 및 선택하고, 저장 버튼을 누릅니다.

왼쪽 화면에 이와 같이 book_search 테이블이 생성된 것을 확인하실 수 있습니다.

book_search 테이블 선택, '구조'를 선택하고, word 줄의 '더보기' - '고유값'을 선택하면

아래와 같은 화면이 나오는데요. 확인 버튼을 눌러주시면 됩니다.

UNIQUE 는 '유일한', '독특한'이란 뜻을 가진 영어단어인데요.
이렇게 규칙을 정해주면 단어가 실수로라도 2번 입력되지 않게 원천적으로 막아줍니다.
그 이외에도 BTREE 라는 구조를 만들어주어 단어 검색을 빠르게 잡아주는 역활을 하게 됩니다.

이 것으로 테이블은 생성되었습니다.
이제 단어 사전을 입력하는 소스를 구성해볼텐데요.
아래 소스를 저장해 주세요,

코드이그나이터폴더\app\Controllers\MakeWordBook.php

<?php
namespace App\Controllers;
use CodeIgniter\Controller;
class MakeBookWord extends Controller
{
    public function index()
    {        
        ini_set('memory_limit', -1);
        $db = \Config\Database::connect("default", false);
        $query_cnt = $db->query("SELECT count(*) as cnt FROM book");
        $total = $query_cnt->getResultArray()[0]['cnt'];        
        $limit=1000; // 1000 건씩 끊어서 처리
        $word_arr=array();

        $special_chars=array(
            "[", "]", "(", ")", ",", ":", ";", "<", ">",
            "&", "?"
        );
        // 유지할 단어 +-*/.%
        for($step=0;$step<$total;$step+=$limit)
        {
            $results = $db->query("SELECT number, title FROM book order by number limit $step, $limit")->getResultArray();
            foreach($results as $result)
            {
                foreach($special_chars as $char){
                    $result['title']=str_replace($char, " ", $result['title']);
                }
                $explode = array_unique(explode(" ", trim($result['title'])));
                foreach($explode as $word)
                {
                    if(strlen($word)<=1)continue;                    
                    if(!empty($word_arr[$word]))$word_arr[$word].=",";
                    $word_arr[$word].=$result['number'];
                }                    
            }
        }

        echo count($word_arr)."개<br/>";
        ob_flush(); flush();

        $cnt=0;
        $db->query("drop table book_search");
        $db->query("create table book_search (
            word varchar(120),
            numbers text
        )");
        $i=0;
        $db->transStart();
        foreach($word_arr as $word=>$numbers){
            $i++;
            echo "$i: ".$word." / ".$numbers."<br/>";
            $sql="insert into book_search (word, numbers)
            values ('".addslashes($word)."', '".addslashes($numbers)."')";
            $db->query($sql);
            $cnt++;
            if($cnt>=100)
            {
                $db->transComplete();
                $db->transStart();
                $cnt=0;
            }
        }
        $db->transComplete();
        $db->query("ALTER TABLE book_search ADD INDEX(word);");
        
        echo "단어사전 입력이 완료되었습니다.";
    }
}

그리고 웹페이지에서 아래 URL 에 접속합니다.

http://localhost/makeBookWord

어느 정도의 시간이 소요되면 화면에 이렇게 뿌려질 것입니다.

각각의 단어가 단어사전 테이블에 보관된 상황을 나타낸 것인데요.
정말로 단어 사전에 자료가 입력되었나 확인해볼까요?

phpMyAdmin 페이지에서 book_search 테이블을 클릭해보시면 이와 같이 단어사전과 책 번호 목록이 만들어진 것을 보실수 있습니다

대용량 쾌속 검색을 위한 단어 사전

단어 사전을 만드는 소스에 대한 설명은 맨 마지막으로 미뤄두도록 하겠습니다.
다음 챕터에서는 이 단어 사전을 통한 쾌속 검색에 대해서 다루어 보도록 하겠습니다.

필요하신 분에게 도움이 되셨는지 모르겠습니다.
오늘도 여기까지 읽어주셔서 감사합니다 :)