역인덱스 게시판 |
작성자 : 김영철
등록날짜 : 2009.01.14 12:59
여러가지 대용량 게시판에 관한 글들이 산재해 있습니다.
그중에 부분업데이트 게시판이 가장 눈에 들어왔으며
나름대로 변형하여 만들어 보았습니다.
어쩌면 독창적일지도... :D
우선 테이블 구조는 아래와 같습니다.
CREATE TABLE `board_news` (
`c_no` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`c_group_no` INT NOT NULL DEFAULT '-1',
`c_depth_no` INT UNSIGNED NOT NULL,
`c_order_no` INT UNSIGNED NOT NULL,
`subject` VARCHAR(50) NOT NULL,
`contents` TEXT NOT NULL,
`reg_date` INT UNSIGNED NOT NULL,
`hits` INT UNSIGNED NOT NULL,
PRIMARY KEY(`c_no`),
INDEX `idx1`(`c_group_no`, `c_order_no`)
);
위의 테이블은 뉴스 게시판을 만들면서 적용한 것을 예로 든것입니다.
중요한 필드는 c_group_no, c_depth_no, c_order_no 입니다.
제가 생각하는 대용량 게시판은 첫 페이지나 마지막 페이지나 검색속도가
일정해야 한다는 것입니다.
그렇지 않은 게시판은 대용량 게시판이라고 할 수 없다고 생각합니다.
그럼 위의 테이블이 어떻게 첫 페이지나 마지막 페이지에서 속도가 같을까요?
차례차례 살펴보겠습니다.
우선 새로운 게시물을 삽입할 때 처리하는 방법입니다.
일반적으로 게시판의 구조는 계층형입니다.
아래의 예를 보세요.
No. Order No. Subject
9 9 다섯번째 게시물
8 5 네번째 게시물
7 4 세번째 게시물
6 2 두번째 게시물
5 6 →두번째 게시물의 첫번째 응답
4 8 →→두번째 게시물의 첫번째 응답의 응답
3 7 →두번째 게시물의 두번째 응답
2 1 첫번째 게시물
1 3 →첫번째 게시물의 응답
위와 같은 리스트가 전형적인 계층형 게시판입니다.
Order No. 는 게시물이 작성된 순서입니다.
우선 게시물을 삽입할 때 아래의 쿼리문을 날려줍니다.
$link_id = @mysql_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD) or
die('데이터베이스 서버(' . DB_SERVER . ')에 접속할 수 없습니다.');
// Table Lock
$query = "LOCK TABLES " . DB_DBNAME . ".board_news WRITE";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
// 최근 게시물 정보 가져오기
$query = "SELECT MIN(c_group_no) " .
"FROM " . DB_DBNAME . ".board_news";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
$query_data = mysql_fetch_array($result_id);
$c_group_no = ($query_data[0]) ? $query_data[0] - 1 : -1;
// 변수 초기화
$reg_date = time();
// 게시물 등록
$query = "INSERT INTO " . DB_DBNAME . ".board_news (" .
"c_group_no, subject, contents, reg_date) " .
"VALUES (" .
"$c_group_no, '$subject', '$contents', $reg_date)";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
위의 코드는 단순히 c_group_no 에 -1 을 해준것입니다.
그럼 아래의 코드를 보세요. 답변글에 대한 처리입니다.
// Table Lock
$query = "LOCK TABLES " . DB_DBNAME . ".board_news WRITE";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
// 상위 게시물 정보 가져오기
$query = "SELECT c_group_no, c_depth_no, c_order_no " .
"FROM " . DB_DBNAME . ".board_news " .
"WHERE c_no = $c_no";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
$query_num_rows = mysql_num_rows($result_id);
if ($query_num_rows) {
$query_data = mysql_fetch_array($result_id);
$c_group_no = $query_data['c_group_no'];
$c_depth_no = $query_data['c_depth_no'] + 1;
$c_order_no = $query_data['c_order_no'] + 1;
// 게시물 정렬을 위한 정보 가져오기
$query = "SELECT c_depth_no, c_order_no " .
"FROM " . DB_DBNAME . ".board_news " .
"WHERE c_group_no = $c_group_no " .
"AND c_order_no >= $c_order_no " .
"ORDER BY c_order_no ASC";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
while ($query_data = mysql_fetch_array($result_id)) {
if ($c_depth_no > $query_data['c_depth_no']) {
break;
} else {
$c_order_no = $query_data['c_order_no'] + 1;
}
}
// 게시물 정렬(부분업데이트)
$query = "UPDATE " . DB_DBNAME . ".board_news " .
"SET c_order_no = c_order_no + 1 " .
"WHERE c_group_no = $c_group_no " .
"AND c_order_no >= $c_order_no";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
// 변수 초기화
$reg_date = time();
// 게시물 등록
$query = "INSERT INTO " . DB_DBNAME . ".board_news (" .
"c_group_no, c_depth_no, c_order_no, subject, contents, reg_date) " .
"VALUES (" .
"$c_group_no, $c_depth_no, $c_order_no, '$subject', '$contents', $reg_date)";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
} else {
exit('상위 게시물 정보가 없습니다.');
}
위 코드에서 주목할 부분은 게시물 정렬부분입니다.
기본적으로 답변글 처리에서 $c_no 는 당연히 상위 게시물이고
상위 게시물의 기본 정보에서 c_group_no, c_depth_no, c_order_no 는
일단 가져와서 게시판에 같은 c_group_no 중 c_order_no 가 크거나 같은 것들을
전부 +1 씩 하는 것입니다.(부분업데이트)
그리고 빈자리에 답변글을 잽싸게 집어 넣습니다.ㅋ
여기서 DB 내부를 들여다보죠.
부분업데이트 전
c_no c_group_no c_depth_no c_order_no subject
9 -5 0 0 다섯번째 게시물
8 -2 2 2 →→두번째 게시물의 첫번째 응답의 응답
7 -2 1 2 →두번째 게시물의 두번째 응답
6 -2 1 1 →두번째 게시물의 첫번째 응답
5 -4 0 0 네번째 게시물
4 -3 0 0 세번째 게시물
3 -1 1 1 →첫번째 게시물의 응답
2 -2 0 0 두번째 게시물
1 -1 0 0 첫번째 게시물
부분업데이트 후
c_no c_group_no c_depth_no c_order_no subject
9 -5 0 0 다섯번째 게시물
8 -2 2 2 →→두번째 게시물의 첫번째 응답의 응답
7 -2 1 3 →두번째 게시물의 두번째 응답
6 -2 1 1 →두번째 게시물의 첫번째 응답
5 -4 0 0 네번째 게시물
4 -3 0 0 세번째 게시물
3 -1 1 1 →첫번째 게시물의 응답
2 -2 0 0 두번째 게시물
1 -1 0 0 첫번째 게시물
대충 설명이 끝났습니다.
그럼 어떻게 보여줘야 할 것까요?
아래의 코드를 보세요. 코드를 보여주는 것이 가장 빠른 설명이라 자꾸 코드를 보여줍니다.
// get GET parameters.
$row_no = (int)$_GET['row_no']; // 페이징할 때 주로 쓰이는 변수이다.
// 변수 초기화
$list_scale = 10;
// 페이지 시작 인덱스 값 가져오기
$query = "SELECT c_group_no, c_order_no " .
"FROM " . DB_DBNAME . ".board_news " .
"ORDER BY c_group_no ASC, c_order_no ASC " .
"LIMIT $row_no, 1";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
$query_data = mysql_fetch_array($result_id);
// 게시물 리스트
$query = "SELECT c_no, c_depth_no, subject, contents, reg_date, hits " .
"FROM " . DB_DBNAME . ".board_news " .
"WHERE (c_group_no = {$query_data['c_group_no']} " .
"AND c_order_no >= {$query_data['c_order_no']}) " .
"OR c_group_no > {$query_data['c_group_no']} " .
"ORDER BY c_group_no ASC, c_order_no ASC " .
"LIMIT 0, $list_scale";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
위 코드는 당연히 게시물이 있을 경우 수행되어야 합니다.
그리고, $row_no, $list_scale 변수는 페이징할 때 주로 쓰이는 것이므로 설명을 생략하겠습니다.
그중에 부분업데이트 게시판이 가장 눈에 들어왔으며
나름대로 변형하여 만들어 보았습니다.
어쩌면 독창적일지도... :D
우선 테이블 구조는 아래와 같습니다.
CREATE TABLE `board_news` (
`c_no` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`c_group_no` INT NOT NULL DEFAULT '-1',
`c_depth_no` INT UNSIGNED NOT NULL,
`c_order_no` INT UNSIGNED NOT NULL,
`subject` VARCHAR(50) NOT NULL,
`contents` TEXT NOT NULL,
`reg_date` INT UNSIGNED NOT NULL,
`hits` INT UNSIGNED NOT NULL,
PRIMARY KEY(`c_no`),
INDEX `idx1`(`c_group_no`, `c_order_no`)
);
위의 테이블은 뉴스 게시판을 만들면서 적용한 것을 예로 든것입니다.
중요한 필드는 c_group_no, c_depth_no, c_order_no 입니다.
제가 생각하는 대용량 게시판은 첫 페이지나 마지막 페이지나 검색속도가
일정해야 한다는 것입니다.
그렇지 않은 게시판은 대용량 게시판이라고 할 수 없다고 생각합니다.
그럼 위의 테이블이 어떻게 첫 페이지나 마지막 페이지에서 속도가 같을까요?
차례차례 살펴보겠습니다.
우선 새로운 게시물을 삽입할 때 처리하는 방법입니다.
일반적으로 게시판의 구조는 계층형입니다.
아래의 예를 보세요.
No. Order No. Subject
9 9 다섯번째 게시물
8 5 네번째 게시물
7 4 세번째 게시물
6 2 두번째 게시물
5 6 →두번째 게시물의 첫번째 응답
4 8 →→두번째 게시물의 첫번째 응답의 응답
3 7 →두번째 게시물의 두번째 응답
2 1 첫번째 게시물
1 3 →첫번째 게시물의 응답
위와 같은 리스트가 전형적인 계층형 게시판입니다.
Order No. 는 게시물이 작성된 순서입니다.
우선 게시물을 삽입할 때 아래의 쿼리문을 날려줍니다.
$link_id = @mysql_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD) or
die('데이터베이스 서버(' . DB_SERVER . ')에 접속할 수 없습니다.');
// Table Lock
$query = "LOCK TABLES " . DB_DBNAME . ".board_news WRITE";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
// 최근 게시물 정보 가져오기
$query = "SELECT MIN(c_group_no) " .
"FROM " . DB_DBNAME . ".board_news";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
$query_data = mysql_fetch_array($result_id);
$c_group_no = ($query_data[0]) ? $query_data[0] - 1 : -1;
// 변수 초기화
$reg_date = time();
// 게시물 등록
$query = "INSERT INTO " . DB_DBNAME . ".board_news (" .
"c_group_no, subject, contents, reg_date) " .
"VALUES (" .
"$c_group_no, '$subject', '$contents', $reg_date)";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
위의 코드는 단순히 c_group_no 에 -1 을 해준것입니다.
그럼 아래의 코드를 보세요. 답변글에 대한 처리입니다.
// Table Lock
$query = "LOCK TABLES " . DB_DBNAME . ".board_news WRITE";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
// 상위 게시물 정보 가져오기
$query = "SELECT c_group_no, c_depth_no, c_order_no " .
"FROM " . DB_DBNAME . ".board_news " .
"WHERE c_no = $c_no";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
$query_num_rows = mysql_num_rows($result_id);
if ($query_num_rows) {
$query_data = mysql_fetch_array($result_id);
$c_group_no = $query_data['c_group_no'];
$c_depth_no = $query_data['c_depth_no'] + 1;
$c_order_no = $query_data['c_order_no'] + 1;
// 게시물 정렬을 위한 정보 가져오기
$query = "SELECT c_depth_no, c_order_no " .
"FROM " . DB_DBNAME . ".board_news " .
"WHERE c_group_no = $c_group_no " .
"AND c_order_no >= $c_order_no " .
"ORDER BY c_order_no ASC";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
while ($query_data = mysql_fetch_array($result_id)) {
if ($c_depth_no > $query_data['c_depth_no']) {
break;
} else {
$c_order_no = $query_data['c_order_no'] + 1;
}
}
// 게시물 정렬(부분업데이트)
$query = "UPDATE " . DB_DBNAME . ".board_news " .
"SET c_order_no = c_order_no + 1 " .
"WHERE c_group_no = $c_group_no " .
"AND c_order_no >= $c_order_no";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
// 변수 초기화
$reg_date = time();
// 게시물 등록
$query = "INSERT INTO " . DB_DBNAME . ".board_news (" .
"c_group_no, c_depth_no, c_order_no, subject, contents, reg_date) " .
"VALUES (" .
"$c_group_no, $c_depth_no, $c_order_no, '$subject', '$contents', $reg_date)";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
} else {
exit('상위 게시물 정보가 없습니다.');
}
위 코드에서 주목할 부분은 게시물 정렬부분입니다.
기본적으로 답변글 처리에서 $c_no 는 당연히 상위 게시물이고
상위 게시물의 기본 정보에서 c_group_no, c_depth_no, c_order_no 는
일단 가져와서 게시판에 같은 c_group_no 중 c_order_no 가 크거나 같은 것들을
전부 +1 씩 하는 것입니다.(부분업데이트)
그리고 빈자리에 답변글을 잽싸게 집어 넣습니다.ㅋ
여기서 DB 내부를 들여다보죠.
부분업데이트 전
c_no c_group_no c_depth_no c_order_no subject
9 -5 0 0 다섯번째 게시물
8 -2 2 2 →→두번째 게시물의 첫번째 응답의 응답
7 -2 1 2 →두번째 게시물의 두번째 응답
6 -2 1 1 →두번째 게시물의 첫번째 응답
5 -4 0 0 네번째 게시물
4 -3 0 0 세번째 게시물
3 -1 1 1 →첫번째 게시물의 응답
2 -2 0 0 두번째 게시물
1 -1 0 0 첫번째 게시물
부분업데이트 후
c_no c_group_no c_depth_no c_order_no subject
9 -5 0 0 다섯번째 게시물
8 -2 2 2 →→두번째 게시물의 첫번째 응답의 응답
7 -2 1 3 →두번째 게시물의 두번째 응답
6 -2 1 1 →두번째 게시물의 첫번째 응답
5 -4 0 0 네번째 게시물
4 -3 0 0 세번째 게시물
3 -1 1 1 →첫번째 게시물의 응답
2 -2 0 0 두번째 게시물
1 -1 0 0 첫번째 게시물
대충 설명이 끝났습니다.
그럼 어떻게 보여줘야 할 것까요?
아래의 코드를 보세요. 코드를 보여주는 것이 가장 빠른 설명이라 자꾸 코드를 보여줍니다.
// get GET parameters.
$row_no = (int)$_GET['row_no']; // 페이징할 때 주로 쓰이는 변수이다.
// 변수 초기화
$list_scale = 10;
// 페이지 시작 인덱스 값 가져오기
$query = "SELECT c_group_no, c_order_no " .
"FROM " . DB_DBNAME . ".board_news " .
"ORDER BY c_group_no ASC, c_order_no ASC " .
"LIMIT $row_no, 1";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
$query_data = mysql_fetch_array($result_id);
// 게시물 리스트
$query = "SELECT c_no, c_depth_no, subject, contents, reg_date, hits " .
"FROM " . DB_DBNAME . ".board_news " .
"WHERE (c_group_no = {$query_data['c_group_no']} " .
"AND c_order_no >= {$query_data['c_order_no']}) " .
"OR c_group_no > {$query_data['c_group_no']} " .
"ORDER BY c_group_no ASC, c_order_no ASC " .
"LIMIT 0, $list_scale";
$result_id = @mysql_query($query, $link_id) or
die('SQL: ' . $query . '<br><br>' . mysql_errno() . ': ' . mysql_error());
위 코드는 당연히 게시물이 있을 경우 수행되어야 합니다.
그리고, $row_no, $list_scale 변수는 페이징할 때 주로 쓰이는 것이므로 설명을 생략하겠습니다.
[출처] 마루아라
"쇼핑몰·홈페이지·오픈마켓
블로그·페이스북·이메일 등의 각종 마케팅 글쓰기, 각종 광고, 영업, 판매, 제안서, 전단지 반응율 3배×10배 이상 높이는 마법의 8단계 공식" |
☞자세히보기 |
|
|