Category Archives: 허접프로그래머

리눅스에서 HDD 추가하기


사용자 삽입 이미지이 작업은 2U Rackmount 서버기반의 SATA-II HDD로 이루어졌습니다.

오늘 서버에 하드디스크를 추가하는 작업이 생겨, 기록을 남겨둘까 한다.

알아서 서버에 HDD를 잘 설치하고 CMOS SETUP에 들어가서 하드디스크를 설정하도록 한다.

또는 RAID기반의 서버일경우 필요한 만큼의 HDD를 묶는다. 하나만 있다면 그냥 Stripe(0)으로 설정해 주면 된다.

리눅스로 부팅한다. 하드디스크는 순서대로 /dev/sda, /dev/sdb, /dev/sdc … 순으로 나열된다.

3번째 하드디스크(또는 3번째 RAID 구성 묶음)를 방금 추가하였다면 새로이 추가할 서버의 디바이스명은 /dev/sdc가 된다.

fdisk를 실행하여 하드디스크 파티션을 설정하자.

[code] # fdisk /dev/sdc[/code]

p를 눌러 현재의 파티션 구성을 볼수 있다. 새로운 하드이므로 아무것도 없다.
[code]Command (m for help): p

Disk /dev/sdc: 318.9 GB, 318999887872 bytes
255 heads, 63 sectors/track, 38782 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System[/code]

n을 눌러 새로운 파티션을 생성하여 보자.
[code]Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-38782, default 1):
Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-38782, default 38782):
Using default value 38782[/code]

파티션의 종류에 primary와 extended의 차이점에 대해서는 대부분이 알고 계실것이라 판단하고 넘어가겠다.

Partition number은 primary에는 총 4개의 파티션을 가질수 있으므로 몇번째로 설정할것인지에 대한것이고, 이후는 파티션의 사이즈에 대한 질문이다.
나는 하드 하나를 전체로 하여 하나의 파티션으로 잡을 계획을 가지고 있으므로 엔터만 눌러 디폴트설정을 따르도록 한다.

다시한번 p를 눌러 확인하여 보자, 방금전에 설정한 내용이 추가되어있다.
[code]Command (m for help): p

Disk /dev/sdc: 318.9 GB, 318999887872 bytes
255 heads, 63 sectors/track, 38782 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System
/dev/sdc1               1       38782   311516383+  83  Linux[/code]

w를 눌러 저장하고 나가도록 하자.
[code]Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.[/code]

이제 mkfs로 포맷을 하도록 하자, 나는 ext3로 하도록 하겠다.
[code]# mkfs -t ext3 /dev/sdc1[/code]

또는 다음과 같은 명령어를 사용해도 된다.
[code]# mkfs.ext3 /dev/sdc1[/code]

이제 특정 디렉토리를 만들어 마운트를 해보도록 하자.
[code]# mkdir /opt
# mount /dev/sdc1 /opt[/code]

잘되는지 확인한후에 /etc/fstab에 추가하도록 하자.

Java Lucene – 루씬을 이용한 JSP용 클래스

자바를 하면서 가장 중독성 있었던것을 꼽아보라고 하면 단연 수많은 오픈소스 프레임워크가 아닐까 생각해 본다.

검색엔진에 대한 아무런 지식이 없는 상태인 나는 검색엔진 구현을 해야 하는 상황에서 어떻게 해야 할까 고민하다가 무턱대고 Lucene in Action을 덜컥 구매해 버렸다.

그리고 보다보니깐 참 이해 안되고, 책 내용도 자꾸 웹과는 거리가 있는 콘솔상에서의 구현에 집중되어있고 인터넷을 검색해 봐도 다 이해할수 없는 예제였고, 아무튼 그렇게 루씬을 공부했다.

이제 꽤나 루씬을 이해하고 있고, 이제 내가 아는 지식을 공개해야 할때인듯 하다. 물론 내가 아는 지식도 매우 기초적인것이라 나역시 초보를 위한 강좌밖에 못할듯 하다.

현존하는 루씬의 강좌나 자료들이 2.0이전 버젼들을 대상으로 제작되어있고, 그 이전버젼들은 한글에 대한 Analyzer버그가 있다.

고로 나는 처음부터 2.0으로 해왔다. 그래서인지 Lucene in Action책을 보고 따라하기엔 다른것이 많아 지식 습득에 조금 문제가 있었다. 앞으로 쓰는 모든 글은 2.0으로 제작할것이며, 한글 문제가 없는 버젼이다.

우선 간단하게 공부해 볼수 있는 루씬 클래스입니다. 공부에 참고만 하세요^^

