태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

How FriendFeed uses MySQL to store schema-less data

2011/04/12 10:40 | Posted by 솔라리스™
By Bret Taylor · February 27, 2009

Background

We use MySQL for storing all of the data in FriendFeed. Our database has grown a lot as our user base has grown. We now store over 250 million entries and a bunch of other data, from comments and "likes" to friend lists.

As our database has grown, we have tried to iteratively deal with the scaling issues that come with rapid growth. We did the typical things, like using read slaves and memcache to increase read throughput and sharding our database to improve write throughput. However, as we grew, scaling our existing features to accomodate more traffic turned out to be much less of an issue than adding new features.

In particular, making schema changes or adding indexes to a database with more than 10 - 20 million rows completely locks the database for hours at a time. Removing old indexes takes just as much time, and not removing them hurts performance because the database will continue to read and write to those unused blocks on every INSERT, pushing important blocks out of memory. There are complex operational procedures you can do to circumvent these problems (like setting up the new index on a slave, and then swapping the slave and the master), but those procedures are so error prone and heavyweight, they implicitly discouraged our adding features that would require schema/index changes. Since our databases are all heavily sharded, the relational features of MySQL like JOIN have never been useful to us, so we decided to look outside of the realm of RDBMS.

Lots of projects exist designed to tackle the problem storing data with flexible schemas and building new indexes on the fly (e.g., CouchDB). However, none of them seemed widely-used enough by large sites to inspire confidence. In the tests we read about and ran ourselves, none of the projects were stable or battle-tested enough for our needs (see this somewhat outdated article on CouchDB, for example). MySQL works. It doesn't corrupt data. Replication works. We understand its limitations already. We like MySQL for storage, just not RDBMS usage patterns.

After some deliberation, we decided to implement a "schema-less" storage system on top of MySQL rather than use a completely new storage system. This post attempts to describe the high-level details of the system. We are curious how other large sites have tackled these problems, and we thought some of the design work we have done might be useful to other developers.

Overview

Our datastore stores schema-less bags of properties (e.g., JSON objects or Python dictionaries). The only required property of stored entities is id, a 16-byte UUID. The rest of the entity is opaque as far as the datastore is concerned. We can change the "schema" simply by storing new properties.

We index data in these entities by storing indexes in separate MySQL tables. If we want to index three properties in each entity, we will have three MySQL tables - one for each index. If we want to stop using an index, we stop writing to that table from our code and, optionally, drop the table from MySQL. If we want a new index, we make a new MySQL table for that index and run a process to asynchronously populate the index without disrupting our live service.

As a result, we end up having more tables than we had before, but adding and removing indexes is easy. We have heavily optimized the process that populates new indexes (which we call "The Cleaner") so that it fills new indexes rapidly without disrupting the site. We can store new properties and index them in a day's time rather than a week's time, and we don't need to swap MySQL masters and slaves or do any other scary operational work to make it happen.

Details

In MySQL, our entities are stored in a table that looks like this:

CREATE TABLE entities (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    id BINARY(16) NOT NULL,
    updated TIMESTAMP NOT NULL,
    body MEDIUMBLOB,
    UNIQUE KEY (id),
    KEY (updated)
) ENGINE=InnoDB;

The added_id column is present because InnoDB stores data rows physically in primary key order. TheAUTO_INCREMENT primary key ensures new entities are written sequentially on disk after old entities, which helps for both read and write locality (new entities tend to be read more frequently than old entities since FriendFeed pages are ordered reverse-chronologically). Entity bodies are stored as zlib-compressed, pickledPython dictionaries.

Indexes are stored in separate tables. To create a new index, we create a new table storing the attributes we want to index on all of our database shards. For example, a typical entity in FriendFeed might look like this:

{
    "id": "71f0c4d2291844cca2df6f486e96e37c",
    "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
    "title": "We just launched a new backend system for FriendFeed!",
    "link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}

We want to index the user_id attribute of these entities so we can render a page of all the entities a given user has posted. Our index table looks like this:

CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;

Our datastore automatically maintains indexes on your behalf, so to start an instance of our datastore that stores entities like the structure above with the given indexes, you would write (in Python):

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index])

new_entity = {
    "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"),
    "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
    "title": u"We just launched a new backend system for FriendFeed!",
    "link": u"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
    "published": 1235697046,
    "updated": 1235697046,
}
datastore.put(new_entity)
entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"))
entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"))

The Index class above looks for the user_id property in all entities and automatically maintains the index in the index_user_id table. Since our database is sharded, the shard_on argument is used to determine which shard the index gets stored on (in this case, entity["user_id"] % num_shards).

You can query an index using the index instance (see user_id_index.get_all above). The datastore code does the "join" between the index_user_id table and the entities table in Python, by first querying theindex_user_id tables on all database shards to get a list of entity IDs and then fetching those entity IDs from the entities table.

To add a new index, e.g., on the link property, we would create a new table:

CREATE TABLE index_link (
    link VARCHAR(735) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (link, entity_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

We would change our datastore initialization code to include this new index:

user_id_index = friendfeed.datastore.Index(
    table="index_user_id", properties=["user_id"], shard_on="user_id")
link_index = friendfeed.datastore.Index(
    table="index_link", properties=["link"], shard_on="link")
datastore = friendfeed.datastore.DataStore(
    mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
    indexes=[user_id_index, link_index])

And we could populate the index asynchronously (even while serving live traffic) with:

./rundatastorecleaner.py --index=index_link

Consistency and Atomicity

Since our database is sharded, and indexes for an entity can be stored on different shards than the entities themselves, consistency is an issue. What if the process crashes before it has written to all the index tables?

Building a transaction protocol was appealing to the most ambitious of FriendFeed engineers, but we wanted to keep the system as simple as possible. We decided to loosen constraints such that:

  • The property bag stored in the main entities table is canonical
  • Indexes may not reflect the actual entity values

Consequently, we write a new entity to the database with the following steps:

  1. Write the entity to the entities table, using the ACID properties of InnoDB
  2. Write the indexes to all of the index tables on all of the shards

When we read from the index tables, we know they may not be accurate (i.e., they may reflect old property values if writing has not finished step 2). To ensure we don't return invalid entities based on the constraints above, we use the index tables to determine which entities to read, but we re-apply the query filters on the entities themselves rather than trusting the integrity of the indexes:

  1. Read the entity_id from all of the index tables based on the query
  2. Read the entities from the entities table from the given entity IDs
  3. Filter (in Python) all of the entities that do not match the query conditions based on the actual property values

To ensure that indexes are not missing perpetually and inconsistencies are eventually fixed, the "Cleaner" process I mentioned above runs continously over the entities table, writing missing indexes and cleaning up old and invalid indexes. It cleans recently updated entities first, so inconsistencies in the indexes get fixed fairly quickly (within a couple of seconds) in practice.

Performance

We have optimized our primary indexes quite a bit in this new system, and we are quite pleased with the results. Here is a graph of FriendFeed page view latency for the past month (we launched the new backend a couple of days ago, as you can tell by the dramatic drop):

In particular, the latency of our system is now remarkably stable, even during peak mid-day hours. Here is a graph of FriendFeed page view latency for the past 24 hours:

Compare this to one week ago:

The system has been really easy to work with so far. We have already changed the indexes a couple of times since we deployed the system, and we have started converting some of our biggest MySQL tables to use this new scheme so we can change their structure more liberally going forward.

크리에이티브 커먼즈 라이선스
Creative Commons License

Twitter와 FriendFeed 이야기

2011/04/12 10:11 | Posted by 솔라리스™

  1. Twitter와 FriendFeed 이야기 (2008.8.27)
  2. 스코블은 블로그로 돌아올 것인가? (2008.12.25)
  3. Facebook과 소셜 검색(2009.8.11)

국내에서는 잘 모르시겠지만 해외에서는 Twitter라는 ‘소셜 메시징’ 서비스가 매우 인기입니다. 우리나라에서는 ‘마이크로 블로그’라고 알려진 Playtalk이나 Me2day의 원조 서비스로 잘알려져 있습니다만 제가 ‘소셜 메시징’이라고 한데는 다른 이유가 있습니다.

저는 웹 2.0을 초고속 인터넷망으로 인해 사람들이 인터넷에 직접 참여하면서 나온 문화적 결과라고 해석한 바 있습니다. 따라서, 우리 나라에서 나타난 (인터넷) 문화 현상이 해외에서도 공통적으로 나타나고 있죠. 마이스페이스나 페이스북은 아이러브스쿨이나 싸이월드와 같은 동인(動因)으로 생각하고 있구요. 블로그 확산 현상도 오마이뉴스 같은 개인 참여 미디어의 결과입니다.

그럼 Twitter는 무엇일까요? 블로그의 아류는 아니고 이게 어떻게 진화할지 꽤 궁금했는데요. 결과적으로 실시간 메시징 플랫폼으로 활용하고 있다고 봅니다. 솔직히 우리 나라도 SMS와 메신저 사용량이 증가하면서 이메일 사용량이 급격하게 낮아졌고, 이제 커뮤니케이션 가속 시대에 접어드는 문화적 변화가 있었습니다. 메신저나 SMS에 답이 안오면 “씹는다”라는 말이 생겼으니 삶이 더 각박해졌죠.

소셜 메시징 플랫폼의 한계
사람들은 이메일이 아니라 Twitter를 열어 놓고 서로 리플놀이를 하면서 연락을 주고 받고 있습니다. 물론 메신저를 사용하지 않는 건 아닙니다만, 웹 접속 접근성이 높아졌기 때문에 솔직히 프로그램을 설치해야 하는 번거러움이 있는 폐쇄된 버디 네트웍을 쓰는 것보다 웹 기반 서비스를 더 선호하고 있습니다.

근데 Twitter를 메시징 플랫폼으로 쓰기 시작하면서 어려움을 겪게 됩니다. 즉, 전 세계 사람들이 함께 토론하면 어떻게 될까? 라는 딜레마에 빠진 것이죠. 웹 서비스에 메시징이 결합하면 그 트래픽은 상상도 못할 정도의 데이터 처리 능력이 필요합니다. 지금까지 전혀 겪어 보지 못한 문제입니다.

올해 3월에 Techcrunch가 잠정 집계한 통계에 따르면 가입자는 수백 만명이고, 매주 20만명 이상이 한 개이상 메시지를 올리고 있으며, 하루에 300만개의 메시지가 왔다갔다 한다고 합니다.

솔직히 이런 폭주 현상은 스타트업 서비스로서 깜짝 인기를 얻은 Twitter가 감내하기 어려운 구조라 볼 수 있습니다. 몇 개월전만 해도 Twitter의 DB 구조가 취약해서 누군가 항상 기계 앞에 있다가 마스터 DB가 장애가 나면 수동으로 슬레이브 DB를 작동시켜야 한다는 루머(?)가 나돌기도 했습니다. 실제로 Twitter의 장애는 아주 빈번해서 사람들을 정말 열받게 할 정도였습니다.

대안은 오픈화? 하지만…
Twitter는 작년 말 부터 올해 상반기까지 수 없는 장애와 기능 중단과 재가동이 계속 겹치면서 기술 아키텍터인 Blaine Cook이 회사를 떠나게 되고 현재 문제를 해결할려고 많은 노력을 하고 있습니다. 하지만, 사람들은 이미 마음을 돌리고 있는 형편입니다.

기술 조언가들은 Twitter가 집중되지 않는 분산형 Instant Messaging 서비스 체제로 바뀌어야 한다고 조언하고 있고, 극단적으로 도메인 네임 서비스 같은 공공재로 전환해야 한다고 주장하는 사람도 있습니다.

일반적으로 Twitter같은 서비스는 사용자 수가 어느 정도 임계치에 다다르면 네트워크 효과가 생겨 새로 등록하는 사람 때문에 장애가 일어날 수 밖에 없습니다. DataPortability의 Faraday Media와 Chris Saad는 해결 방법으로 Twitter 트래픽의 많은 부분을 차지 하는 외부 서드파티 애플리케이션(Twitterific, AlertThingy, Twhirl등)과 메신저, SMS 등을 분산화 하라고 조언하기도 했습니다.

게다가 identi.ca라는 서비스는 아예 Twitter 같은 서비스를 laconi.ca라는 오픈 소스 소프트웨어로 만들어서 공개해 버렸습니다. 이 프로그램을 설치하면 Twitter 같은 소셜 메시징 사이트를 손 쉽게 만들 수 있습니다. 현재 크리에이티브 커먼즈 라이센스를 이용하고 있기 때문에 여기에 관여하는 유명한 분들이 많이 이용하고 있습니다.

틈새를 파고든 FriendFeed
이럴 때 사람들은 대안을 찾게 마련인데요. 남의 불행은 나의 기회라고 여기에 FriendFeed가 끼어듭니다. 이 서비스는 전직 구글 개발자들이 나와서 만든 일종의 소셜 서비스 신디케이션인데 Plxaso 같은데서 이미 있던 아이디어를 구현한 것입니다.

블로그, Twitter, Flickr, 유튜브 등 자기가 활동하는 웹 서비스의 데이터를 한꺼번에 나열해서 서로 뭐하고 놀고 있는지 친구들과 공유하는 것이죠. 솔직히 이게 처음 나왔을 때는 사람들에게 거의 관심을 못받았습니다.

그런데, FriendFeed에서 외부에서 받은 각 항목에다 직접 댓글을 다는 기능을 추가하여 신디케이션에서 메시징 기능을 추가하면서 논란이 벌어졌습니다. 내가 다는 댓글이 FriendFeed인지 원래 서비스에 가야할 댓글인지 논란이 된것이죠.

솔직히 신디케이션만 해야지 거기서 커뮤니케이션을 하게 한다는 건 올블로그에서 직접 댓글 서비스를 하는 것과 같은 거니까 문제가 있는 것이죠. 사실 직접적인 영향을 받은 게 Twitter인데 FriendFeed의 40%는 Twitter의 메시지를 받아오고 있었으니까요.

실제로 Twitter가 빈번히 장애가 나고 FriendFeed가 리플 기능을 제공하기 시작한 올해 상반기 부터 FriendFeed 이용률이 급속히 증가하고 있습니다. 주목할 점은 사용자의 체류 시간이 증가하고 있는데 이들 대부분은 Twitter에서 온 액티브 사용자라는 점은 이견이 없습니다.

아직은 FriendFeed가 완벽히 Twitter를 대체하고 있다던지 Twitter 사용자 탈퇴 러시가 있다던지 하는 것은 아닙니다. Twitter는 유명세 덕분에 서비스가 불안정함에도 불구하고 계속해서 사용자들이 유입되고 있고요. 특히 일본에서도 인기가 높습니다.

다만 소셜 네트웍 사이트들 끼리 경쟁이 극도로 심해 지고 있다는 것은 사실입니다. 얼마전 Facebook이 새로 화면 개편을 했는데 거의 FriendFeed와 비슷한 모양으로 바뀌었습니다. 오늘 나온 FriendFeed의 새 베타 버전을 보니 거의 Facebook과 닮아 있네요. 이제 서로 서로를 베끼면서 소셜 메시징 혹은 소셜 신디케이션의 UI 표준이 거의 자리 잡혔다고 봐야겠습니다.

결국 뛰어난 기술 기반이 중요할 듯
솔직히 말해 웹 2.0의 개방이니 참여니 하는 것은 다 개뿔같은 소리이고, 현재 미국의 소셜 네트웍 비지니스 이 동네는 서로 죽이지 못해 안달나있는 심한 경쟁 체제에 접어 들어 있습니다. 웹 2.0의 성공을 거울 삼은 이들 업체들이 ‘개방 플랫폼화’라는 기술적 성공 요소는 잘 접목을 시켜 왔지만 심한 경쟁 때문에 빛을 바래고 있다고 봐야 합니다.

오픈 ID니 DataPortablity니 하는 것도 성공한 젊은 창업자들에게는 별로 안중에 없습니다. 결국 성공한 서비스가 자기네들 역사를 새로 쓰게 되겠지요.

누구도 흉내 못낼 검색 기술을 웹을 성공적으로 플랫폼화 시킨 구글의 입장에서도 자기네들 안으로 팔을 굽는 (폐쇄된) 서비스들이 못마땅해 보일 겁니다. 게다가 폭발적인 성장을 이루고 있는 소셜 네트웍 시장에 어떻게든 한자리 차지해야 한다는 강박관념도 있어 만들어낸 오픈 소셜 같은 훌륭한 철학이 젊은 애들의 철없는 장난 같은 서비스 때문에 매장되는 것 처럼 보이죠. 어른말 안듣는 애들 마냥 답답해 보이기 까지 합니다.

반짝이는 아이디어와 문화적 코드가 만나 스파크를 일으키려는 이런 도찐개찐 같은 서비스 경쟁은 ‘기획’의 세계에서는 일상 다반사로 일어납니다. 하지만, Twitter와 FriendFeed 사이의 문제에서 불거진 “전 세계 사람들이 동시에 메시지를 주고 받을려면 어떻게 해야할까?”라는 문제에 해답은 기획에 있지 않습니다. 바로 기술에 있죠.

MS가 전 세계 사람들이 함께 쓰는 운영체제를 만들고, 구글이 전 세계 정보를 끌어 모으고, 아마존이 전 세계에 컴퓨터를 빌려주겠다고 나서는 기반에는 자기 기술에 대한 경쟁력이 있기 때문입니다. 단기적 서비스 확장 뿐 아니라 장기적 인터넷 기업의 생존을 위해서도 기술 확보 능력이 정말 중요하다는 것을 반증하는 것이죠.

Twitter의 예에서 보듯이 아이디어가 구현되어 글로벌 서비스로 나오는 단계에서 기술력이 얼마나 중요한가 다시 한번 깨닫을 수 있습니다. 근본적 물음에 대한 해결 없이 수틀리면 그냥 돈(장비)으로 쳐바르는 우리네 기술력도 한번 돌이켜 봐야겠지요. 그리고 주위에 개발자들이 있으면 격려의 한마디 건네 주시는 것도 좋겠습니다.

이어지는 글… Twitter와 FriendFeed 두번째 이야기 – 스코블은 블로그로 돌아올 것인가?

새벽에 쓰는 글이라 좀 횡설수설합니다. 너무 심각하게 받아들이지 마시고 그냥 세상 밖 돌아가는 이야기라고 생각하시고 읽어 주세요.

http://channy.creation.net/blog/550

크리에이티브 커먼즈 라이선스
Creative Commons License

PassMark - G3D Mark

2010/10/12 13:39 | Posted by 솔라리스™
http://www.videocardbenchmark.net/high_end_gpus.html

PassMark - G3D Mark
High End Videocards - Updated 11th of October 2010
Videocard PassMark G3D Score
GeForce GTX 480 3,542
GeForce GTX 470 2,953
Radeon HD 5870 2,628
Radeon HD 5970 2,536
Quadro 5000 2,535
GeForce GTX 465 2,451
Radeon HD 5850 2,420
GeForce GTX 460 2,316
GeForce GTX 285 2,037
Radeon HD 5830 2,016
Radeon HD 4890 1,943
GeForce GTX 280 1,940
ASUS EAH4890 1,917
GeForce GTX 275 1,878
Quadro 4000 1,877
FirePro V8750 1,769
Radeon HD 4870 1,741
RV790 1,728
GeForce GTX 260 1,712
GeForce GTX 295 1,689
FirePro V8700 1,655
Radeon HD4870 X2 1,641
Radeon HD 5770 1,580
Radeon HD 4870 X2 1,536
Quadro FX 5800 1,513
ASUS EAH4870x2 1,466
Radeon HD 4850 X2 1,446
GeForce GTS 450 1,427
Radeon HD 5750 1,424
FirePro V5800 1,359
Quadro FX 4800 1,348
Radeon HD 4850 1,332
Radeon HD 4830 1,253
Radeon HD 4770 1,252
Mobility Radeon HD 4870 X2 1,250
Mobility Radeon HD 5870 1,249
FirePro M7820 1,224
GF 9800 GX2 1,204
Mobility Radeon HD 4870 1,170
Radeon HD 5670 1,162
GeForce 8800 Ultra 1,153
FirePro V4800 1,145
GeForce 9800 GTX 1,139
GeForce 9800 GTX+ 1,112
GeForce 9800 GTX/9800 GTX+ 1,094
Mobility Radeon HD 4850 1,078
GeForce GTS 250 1,070
FirePro M7740 1,069
Quadro FX 5600 1,062
GeForce GTS 150 1,060
GeForce 8800 GTX 1,052
WinFast GTS 250 1,047
Quadro FX 3800 1,014
GeForce 8800 GTS 512 1,007
Radeon HD 3870 X2 991
GeForce GTS 240 981
Quadro FX 3800M 980
GeForce 9800 GX2 976
Radeon HD 3870 961
Mobility Radeon HD 5850 954
FireGL V7350 951
GeForce 8800 GT 944
GeForce GT 140 931
FireGL V7700 930
GeForce 9600 GT 928
GeForce 9800 GT 916
GeForce GTX 280M 910
GeForce GTX 285M 904
Radeon X1900 CrossFire Edition 892
Radeon HD 3850 X2 888
Quadro FX 4600 886
WinFast PX9800 GT 885
Quadro FX 2800M 882
WinFast PX8800 GTS TDH 878
FireGL V7200 866
GeForce 8800 GTS 862
GeForce GTS 360M 860
Mobility Radeon HD 3870 X2 859
Radeon X1950 CrossFire Edition 857
FireGL V8600 857
WinFast PX9600 GT 853
Quadro FX 3700 844
Radeon X1950 Pro 820
Radeon HD 3850 806
GeForce GTS 160M 799
GeForce 9800M GTS 793
Radeon X1900 GT 785
GeForce GT 240 782
Quadro FX 3700M 779
Radeon HD 4670 771
Radeon X1950 GT 766
FirePro V5700 761
Radeon HD 5570 742
Radeon HD 2900 XT 741
Radeon HD4670 733
FirePro V7750 731
GeForce 7900 GTX 727
GeForce 9800M GTX 726
RADEON X800 XT Platinum Edition 724
Radeon X1800 GTO 715
GeForce 9800M GT 712
Radeon HD 3850 AGP 709
Quadro FX 1800 709
Radeon HD 2900 PRO 707
GeForce 8800 GS 706
GeForce GTX 260M 700
GeForce 9800M GS 696
All-in-Wonder X1900 696
FireGL V7600 694
Quadro FX 2700M 679
RADEON X850 XT Platinum Edition 676
GeForce 8800M GTX 673
GeForce 7950 GT 672
GeForce GTS 250M 662
GeForce 8800M GTS 661
nForce 760i SLI 658
GeForce GT 320 658
Mobility Radeon HD 5730 657
FireGL V8650 654
GeForce 9600 GSO 512 647
Quadro FX 3600M 646
RADEON X850 XT 642
Radeon Processor DM 1 639
GeForce GT 330 637
GeForce GT 425M 628
All-in-Wonder X1800XL 628
Quadro FX 1800M 627
RADEON X800 XT 624
GeForce 9700M GTS 623
GeForce GT 335M 622
ALL-IN-WONDER X800 XL 619
GeForce 7900 GT/GTO 617
VisionTek RADEON X1950 Pro XGE 615
Mobility Radeon HD 5650 610
GeForce 7900 GS 607
FirePro V3750 602
Quadro FX 3500 592
Quadro FX 5500 590
GeForce Go 7900 GTX 589
FireGL V7100 588
Mobility Radeon HD 4670 585
GeForce 9600 GSO 583
GeForce GT 230 573
GeForce 7900 GT 572
RADEON X800 XL 570
Mobility Radeon HD 5165 570
GeForce Go 7950 GTX 564
GeForce 7800 GTX 563
Radeon X800 CrossFire Edition 558
Mobility Radeon HD 560v 556
MOBILITY RADEON X1800 551
GeForce GT 130 549
WinFast GT 220 546
Quadro FX 2500M 545
Quadro FX 3500M 541
Mobility Radeon X1900 538
Quadro NVS 510M 536
GeForce Go 7800 GTX 536
GeForce 9600 GS 529
GeForce 7800 GT 527
GeForce 7950 GX2 527
GeForce 8600 GTS 525
Mobility Radeon HD 4650 523
Quadro FX 4500 523
RADEON X800 PRO 508
GIGABYTE Radeon X1650 508
RADEON X800 PRO/GTO 505
Mobility Radeon HD 3850 502
Quadro FX 880M 501
NVS 5100M 499
Mobility Radeon HD 550v 497
Radeon HD 2900 GT 497
GeForce 7800 GS 492
Quadro FX 1500M 486
Quadro FX 1500 483
RADEON X800 GTO 483
GeForce GT 330M 481
FirePro V3800 481
FireGL V5100 475
Quadro FX 3450 466
GeForce 6800 Ultra 460
Radeon HD 4650 459
Sapphire RADEON X1600 XT 459
Quadro FX 580 455
GeForce GT 230M 455
GeForce GT 220 447
Sapphire RADEON X800 446
Sapphire RADEON X800 GT 443
WinFast PX7600 GT 443
GeForce 6800 GS 443
Quadro FX 3450/4000 SDI 442
GeForce GT 240M 441
GeForce Go 7900 GS 440
Quadro FX 1700M 434
FireGL V5600 433
Quadro FX 770M 431
RADEON X800GT 430
WinFast PX9500 GT 429
FireGL V5200 420
GeForce 9700M GT 416
G96-950-GL 414
GeForce 6800 GT 409
Quadro FX 3400 408
VMware SVGA 3D 407
GeForce 9400 406
Quadro FX 1600M 402
RADEON X800 GT 398
GeForce 7600 GT 397
GeForce 6800 GTO 393
GeForce 8600 GT 389
GeForce GT 120 388
GeForce Go 6800 Ultra 387
GeForce 8700M GT 386
Quadro NVS 320M 383
GeForce 9650M GS 380
GeForce GT 130M 378
Radeon HD 2600 XT 375
GeForce 6800 GS/XT 370
WinFast PX8600 GT 367
GeForce GT 320M 365
Mobility Radeon HD 4550 363
GeForce 9500 GT 357
GeForce 9500 GS 356
Radeon HD 4550 354
Quadro FX 570M 352
Quadro FX 3400/4400 339
FireGL V3400 338
Radeon HD 2600 XT AGP 336
GeForce 9600M GT 334
128MB DDR Radeon 9800 Pro 331
Intel Media Accelerator HD 331
WinFast PX9400 GT 330
MOBILITY RADEON HD 4530 / 4570 330
Quadro FX 380 329
RADEON 9800 PRO 328
RADEON 9800 XT 328
Radeon HD 5450 325
RADEON X800 SE 325
GeForce 6800 XT 324
Mobility FireGL V5725 324
GeForce 9400M G 321
Intel HD 319
GeForce GT 120M 319
GeForce Go 7600 GT 319
GeForce 320M 317
nForce 980a/780a SLI 316
GeForce GT 220M 315
GeForce Go 7800 315
GeForce 9600M GS 311
Mobility Radeon HD 5470 311
Mobility Radeon HD 5145 308
Quadro FX 560 307
GeForce 315 306
Quadro FX 1700 305
Sapphire RADEON X700 PRO 304
Radeon HD 3670 303
Mobility Radeon HD 3670 303
Radeon HD 4450 303
Mobility Radeon HD 2600 XT 302
MEDION RADEON X740XL 302
Quadro FX 380 LP 302
GeForce 6800 300
GeForce 9650M GT 299
Mobility Radeon HD 5450 298
VisionTek Radeon X1650 Pro 295
GeForce 6700 XL 291
Sapphire RADEON X1600 PRO 291
FireGL X1 288
Quadro FX 1400 288
RADEON 9700 PRO 287
FireGL V5000 287
NVS 3100M 286
GIGABYTE Radeon X1600 PRO 286
MSI NX7600 GS 286
GeForce Go 6800 286
Mobility Radeon HD 5430 285
GeForce 310M 285
Mobility Radeon HD 4570 283
GeForce 8600M GT 283
MOBILITY RADEON 9800 281
Copyright Passmark 2010
크리에이티브 커먼즈 라이선스
Creative Commons License

실패에서 배웠다! NHN 첫SNG 도전

2010/09/10 15:07 | Posted by 솔라리스™
페이스북의 팜빌이 성공한 후 새로운 장르의 가능성을 엿본 게임업계의 관심은 너나할것없이 SNG로 몰리기 시작했다. 최근 한국에서도 소셜 네트워크나 소셜 게임에 대한 세미나가 여러차례 진행되었고, 게임 관련 매체에서도 SNG라는 장르에 대해 비중있게 다루어주고 있다.

9월 8일 NHN의 개발자들이 한자리에 모인 2010 NHN Deview에서, Facebook의 시장에 첫발을 내딛은 NHN의 소셜 네트워크 게임인 Richtown의 사례에 대한 발표가 있었다.

새로운 시장으로의 도전과 높은 진입장벽으로 인한 시행착오, 그리고 앞으로의 가능성까지... NHN의 최초 SNG이며 Facebook을 통해 전세계의 게이머를 대상으로 서비스된 소셜게임, Richtown의 사례를 김기영 PM이 직접 발표를 통해 소개해 주었다.







발표를 담당한 김기영 PM은 2002년 한게임 게임 제작 공모전에서 은상을 수상했고 지금까지 6년여 동안 게임기획 업무를 담당하고 있는 베테랑 개발자로 RichTown 프로젝트에서 PM 및 게임기획자로 활동하고 있다.

NHN에서 SNG에 대한 움직임을 시작한 것은 2010년 1월로, SNG 프로젝트팀이 구성되어 1월 중순부터 개발이 시작된 뒤 6월 28일 오픈되기까지 직접 체험해봐야 알 수 있는 여러 난관들을 겪었다는 설명이 있었다.




김기영 PM이 꼽은 첫번째 난관은 SNG만의 독특한 게임구조때문에 생기는 서버의 문제.





MMORPG는 물론이고 웹게임들까지 기본적으로는 서버를 기반으로 인원이 나누어지는 구조를 채택하고 있으나, SNG는 서버나 채널로 유저를 나눠서 서비스할 수 없다고 한다.

페이스북의 팜빌같은 예에서 볼 수 있듯이 수백 수천만명이 SNG에 접속할 수 있기 때문에 DB를 통해 상호 작용이 가능하게 구성하면서 물리적인 서버는 여러개를 두는 구조가 필요했다는 것이다.



두번째 난관은 지역적인 문제로, 게임 서버 등의 기본 인프라가 모두 한국에 있는 반면 서비스는 전세계를 대상으로 하기 때문에 어쩔수 없이 발생하는 지연현상.




아무리 서버가 좋아도 해외의 서버에 비해 0.3초 정도의 지연이 생길 수 밖에 없다는 것인데, 이런 지연 현상을 해결하기 위해서 미국에 인프라를 새로 구축하는 것은 배보다 배꼽이 더 큰 일이 될 수도 있다.

결국 NHN의 경우 게임을 개발하면서 Richtown의 주된 활동과 규칙을 일정 시간이 걸려야 완료되는 형태로 바꾸고 이미지 서버는 NHN USA의 현지 네트워크를 사용해 해결했다고 한다.







세번째 난관은 테스트 계정의 문제. 게임을 개발할 때 실제 페이스북에 연동되는 다양한 상황의 개발 및 테스트가 필요하지만, 계정은 모두 핸드폰 인증이 필요하기 때문에 테스트 계정을 확보하는 것이 쉽지 않다고 한다.

페이스북과의 연동을 감안한 테스트는 현재 만족스러운 해결 방법이 없기 때문에 페이스북을 대상으로 SNG를 개발하고 있다면 고려해야할 사항 중의 하나라고 한다.





네번째 난관은 시간을 기반으로 하기 때문에 테스트와 밸런스가 어렵다는 것.

SNG의 경우 건물을 완성하는데 적게는 1시간부터 길게는 며칠씩 걸리는 것이 기본이기 때문에 시간이 오래 걸리는 게임 규칙을 그대로 적용하면 개발 및 테스트가 어려워질 수 밖에 없다.

결국 게임의 밸런스를 시험하기 위해서 게임을 오픈하기 2주 전까지 테스트를 위한 밸런스로 제작을 진행할 수 밖에 없었다는데, 이렇게 될 경우 실제 게임이 오픈된 뒤에 테스트와는 다른 결과가 나올 수도 있다는 것.

결국 김기영 PM은 밸런스 테스트를 위해서 엑셀의 VBA 기능을 활용한 시뮬레이션 툴을 따로 구축해서 밸런스를 테스트하는 과정을 별도로 거쳤다고 한다.





SNG를 개발하게 되면서 겪은 난관들에 대한 소개가 끝난 뒤에는, 실제 Richtown이라는 SNG를 서비스하면서 겪게된 경험들에 대한 발표가 이어졌다.

가장 주목할만한 점은 SNG의 성공을 가늠하는 신규 유저의 유입에 대한 것. 최초 Richtown에서도 광고를 통해 신규 유저들이 유입되었으나 유저들의 잔류율이 낮기 때문에 광고가 중단되자 바로 접속자가 감소되는 것을 확인할 수 있었다고 한다.

즉 광고나 크로스 마케팅, 친구의 유인책 등을 통해서 신규 유저들을 끌어들이는 것도 중요하고 그에 못지않게 게이머들의 잔류율을 높이는 것도 중요하다는 것이다.







김기영 PM은 실제 상위에 위치한 SNG 들의 경우 잔류율이 22%에 가깝지만, 하위로 갈수록 잔류율이 떨어져 1000위 근처의 게임들은 약 7% 정도밖에 되지 않는다면서, 초반 3분 안에 유저를 잡아낼 수 있느냐와 실제 SNG의 이용자들을 고려한 밸런스가 필요하다는 것을 강조했다.

잔류율을 높이기 위해서는 초반에 유저들의 시선을 사로잡을 수 있는 화려함과 친절함, SNG를 이용하는 유저들의 성향에 맞는 쉬운 밸런스를 추구해야할 필요가 있다는 것이다. 또한 이미 시장에 자리잡은 성공한 게임들과 연계하는 크로스 마케팅 역시 신규 유저의 유입을 위해 좋은 선택이라는 조언을 남겼다.







다시 일어설 각오와 준비만 되어 있다면 실패는 성공을 이끄는 최고의 경험이 될 수 있으며, 성공보다 실패에서 더 많은 것을 배울 수도 있다.

결과만 놓고 보자면 NHN의 첫 SNG 도전은 성공하지 못했으나, 동시접속자 수백만을 넘나드는 정상급 SNG의 화려함에 가려진 도전과 실패를 현장 그대로의 목소리로 가감없이 들을 수 있었다는 점에서 충분히 가치있는, 그리고 지금 SNG를 준비하는 업체나 개발자를 꿈꾸는 학생들이라면 귀담아 들을 필요가 있는 발표의 자리였다.

한편 올해 하반기인 10월에는 현재 베타 상태인 앱 팩토리가 오픈할 예정이라고 밝혔으며, 김기영 PM의 Richtown 역시 페이스북의 경험을 토대로 다양한 업데이트를 준비하는 한편, 앱 팩토리에도 함께 진출할 예정이다.



http://www.inven.co.kr/webzine/news/?news=30439
크리에이티브 커먼즈 라이선스
Creative Commons License

ROLLBACK SEGMENT의 MINEXTENTS를 20 이상으로 하면 좋은 이유
=========================================================

PURPOSE



이 자료는 다음과 같은 주제에 대하여 소개하는 자료이다.
이 문서는 database application의 요구 사항을 충족시키기 위해 고려되어
져야 할 rollback segment tablespace 구성에 관한 내용을 담고 있다.

Creating, Optimizing, and Understanding Rollback Segments



-Rollback Segment 구성과 기록 방식
-Transaction에 Rollback Segment를 할당하는 Oracle 내부 메커니즘
-Rollback Segment 크기와 갯수
-Rollback Segment의 크기와 갯수 결정을 위한 테스트
-Rollback Segment extent의 크기와 갯수
-Rollback Segment의 minextents를 20 이상으로 하면 좋은 이유?
-Rollback Segment의 Optimal storage parameter와 Shrink

Explanation



Rollback Segment 구성과 기록 방식


Rollback segment는 extent라 불리는 연속적인 여러 개의 block으로 구성된다.
Rollback segment는 ordered circular 방식으로 extent를 쓰게 되는데,
current extent가 full이 되면 next extent로 옮겨 가며 사용하게 된다.
Transaction은 rollback segment 내의 current location에 record를 쓴 다음,
record의 size 만큼 current pointer를 옮겨 간다.
Rollback segment에 현재 record가 쓰여지고 있는 위치를 "Head"라고 한다.
또한, "Tail"이란 용어는 rollback segment에서 가장 오래된 active
transaction record의 시작 위치가 되는 부분을 말한다.

Transaction에 Rollback Segment를 할당하는 Oracle 내부 메커니즘



새로운 transaction이 rollback segment 를 요청하면, 각 rollback segment
를 이용하고 있는 active transaction 갯수를 확인하여 가장 적은 갯수의
active transaction 을 가진 rollback segment를 할당하게 된다.
Rollback segment는 transaction load를 처리하기에 충분한 크기를 가져야
하고, 필요한 만큼의 rollback segment를 사용할 수 있도록 적당한 갯수의
rollback segment를 가져야 한다.

1. 한 transaction은 단 하나의 rollback segment만을 사용할 수 있다.
2. 같은 extent에 여러 transaction이 기록할 수 있다.
3. Rollback segment의 Head는 Tail에 의해 현재 사용 중인 extent를
침범하지 않는다.
4. 링 형태로 구성되어 있는 rollback segment의 extent들은 다음 extent를
찾을 때 절대 건너 뛰는 일이 없으며, 순서를 뒤바꾸어 사용하지도 않는다.
5. Head가 next extent를 찾지 못하면, 새로운 extent를 추가로 할당하고,
그 extent를 링 안에 포함시킨다.

위와 같은 원리를 감안할 때, transaction size 뿐만 아니라 transaction
time도 상당히 중요한 고려 사항이라는 것을 알 수 있다.

Rollback Segment 크기와 갯수



Rollback segment size가 충분한지 판단하는 기준은 transaction activity에
직접적으로 영향을 받는다. 주로 일어나는 transaction activity에 근거하여
rollback segment size를 결정하여야 하고, 잘 일어나지 않는 특수한 경우의
큰 transaction이 문제라면 별도의 rollback segment로 관리되어야 한다.
Transaction 발생 중 Head가 너무 빨리 wrap around 시켜서 tail을 catch하
지 않도록 하여야 하며, 자주 변경되는 data에 대해 long-running query가
수행되었을 경우 read-consistency가 유지될 수 있도록 rollback segment
가 wrap around되지 않아야 한다.

Rollback segment 갯수를 적당히 잡아야 하는 이유는 process들 간에
contention을 방지하기 위함이고, V$WAITSTAT, V$ROLLSTAT, V$ROLLNAME
view를 통해서 contention을 확인할 수 있으며, 조회문은 다음과 같다.

sqlplus system/manager

select rn.name, (rs.waits/rs.gets) rbs_header_wait_ratio
from v$rollstat rs, v$rollname rn
where rs.usn = rn.usn
order by 1;

위의 query에 의해 조회된 rbs_header_wait_ratio 가 0.01 보다 크면,
rollback segment 갯수를 추가한다.

Rollback Segment의 크기와 갯수 결정을 위한 테스트



1. Rollback segment tablespace 생성
2. 테스트하기 위해 생성할 Rollback segment 갯수 결정
3. 같은 크기의 extent로 rollback segment 생성
extent 갯수는 최대 확장 시 10 - 30 개 정도가 되도록 extent 크기를 결정
4. Rollback segment의 minextents는 2이다.
5. 테스트할 rollback segment와 system rollback segment만 online 상태로 한다.
6. Transaction을 수행하고, 필요하면 application을 load한다.
7. Rollback segment contention을 확인한다.
8. Rollback segment가 최대 얼마까지 확장하는지 모니터링한다.

Rollback Segment extent의 크기와 갯수


Rollback segment가 자라나는 최대 사이즈를 알 수 있는데, 이 수치를
"minimum coverage size"라 한다. 만약, contention이 발생한다면 rollback
segment 갯수를 늘려 가면 테스트를 반복한다. 또한, extent 갯수가 10개
미만이나 30개 이상이 될 필요가 있다면 extent 크기를 늘리거나 줄이면서
테스트를 반복해 나가면 된다.
Rollback segment의 extent 크기를 정할 때, 각 extent는 모두 같은 크기로
생성할 것을 recommend한다.
Rollback tablespace의 크기는 extent size의 배수로 지정한다.
최적의 성능을 위한 rollback segment의 minextents는 20 이상이어야 한다.

Rollback Segment의 minextents를 20 이상으로 하면 좋은 이유?



Rollback segment는 dynamic하게 allocate되고, 더 이상 필요 없게 되었을 때
(만약, Optimal parameter가 셋팅되어 있으면) 모두 commit된 extent에
대해서는 optimal size 만큼만 남기고 release(deallocate)된다.
Rollback segment가 적은 수의 extent를 가질 수록, space 할당/해제 시
extent 수가 많을 때보다 큰 사이즈의 space가 할당되고, 해제된다.

다음과 같은 예를 들어 보자.
200M 정도의 rollback segment가 있는데, 100M 짜리 2개의 extent로 이루어져
있다고 가정해보자. 이 rollback segment에 추가로 space를 할당해야 할 일이
생겼을 때, 모든 rollback segment extent는 같은 크기를 가져야 한다는 점을
감안할 때, 100M 짜리 extent를 하나 더 할당해야 할 것이다.
이 결과 직전의 rollback segment 크기에 비하여 50% 만큼의 크기 증가분이
생겨나게 된 것인데, 실제 필요로 하는 space보다 더 많은 space가 할당되었을
것이다.

이와 반대로, 10M 짜리 extent 20개로 구성된 200M 짜리 rollback segment를
생각해보자.
여기에 추가로 space를 할당해야 할 일이 생겼을 때, 10M 짜리 extent 하나만
추가되면 되는 것이다.
Rollback segment가 20개 또는 그 이상의 extent로 구성되어 있다면 extent가
하나 더 증가할 경우가 생겼을 때, rollback segment의 전체 크기가 5% 이상은
늘어나지 않는다는 것이다.
즉, space의 할당과 해제 작업이 보다 유연하고 쉽게 일어날 수 있다.

요약하면, rollback segment의 extent 갯수를 20 이상으로 잡으면 space
할당과 해제가 "보다" 수월해진다.
실제로 extent 갯수를 20 이상으로 잡았을 때, 처리 속도가 훨씬 빨라진다는
사실이 많은 테스트 결과 밝혀졌다.
한가지 확실한 사실은, space를 할당하고 해제하는 작업은 cost가 적게 드는
작업이 아니라는 사실이다.
실제로 extent가 할당/해제되는 작업이 일어날 때, performance가 저하되는
일이 발생한다는 것이다.
Extent 하나에 대한 cost는 별 문제가 안 된다고 할지라도, rollback segment
는 끊임없이 space를 할당하고 해제하는 작업을 반복하기 때문에 작은 크기의
extent를 갖는 것이 cost 측면에서 훨씬 효율적이라는 결론이다.

Rollback Segment의 Optimal storage parameter와 Shrink



Optimal은 deallocate 시에 rollback segment 내에 optimal size 만큼의
extents를 유지하기 위해 사용하는 rollback segment storage parameter이다.
다음과 같은 명령으로 사용한다.

alter rollback segment r01 storage (optimal 1m);

Optimal size는 storage 절 안에서 기술되어야 한다.
Optimal size 이상이 되면, 모두 commit된 extent에 대해서는 optimal size
만큼만 남기고 release된다.
즉, optimal에서 지정한 크기 만큼만 rollback segment를 유지하겠다는
뜻이며, 일정한 크기로 늘어났다가 다음번 tx이 해당 rbs를 취할 경우
optimal size만큼 resize하는 option이다.

rbs의 가장 최근에 사용된 extent가 다 차서 다른 extent를 요구할 때
이 optimal size와 rbs size를 비교하게 되며, 만약 rbs size가 더 크다면
active tx에 관여하지 않는 tail extent에 대하여 deallocation이 이루어진다.
특정 rollback segment가 너무 큰 space를 차지해서 다른 rollback segment가
extent를 발생할 수 있는 여유 공간을 부족하게 만들기 때문에 이를 극복하기
위해서 optimal size를 지정할 필요가 있다.
즉, optimal parameter를 지정하면 space availability 측면에서 효율적이다.

다음과 같이 shrink 명령을 수행하는데, size를 지정하지 않으면 optimal
size 만큼 shrink된다.

alter rollback segment rbs_name shrink to size;

Shrink 명령 수행 후, 바로 줄어들지 않는 경우가 있는데,
transaction이 있는 경우는 줄어들지 않고, transaction이 종료되면 줄어든다.
Optimal이 적용되는 시간은 session이 빠져 나가고 약 5~10 분 정도 걸린다.

적당한 OPTIMAL SIZE?
=> 20 ~ 30 extents 정도가 적당한데, batch job의 성격에 따라 size는 달라
지며 각 optimal의 합이 datafile의 size를 넘어도 전혀 상관없다.
Optimal size를 initial, next와 같게 주면 extent가 발생하는 매번 shrink가
일어나므로 좋지 않다.
RBS들의 평균 크기를 구하여 이것을 optimal 크기로 지정하여 사용하는 것을
권한다.

다음의 query를 이용하여 peak time에 rollback segment들의 평균 크기를 구한다.

select initial_extent + next_extent * (extents-1) "Rollback_size", extents
from dba_segments
where segment_type ='ROLLBACK';

이 크기의 평균값(bytes)을 rollback segment들의 optimal size로 사용할 수
있다.
주의할 사항은 너무 자주 shrink된다거나 optimal 값을 너무 작게 주면
ora-1555 : snapshot too old error가 발생할 확률이 높아지므로,
사용하지 않는 것이 좋을 수도 있고, 되도록 큰 값으로 셋팅해야 한다.

Rollback segment의 optimal size를 확인할 수 있는 view는 V$ROLLSTAT
이라는 dynamic view로서 OPTSIZE column에서 확인이 가능하다.

Example


none

Reference Documents

 출처 : OTN Discussion Forum

크리에이티브 커먼즈 라이선스
Creative Commons License

Oracle Archive log mode 설정

2009/01/14 09:31 | Posted by 솔라리스™

spfile로 운영시

sysdba로 login
DB shutdown, DB mount
(shutdown immediate -> startup mount)

alter system set log_archive_start=true scope=spfile;
alter system set log_archive_dest_1='아카이브 파일이 저장될 경로' scope=spfile;
alter system set log_archive_format='파일이름_%t_%s_%r.arc' scope=spfile;
alter database archivelog;
alter database open;

Archive log mode 확인
select log_mode from v$database;
-> Archive log mode로 되어있을시, ARCHIVELOG라고 나온다.

pfile로 운영시

pfile에 다음항목 추가
log_archive_dest_1 = "location=아카이브 파일이 저장될 경로"
log_archive_format = arch_%t_%s_%r.arc (아카이브 파일 포맷)


sysdba로 login

DB shutdown, DB mount
alter database archivelog;
archive log list;
alter database open;
Post from http://nasty.tistory.com/82
크리에이티브 커먼즈 라이선스
Creative Commons License

8가지 자동 완성 Ajax Scripts

2009/01/13 17:22 | Posted by 솔라리스™

1. jqac
 - jQuery를 활용한 Auto-complete/suggest javascript 라이브러리
 - 다운로드 : http://code.google.com/p/jqac/downloads/list
 - 데모 : http://www.cs.bgu.ac.il/~ygleyzer/files/utils/jqac/jqac_example.html

2. AutoComplete 1.2
 - prototype과 scriptaculous를 활용한 Auto-complete javascript 라이브러리
 - 다운로드 : http://www.beauscott.com/examples/autocomplete/autocomplete.zip
 - 데모 : http://www.beauscott.com/examples/autocomplete/doc/examples.html
 
3. AutoCompleter(mootools)
 - MooTools를 활용한 Auto-complete javascript 라이브러리
 - 다운로드 : http://digitarald.de/project/autocompleter/#download
 - 데모 : http://digitarald.de/project/autocompleter/1-1/showcase/delicious-tags/, http://digitarald.de/project/autocompleter/1-1/showcase/local/

4. AJAX AutoComplete
 - Jim Roos가 자체로 만든 Ajax framework
 - 다운로드 : http://jimroos.com/AutoComplete.zip
 - 데모 : http://www.jimroos.com/2007/05/ajax-autocomplete.html

5. YUI Autocomplete
 - Yahoo! UI Library로 만든 Auto-complete
 - 다운로드 : http://brandspankingnew.net/download.php?file=autosuggest_v2.zip
 - 데모 : http://developer.yahoo.com/yui/examples/autocomplete/index.html

6. AutoSuggest
 - 심플한 Javascript를 활용한 Auto-complete
 - 다운로드 : http://www.brandspankingnew.net/specials/ajax_autosuggest/ajax_autosuggest_autocomplete.html
 - 데모 : http://www.brandspankingnew.net/specials/ajax_autosuggest/ajax_autosuggest_autocomplete.html

7. dhtmlxCombo
 - 콤보형 Auto-complete
 - 다운로드 : http://www.dhtmlx.com/docs/download/dhtmlxCombo.zip
 - 데모 : http://dhtmlx.com/docs/products/dhtmlxCombo/index.shtml

8. AutoComplete (mootools)
 - scriptaculous를 활용한 Auto-complete
 - 다운로드 : http://script.aculo.us/
 - 데모 : http://demo.script.aculo.us/ajax/autocompleter
크리에이티브 커먼즈 라이선스
Creative Commons License

10g RMAN backup script

2009/01/12 10:24 | Posted by 솔라리스™
[oracle@localhost ~] $ rman target /

# database에 full backup을 수행한다.
RMAN> BACKUP AS COMPRESSED BACKUPSET INCREMENTAL LEVEL 0 DATABASE
   2  TAG rman_db_full
   3  FORMAT '/home/oracle/backup/%d.%T.%u.dbfull.rman';

# level 0 full backup 수행이후의 증분에 대해 incremental backup을 수행한다.
RMAN> BACKUP AS COMPRESSED BACKUPSET INCREMENTAL LEVEL 1 DATABASE
   2  TAG rman_db_inc
   3  FORMAT '/home/oracle/backup/%d.%T.%u.dbinc.rman';

#### archive log를 백업한다.
# 현재의 redolog 파일을 아카이브화 시킨다.
RMAN> SQL 'ALTER SYSTEM ARCHIVE LOG CURRENT';
# archive log를 백업하고, 원본 archive log는 삭제한다.
RMAN> BACKUP AS COMPRESSED BACKUPSET ARCHIVELOG ALL DELETE INPUT
   2  TAG rman_arc
   3  FORMAT '/home/oracle/backup/%d.%T.%u.arc.rman';

# 컨트롤 파일 백업.
# 컨트롤 파일이 없다면 컨트롤 파일 장애시 복구 할 수 없을 것이다.
RMAN> BACKUP AS COMPRESSED BACKUPSET CURRENT CONTROLFILE
   2  TAG rman_ctl
   3  FORMAT '/home/oracle/backup/%d.%T.%u.ctl.rman';

# 과거 백업본중 만료된 백업본 삭제.
RMAN> DELETE NOPROMPT EXPIRED BACKUP;
RMAN> DELETE NOPROMPT EXPIRED COPY;

# 과거 백업본 중 지금의 백업에 의해 필요없어진 백업본 삭제.
RMAN> DELETE NOPROMPT OBSOLETE;

# 실제 archivelog 목록과 DB상에 기록된 archivelog 목록을 비교 검사한다.
RMAN> CROSSCHECK ARCHIVELOG ALL;

# archivelog를 삭제한다.
# NOPROMPT 옵션이 있을경우 Y/N를 묻지 않는다.
RMAN> DELETE [NOPROMPT] ARCHIVELOG ALL;
크리에이티브 커먼즈 라이선스
Creative Commons License

Ascii Code Chart (for use with ord and chr functions)

2009/01/08 17:10 | Posted by 솔라리스™

æ







backspace
tab
linefeed


c return


















space
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/
00
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~

€


ƒ




ˆ

Š

Œ

?br>
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143








˜

š

œ

?br>Ÿ

¡
¢
£

¥
|
§
¨
©
ª
«
¬
¯
®
¯
°
±
²
³
´
µ

·
¸
¹
º
»
¼
½
¾
¿
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
À
Á
Â
Ã
Ä
Å
Æ
Ç
È
É
Ê
Ë
Ì
Í
Î
Ï
Ð
Ñ
Ò
Ó
Ô
Õ
Ö

Ø
Ù
Ú
Û
Ü
Ý
Þ
ß
à
á
â
ã
ä
å
æ
ç
è
é
ê
ë
ì
í
î
ï
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
ð
ñ
ò
ó
ô
õ
ö
÷
ø
ù
ú
û
ü
ý
þ
ÿ

240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255

크리에이티브 커먼즈 라이선스
Creative Commons License

URLEncode Code Chart

2009/01/08 17:07 | Posted by 솔라리스™



æ







backspace
tab
linefeed


c return


















space
!
"
#
$
%
&
'
(
)
*
+
,
-
.
/
%00
%01
%02
%03
%04
%05
%06
%07
%08
%09
%0a
%0b
%0c
%0d
%0e
%0f
%10
%11
%12
%13
%14
%15
%16
%17
%18
%19
%1a
%1b
%1c
%1d
%1e
%1f
%20
%21
%22
%23
%24
%25
%26
%27
%28
%29
%2a
%2b
%2c
%2d
%2e
%2f
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
%30
%31
%32
%33
%34
%35
%36
%37
%38
%39
%3a
%3b
%3c
%3d
%3e
%3f
%40
%41
%42
%43
%44
%45
%46
%47
%48
%49
%4a
%4b
%4c
%4d
%4e
%4f
%50
%51
%52
%53
%54
%55
%56
%57
%58
%59
%5a
%5b
%5c
%5d
%5e
%5f
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
{
|
}
~

€


ƒ




ˆ

Š

Œ

?br>
%60
%61
%62
%63
%64
%65
%66
%67
%68
%69
%6a
%6b
%6c
%6d
%6e
%6f
%70
%71
%72
%73
%74
%75
%76
%77
%78
%79
%7a
%7b
%7c
%7d
%7e
%7f
%80
%81
%82
%83
%84
%85
%86
%87
%88
%89
%8a
%8b
%8c
%8d
%8e
%8f








˜

š

œ

?br>Ÿ

¡
¢
£

¥
|
§
¨
©
ª
«
¬
¯
®
¯
°
±
²
³
´
µ

·
¸
¹
º
»
¼
½
¾
¿
%90
%91
%92
%93
%94
%95
%96
%97
%98
%99
%9a
%9b
%9c
%9d
%9e
%9f
%a0
%a1
%a2
%a3
%a4
%a5
%a6
%a7
%a8
%a9
%aa
%ab
%ac
%ad
%ae
%af
%b0
%b1
%b2
%b3
%b4
%b5
%b6
%b7
%b8
%b9
%ba
%bb
%bc
%bd
%be
%bf
À
Á
Â
Ã
Ä
Å
Æ
Ç
È
É
Ê
Ë
Ì
Í
Î
Ï
Ð
Ñ
Ò
Ó
Ô
Õ
Ö

Ø
Ù
Ú
Û
Ü
Ý
Þ
ß
à
á
â
ã
ä
å
æ
ç
è
é
ê
ë
ì
í
î
ï
%c0
%c1
%c2
%c3
%c4
%c5
%c6
%c7
%c8
%c9
%ca
%cb
%cc
%cd
%ce
%cf
%d0
%d1
%d2
%d3
%d4
%d5
%d6
%d7
%d8
%d9
%da
%db
%dc
%dd
%de
%df
%e0
%e1
%e2
%e3
%e4
%e5
%e6
%e7
%e8
%e9
%ea
%eb
%ec
%ed
%ee
%ef
ð
ñ
ò
ó
ô
õ
ö
÷
ø
ù
ú
û
ü
ý
þ
ÿ

%f0
%f1
%f2
%f3
%f4
%f5
%f6
%f7
%f8
%f9
%fa
%fb
%fc
%fd
%fe
%ff

크리에이티브 커먼즈 라이선스
Creative Commons License
이전 1 2 3 다음