[CODE]package ke.pe.theeye.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class SearchEngine {
 private static SearchEngine instance;
 private Analyzer standardAnalyzer;
 private Analyzer whitespaceAnalyzer;
 private Analyzer simpleAnalyzer;
 private Analyzer currentAnalyzer;
 private String directory;

 private static final int STANDARD = 0;
 private static final int WHITESPACE = 1;
 private static final int SIMPLE = 2;

 public static SearchEngine getInstance() {
  if (instance == null) {
   synchronized (SearchEngine.class) {
    if (instance == null)
     instance = new SearchEngine();
   }
  }

  return instance;
 }

 private SearchEngine() {
  instance = null;
  currentAnalyzer = null;
  standardAnalyzer = new StandardAnalyzer();
  whitespaceAnalyzer = new WhitespaceAnalyzer();
  simpleAnalyzer = new SimpleAnalyzer();
 }

 public boolean setIndexDirectory(String directory) throws IOException {
  if (this.currentAnalyzer == null) {
   return false;
  }

  this.directory = directory;

  Directory fsDir = null;
  IndexWriter writer = null;
  try {
   fsDir = FSDirectory.getDirectory(directory, false);
   if (!fsDir.fileExists(“segments”)) {
    writer = new IndexWriter(this.directory, currentAnalyzer, true);
   }
  } finally {
   if (writer != null)
    try {
     writer.close();
    } catch (IOException ex) {
    };
   if (fsDir != null)
    try {
     fsDir.close();
    } catch (IOException ex) {
    }
  }

  return true;
 }

 public boolean setAnalyzer(int type) {
  if (type == STANDARD) {
   this.currentAnalyzer = this.standardAnalyzer;
  } else if (type == WHITESPACE) {
   this.currentAnalyzer = this.whitespaceAnalyzer;
  } else if (type == SIMPLE) {
   this.currentAnalyzer = this.simpleAnalyzer;
  } else {
   return false;
  }

  return true;
 }

 public boolean makeIndex(String word) throws IOException {
  if (this.currentAnalyzer == null) {
   return false;
  }

  IndexWriter writer = null;

  try {
   writer = new IndexWriter(this.directory, this.currentAnalyzer,
     false);

   Document document = new Document();
   document.add(new Field(“word”, word, Store.YES, Index.TOKENIZED));
   writer.addDocument(document);
  } finally {
   if (writer != null)
    try {
     writer.close();
    } catch (IOException ex) {
    };
  }
  return true;
 }

 public List searchIndex(String queryString) throws IOException,
   ParseException {
  Directory fsDir = null;
  IndexSearcher is = null;

  try {
   fsDir = FSDirectory.getDirectory(this.directory, false);
   is = new IndexSearcher(fsDir);

   QueryParser parser = new QueryParser(“word”, this.currentAnalyzer);

   Query query = parser.parse(queryString);
   Hits hits = is.search(query);

   List<String> list = new ArrayList<String>();

   if (hits.length() > 0) {
    for (int i = 0; i < hits.length(); i++) {
     Document doc = hits.doc(i);
     list.add(doc.get(“word”));
    }
   }

   return list;
  } finally {
   if (is != null)
    try {
     is.close();
    } catch (IOException ex) {
    }
   if (fsDir != null)
    try {
     fsDir.close();
    } catch (IOException ex) {
    }
  }
 }
}[/CODE]

사용은 다음과 같이 한다.

초기화 :
[CODE]SearchEngine engine = SearchEngine.getInstance();
// 0 : Standard Analyzer, 1 : Whitespace Analyzer, 2 : Simple Analyzer
engine.setAnalyzer(0);
setIndexDirectory(“C:\LuceneIndex”);[/CODE]

인덱스 생성 :
[CODE]engine.makeIndex(“아이군의 홈페이지 주소는 theeye.pe.kr이다”);[/CODE]

검색시 :
[CODE]// 검색결과 객체들이 list로 담겨 나옴, 알아서 재사용
List list = engine.searchIndex(“아이군”); [/CODE]

위와같다. 이 글을 보는 분들은 루씬에 대해 어느정도 사전지식이 있는 분일것이다. 이것을 이해하는것은 어렵지 않았을것이라 생각한다.

위와같은 예제로(각각의 인덱스는 String형 하나가 아닌, Beans형의 특정 데이터 객체였다) 10만건을 입력해 놓고 검색해 보니 0.1초 이상 걸리지 않았다.

검색엔진 알고리즘에 사전지식이 없는 사람도 이정도로 사용할수 있다는것에 감탄을 표하는 바이다.

각각의 Analyzer차이와 인덱스 및 검색 옵션 여러가지 Term들에 대해서는 앞으로 계속 짬짬이 글을 써보겠다.