Manning-mongodb-in-action

Manning-mongodb-in-action

Part 1 Getting Started

  • JavaScript Shell, Ruby driver

1장. Database for Modern Web

목적은 딱 2가지

  • Throughput
  • Scale easily

key-value store와 Relational DB 의 조합에 유리한 feature를 갖고 있음. 중간쯤 특징을 갖는다.

  • 구체적으로 뭐??

Secondary Indexes

Secondary Index로 MongoDB는 B-tree로 구성함. 
Collection마다 64 개까지 Secondary Index를 만들 수 있음. 
RDBMS에서 제공하는 Index 종류는, 다 제공한다.

  • ascending, descending, unique, compound-key, geospartial index

Replication

  • Replica Set : MongoDB에서, 자기네 Replication topology에 붙인 이름.
    • 구성 : primary 1개, secondary 1+ 개
    • 동작 : primary는 R/W 모두 가능, secondary는 R만.
      primary가 죽으면, secondary 중 한 놈이 primary로 변신. 죽었던 primary가 살아나면, secondary로 남음.

Speed and Durability

  • Speed와 Durability는 거꾸로 도는 관계 : Memory에 쓰면, Disk보다 빠르긴 한데, Durability가 없다. 이런 식.

MongoDB’s core server and tools

  • Core Server는 C++로 개발.
  • /data/db 또는 C:/data/db 에 mongod (=Core Server)의 모든 data file을 저장함. (default path)

Database Driver

  • 언어별로 MongoDB Collection의 document와 유사한 자료 구조.
    Ruby Ruby hash
    python dictionary
    Java LinkedHashMap
  • 10gen이 공식적으로 제공하는 Driver들 
    C, C++, C#, Erlang, Haskell, Java, Perl, PHP, Python, Scala, Ruby

Database Tools (command-line utilities)

  • mongodump vs. mongorestore : BSON 형태로 dump하고, restore함. hot backup 용도.
  • mongoexport vs. mongoimport : JSON, CSV, TSV 형태의 data를 import/export함.
  • mongosniff : 가져온 BSON을 사람이 읽을 수 있게~ shell상에 뿌려줌.
  • mongostat : iostat와 유사. 계속 polling을 해서, 통계치를 당겨옴. 초당 query수…
  • bsondump
  • monfiles

MongoDB vs. other DB

  • auto-sharding은 Yahoo! PNUTS, Google BigTable에서 따온 것. 새로운 것이 아니다.
  • MySQL, MongoDB 공히 B-Tree index를 사용함.
  • Apache의 CouchDB는 JSON을 text로 그대로 저장함. (반면에, MongoDB는 BSON.) 그러나, 내부는 많이 유사함.
    CouchDB도 Secondary Index 지원함. 다른 점은 CouchDB의 Index는 map-reduce function을 써서, 정의됨. 
    scale하는 방법은 서로 약간 다름. replica도 약간 다름. CouchDB는 machine별로 쪼개서 갖지는 않음. 모든 machine이 complete set을 가짐.

Tips and Limitations

  • 64 bit machine에서 돌려라
    32 bit machine은 4GB memory addressing. 2G 정도는 OS kernel이나 다른 process가 써버리면, mongoDB는 남은 2G 안에서, data file들을 mapping 해야 함(memory-mapped file). 부족해~
  • dedicated server에서 돌려라
  • journaling 없이(operation에 대한 기록) 운용하는 경우, replication하면서 돌려라.
    mongoDB는 memory-mapped file을 사용하므로, 예상치 못한 shutdown 전후, 찌꺼기들은 깨질 수 있다.
  • memory-mapped file이 뭐지???

2장. MongoDB through JavaScript Shell

Indexing and explain()

  • ~.explain() 출력 결과 중
    “cursor” : “BasicCursor” // index를 사용하지 않음.을 의미함. 
    document 4개 찾으려고, 200,000개를 scan하게 할 순 없다. –> Index() 를 사용
  • db.xxx.ensureIndex( { 필드명 : 1 }) // 실행 이전에 유일한 index는 _id임.
    // 실행 이후에, 필드명에 해당하는 Index가 생성됨.
  • db.xxx.getIndexes() // Index가 정의된 상태를 조회함.
  • 필드명을 Index로 내세운 후, find(…).explain() 을 확인하면, 
    “cursor” : “BtreeCursor 필드명_1” // 와 같이, Btree Index를 사용하였음을 확인할 수 있음.
  • query optimization 에 활용

basic administration

  • 크게 CRUD command, database command(=admin commmad)로 구분 가능함.
    show dbs  
    show collections show tables과 동일함. show table 명령어도 먹힘.
    db.stats() db, colleciton에 대한 정보 조회. db 그 자체도 object 라고 보면 된다.
    db.xxx.stats() 특정 colleciton에 대한~ . xxx 는 colleciton 명
    db.runCommand( ) db.runCommand({dbstats: 1}) 과 db.stats()을 직접 호출하는 것과 같은 효과를 가짐.
      db.runCommand({collstats: ‘xxx’}) 와 db.runCommand({ })
  • 모든 database command는 $cmd 라고 불리는 special virtual collection에 query 가능한 형태로,implementation이 저장되어있음. 
    추측컨대, command를 입력하면, 이 implementation을 query해와서, 실행시켜서, 결과를 주는 듯.
  • db.runCommand({dbstats: 1}) 에서, dbstats 가 doc형태로 argument로 들어가는 것을 보면, 알 수 있음.
    MongoDB shell은 () 를 빼고 command를 입력하면, implemenation을 직접 shell상에서, 보여줌.
> db.runCommand()    // 명령어 실행

> db.runCommand      // runCommand()의 implemenation을 뿌려줌. 
 function (obj) {
    if (typeof obj == "string") {
      var n = {};
      n[obj] = 1;
      obj = n;
    }
    return this.getCollection("$cmd").findOne(obj);    // db.$cmd.findOne(obj) 와 별반 다르지 않다.
 }
> ls()         // 실제 디렉토리의 내용을 보여줌.
./.dbshell
./bsondump.exe
./mongo.exe
...

> ls           // ls 명령어의 definition을 보여줌.
function() {
    return nativeHelper.apply(ls_, arguments); 
}
  • tab-completion 기능 있음 : shell상에서 Tab 입력하면, matching되는 명령어를 고를 수 있음. 
    Tab 2번 누르면, 목록을 통으로 보여준다고 되어 있는데, 해보니 안 먹음. 확인할 것.

3장. Writing Program using MongoDB

  • Ruby client driver로 MongoDB 연결하기~
      JavaScript Ruby
    JSON 표기 시, key와 value 구분 : => (rocket이라고 부름.)
      JSON object hash data structure
  • client driver의 역할 3가지
    objectId 생성
    document를 언어별 structure –> BSON으로 변환 (응답은 <– 방향으로 변환)
    Server와 TCP socket 연결
    • ObjectId : 4 byte timestamp + 3 byte machine id + 2 byte process id + 3 byte counter = 12 byte, 
      Object니까, Globally Unique해야 한다.
      timestamp가 곧 생성시간이므로, 시간 기준으로 조회할 때, 기준값으로 사용할 수 있다.
    • BSON : storage에 들어가는 format이기도 하고, command format이기도 함.

Part 2 Application Development in MongoDB

4장. Document-oriented data

  • MongoDB는 Data Model — Object 간 Mapper가 따로 필요없다. document 자체가 object 처럼 보이니까, 따로 driver가 제공하는 API로 충분하다. 
    Data-Object Mapper의 예) Java의 Hibernate framework, Ruby의 ActiveRecord, …
  • document에 대한 URL을 만들때, ObjectId 대신, 사람이 읽을 수 있게, string 형태로 따로 필드를 정의하는 게 좋다. 
    이 필드를 ensureIndex()로 만들어서, PK로 사용함.

Nuts and bolts

  • db를 생성하면, dbpath ( /data/db ) 에 data file, meta-data, .. 모두 저장함.
    /data/db/ mongod.lock 파일에, server의 process id를 저장함.
    /data/db/ db명.ns namespace 파일, 16MB 로 size 고정. 24,000개의 name space만 가능함. 늘리는 option –nssize
    /data/db/ db명.0 // 64 MB
    /data/db/ db명.1 // 128 MB

db명.숫자 파일은 collection file들, collection 별로 숫자를 뒤에 붙여서, naming. 
미리 64 MB, 128 MB 이런 식을 preallocation 해둔다. 2 GB 가 될 때까지, 크기 2배씩 증가함. 이게 standard 방식. ( <–> capped collection)
preallocation을 안하게 하거나, size 조절 가능함.

  • db.stats() 에서 확인할 수 있는 값 중에, indexSize 값을 주목해야 한다. 
    RAM에 indexSize가 통으로 올라갈 수 있는 크기로 돌려야 성능이 훅 떨어지는 걸 막을 수 있다.
    dataSize 는 BSON의 size 합.
    storageSize 는 그 외 Collection에 부수적으로 따라가는 space의 전체 size. 부수적인 space를 extents 라고 부름.

Capped Collections

  • 크기가 무한정 늘어날 수 있음. 시스템이 허용하는 한. ( <–> standard collection)
    계속 insert하면, old document에 덮어쓰는 방식, log handling에 유용

System Collections

내부적으로 쓰는 collection, standard collection임.

  • db.system.namespaces
  • db.system.indexes : 각 collection들의 index를 죄다 가짐.

MongoDB는 내부적으로 replication 용도의 내부 collection으로는 standard 가 아닌, capped collection을 사용함. 이름은 “oplog.rs” 이며, replica set에 포함된 member들은 여기에 log를 쌓고, secondary node는 이걸 읽어다가 최근 update를 스스로 반영함. 
(오래된 건 자동으로 overwrite 되므로 capped collection 이 자연스럽다.)

Bulk Inserts

  • client 입장에서, Bulk Inserts 는 결과로 ObjectId의 array를 return함. 
    bulk 안에 몇 개를 싣는 게 이상이냐? 상황에 따라 변수가 많지만, 10..100 개 정도…

기타

  • BSON이 지원하는 String은 UTF-8
  • BSON이 Number은 int, double, long 딱 3개, 즉, 길어야 8 byte까지 밖에 못 쓴다. 
    dynamic language의 경우, 4 byte 혹은 8 byte를 driver가 알아서 결정할꺼고, 그외 explicit하게 선언할 수도 있다. 
    JavaScript shell의 경우, Number(), NumberLong(), NumberInt()를 써라. 말없으면, double 로 간주함.
  • $type : 변수 형을 확인. type을 번호로 정의함. type 1.. 18
  • document 크기 제한 : 16 MB ( MongoDB 2.0 기준), 크기를 제한하는 이유 : document를 너무 deep하게 정의하지 못하게, 또, server/client간 이동 시, performance도 고려.

5장. Queries and aggregation

Query Language

  • Query selector 
    특정한 값이 적용하는 것과 array에 적용한 것으로 크게 볼 수 있음. 
    value의 한 종류로, sub-document가 통으로 value 역할을 할 수도 있음. (sub-document 이란, doc안에 {} 가 value로 들어가 있는 것.)
    단, sub-doc이 통으로 value로 간주되면, byte-by-byte로 compare하는 것과 같음. 즉, field 순서가 바뀌면, 안 된다.
    • range operator
      $lt, $lte, $gt, $gte
      > db.users.find({age: {$gt: 0}, age: {$lt: 30}})   // error~
      
      > db.users.find({age: {$gt: 0, $lt: 30})          // good~
      
    • set operator
      $in, $all, $nin
      > db.products.find({tags: {$all: ["gift", "bird", "garden"]})   // gift && bird && garden
      
      > db.products.find({color: {$in: ["blue", "white"]})            // blue || white
      
    • boolean operator
      $ne, $not, $or, $and, $exists
      • $ne 와 $not의 차이점
        $ne 는 특정 값에 대한 부정~
        $not 은 MongoDB(다른 operator나, reg exp query)가 물어온 결과에 대한 부정. 즉, 그 결과 아닌 것 몽땅~
      • 부정의 의미가 이미 들어있는 $in, $nin, $gt, $lt 와 $not을 같이 쓰지 마라.
      • $in 은 key가 같을 때, 사용하고, $or 는 key가 다를 때 써야 한다.
      • $exists 는 MongoDB가 schemaless이기에, 존재할 수 밖에 없는 operator임. 그런 필드가 있긴 한가? 의 의미.
        > db.products.find({detail.color: {$exists: true})      // or false; defail.color 이라는 필드가 있는/없는 document를 보여줘.
        
        > db.products.find({detail.color: {$ne: null})         // {$exists: true} 와 동일.
        
        > db.products.find({detail.color: null})               // {$exists: false} 와 동일.
        
    • Array element에 적용하는 operator 
      $elemMatch, $size
      $size는 정확한 숫자를 콕 집어 사용해야 한다. range를 허용하지 않는다. 
      범위를 반드시 써야 하는 경우라면, address_length를 따로 정의해서, elem 추가/삭제 시, ++/– 연산하고, range로 비교해라. 
      이걸 ensureIndex() 정의하면, 범위에 해당하는 값을 한 방에 찾아갈 수 있다. 
      > db.products.find({addresses: {$elemMatch: {name: 'home', state: 'NY'}}})   // "address" array에 name필드는 home이고, state필드는 NY인
                                                                                   //  sub-doc을 element로 갖고 있다면, 그 doc 전체를 보여줘.
      > db.products.find({addresses: {$size: 3}})               // "address" array에 element가 3개 들어있는 것.
      
    • JavaScript expression을 query로 넘기는 operator 
      $where : $where 뒤에 JavaScript expression이 와야 한다. 
      단점은 Index를 못 쓴다는 거. 
      > db.reviews.find({$where: "function() { return this.votes > 3; }"})  // this는 현재 doc을 가리킨다. 
      
      > db.reviews.find({$where: "this.votes > 3"}})                        
      
    • Regular Expression 
      $regex, $options 
      > db.reviews.find({user_id: ObjectId("..."), text: /best|worst/i })           // i 는 case-insensitive
      
      > db.reviews.find({user_id: ObjectId("..."), text: {$regex: "best|worst", $options: "i" })  
      
    • 기타
      $mod : 연산, array 모양안에 집어 넣는다는 게, 특이함. 
      $type : 
      > db.reviews.find({subtotal: {$mod: [3, 0]}})      //  ... % 3 == 0  3으로 modulo해서, even 인 것 찾기
      
      > db.reviews.find({id: {$type: 2}})                // BSON type을 확인할 때. 별로 쓸 일이 없을 듯.
      
  • Query options
    • projections : 결과로 넘어온 document 필드 전체가 아닌, 일부 필드만 넘기는 것. 
      inclusion
      exclusion
      $slice : range
      > db.users.find({}, {username: 1})      //  1 == inclusion, _id 필드와 username 필드만 넘긴다. 
      
      > db.users.find({}, {address: 0, payment_method: 0})      // 0 == exclusion, 언급된 2 필드만 빼고, _id 포함 넘겨라. 
      
      > db.users.find({}, {reviews: {$slice: 12}})              // 앞에서 12개 
      
      > db.users.find({}, {reviews: {$slice: -5}})              // 뒤에서 5개 
      
      > db.users.find({}, {reviews: {$slice: [12, 10]}})        // 앞에서, 12개 skip 후, limit 10개 가져와라.
      
      > db.users.find({}, {reviews: {$slice: [12, 10]}, 'reviews.rating': 1 })   // 위의 2 가지 이상 조합도 가능함. 
      
    • sorting
      > db.users.find({}).sort({rating: 1})      //  1 == ascending, -1 == descending
      
    • skip and limit
      > db.users.find({}).skip(50000).limit(10).sort({date: -1})    
      

Aggregating Orders

  • Grouping reviews by user
    특정 필드(user)를 기준으로, Aggregating 하는 예를 설명.
    group() 함수 사용 예를 설명.
  • Map-reduce for orders by region
    mapReduce() 함수 설명, group() 보다 강화된 기능이 많다. 
    결과를 collection으로 넣는 기능
    map 함수로는 key 기준에 해당하는 것만 찝어온다. shuffle 단계를 거치고, reduce의 입력으로 집어 넣고, 
    reduce 함수로는 single element가 되게 만듦. 
    비교적 느림. real-time에 적용 불가능함. MapReduce를 background로 돌리고 난 뒤, real-time으로 결과 주는 방법도 가능함.

Aggregations in detail

  • 최대값, 최소값 뽑기
    따로 제공하는 것 없다. sort() 해서, limit(1) 해라.
  • Distinct 
    distinct : 특정 key에 대하여, duplicated value를 제거한 value의 list를 준다.
  • Group
    group : 10,000 unique key 이상 다룰 수 없다. 장점은 map-reduce보다 빠르다는 거.
    • group, distinct 는 결과가 16 MB 이하로 제한되므로, 더 큰 Aggregation이 필요하면, map-reduce를 써야 한다. map-reduce는 결과를 제 3의 collection에 저장할 수 있으므로.
    • key 
      특정 필드를 찝어오는 기준을 정의할 때. 필수 argument임. ( 대신, $keyf 를 쓰던지..)
    • $keyf : JavaScript function
    • initial : reduce 함수 첫 구동 시, 초기 value로 사용되며, 두번째 argument(=aggregator)에 들어감.
    • reduce : JavaScript function, argument 2개 필요함. 각각 current document, aggregator라고 부름.
      return 이 없을 수도 있음.
    • cond : query selector
    • finalize : JavaScript function, reduce 결과, 후처리용.
  • Map-reduce 
    Aggregation 기능으로 group()이 MongoDB에 먼저 정의되어있고, map-reduce가 나중에 추가로 정의되었다. 기능이 일부 겹치긴 하지만, shard 구성에서, 큰 data들을 반복해서, 돌리는 건, map-reduce로만 해야 한다. “distributed aggregator” 
    Google에서 정의한 MapReduce 설명 슬라이드
    • map
      emit를 반드시 호출해야 한다. emit() 의 두번째 argument가 reduce에게 넘겨줄 중간단계 output임.
    • reduce
      key와 value list를 받고, list를 iterate하면서, aggregate하는 동작이 정의됨. key 자체는 aggregation에 사용되지 않고, 대상을 찝어오는 역할만 한다.
    • query
    • sort
    • limit
    • out
      결과 output이 16 MB 안으로 들어올 때, 의미가 있다.
    • finalize
  • 대용량 data를 다룰때, MongoDB가 가지는 한계점이 JavaScript engine이라고 말하기도 함. : single threaded에 interpret 방식.
    대안으로, 여러곳에서 돌리는 방향으로~ Hadoop~ 같이.
  • 향후, 개선 방향으로, multi-threaded에 complie 방식이 될 수도. MongoDB 2.0 이후 버전을 기대해 보길.
    • scope
    • verbose

6장. Updates, atomic operations and deletes

Updates

  • replacement 와 targeted modification 의 비교.
    replacement targeted modification
    doc을 퉁으로 읽어와서 client가 바꾸고자 하는 필드만, 바꿔서, 퉁으로 Server로 다시 올림. operator로 한방에 필요한 필드만…
    generic approach performance는 훨씬 낫다.
    내가 읽어온 동안 누가 바꿔 놨을 수도 있다. atomic을 보장한다.
    incrementing counter라면, 문제는 심각하다.  
    optimistic locking (= optimistic concurrency control) 은 lock을 안 걸면서, clean update를 보장하는 기술. 예) wiki 같은 데서, 많이 씀. update에 대해, Timestamp를 걸어서, 최근 update보다 오랜된 거면, 거부. findAndModify()
  • update 하러 갔다가 없어서, insert를 그냥 하는 경우를 upsert 라고 부르기도 함.
  • Array update operators
    • Append values
      $push, $pushAll
    • Append values + duplication 방지 기능 포함
      $addToSet, $each
    • remove an item
      $pop : 끝 item을 하나 뺌. $pop을 “-1” 값과 같이 쓰면, 첫 item 하나를 뺌. stack 처럼 동작하지 않음에 유의할 것.
    • 특정 값을 remove
      $pull : $pop을 좀 더 발전시킨 것. 위치로 값을 삭제하는 게 아니라, 삭제 대상 *값*으로 item을 찾아 삭제함. 
      $pullAll : 값 N개에 $pull이 적용됨.
    • Positional updates
      • sub-document가 array 안으로 들어갔을 때, sub-document의 일부 필드를 update 하는 경우. 
        특정 index를 지정하지 않고, query selector로 특정 element를 찾아, sub-doc의 일부 필드를 update할 수 있음.

findAndModify command

  • query : required (M)
  • update : required (C), update, remove 둘 중 하나는 반드시 있어줘야 함.
  • remove : required (C)
    true 면 삭제. default 는 false.
  • new 
    true 면, modify 후에, 적용된 값을 return. default는 false.
  • sort
  • fields
    특정 필드 일부만 건져, 결과로 return 받고 싶을 때.
  • upsert
    true 면, upsert 허용.

Delete command

  • remove()

Concurrency, atomicity, isolation

  • locking strategy는 coarse한 편, single global r/w lock 이 전체를 좌지우지하는 상황임. (crystal: single thread라서, single이 아닐런지…)
  • 하나를 어떻게 잘 나눠쓸까? 대안으로, 오래 잡을 놈들은 자기가 알아서 주기적으로 내 놓음. 이걸 꼭 피하고 싶으면, 
    $atomic 을 써라. {$atomic: true} 이면, 양보없이 operation 끝날 때까지 혼자 잡음.

Update performance

disk 상의 document에 영향을 주는 update는 아래 3가지 유형으로 구분한다.

  • BSON size 불변, 값만 바뀌는 경우. (가장 빠름)
    $inc 가 대표적인 예.
  • struct size가 변하는 경우. 
    BSON에서 첫 4 byte는 doc size를 가리킴.
    전체 doc을 disk상에서 rewrite 해야 함. (crystal: 그래서 왠만한 필드는 미리 정의하라고 권장하는 걸로..)
    RAM 상에서만 일어나면, 별로 심각하지 않지만, 4M 가 넘어버리면, …
  • doc이 space가 모자라서, 전체가 space가 널널한 곳으로 이동해야 하는 상황. 즉, doc 하나가 아니라, doc이 떼로 따라 움직여야 하는 상황.
    이런 경우를 대비해서, MongoDB는 collection 별로 padding factor를 정의해서, 영향을 상쇄하는 방향으로 하고 있긴 함.

Part 3 MongoDB Mastery

7장. Indexes and query optimization

Index concept

  • single-key index : _id 는 default, single key index임.
  • compound-key index :

Index efficiency

  • RAM 4KB page chunk를 넘어버리면, swap에 영향을 받고, 더 심하면, 급기야 RAM 보다 data가 커지면, thrashing. 
    thrashing : read/write 때마다 data가 paging 땜에, disk로 들락날락하는 현상.
  • 안쓰거나, 불필요한 index를 정의하면 안된다. 즉, 가능한한 작게 유지해야한다. stats() 로 크기 확인.
    index 와 working set은 RAM안에 대충 들어가면 바람직. working set = 전체 중, 자주 r/w가 일어나는 일부를 가리킴.

B-tree 구성

  • node 하나에 key 종류에 상관 없이, key를 2개씩 갖는다. (나머지 preallocated 영역 8k 따라 붙고. )
    key 하나 안에, 2개의 pointer가 존재하고, 하나는 자기 node가 가리키는 값. 다른 하나는 다른 child node를 가리키는데, child node의 자격은 자기보다 key 값이 작아야 한다. 
     
                              10, 20, "Empty space" 
                             /    |   \
                      5, 6, "E"   |   23, 28, "E" 
                                  |
                                15, 17,"E" 
    

Index types

  • unique
    unique한 값을 넣지 않아 발생하는 에러 즉, exception을 client driver에서 잡으려면, safe mode로 설정해야만 잡힌다. 
    data를 insert 하기전에, ensureIndex()를 해주는 게 바람직~ 
    duplicated key가 들어있는 체로, ensureIndex() 시도하면, Index 생성되지 않음. 
    이런 상황에 맞닥드리면, 손으로 duplicated key를 remove() 해줘야.
    data가 그닥 중요하지 않으면, dropDups 옵션으로 자동으로 제거하는 방법도 있음. 
    > db.users.ensureIndex({username: 1}, {unique: true})
    
    > db.users.ensureIndex({username: 1}, {unique: true, dropDups: true})    // 자동 제거
    
  • sparse
    군데군데 이빨 빠진 값도 index로 정의할 수 있음. 일부 doc에서 field가 존재하지 않아도 됨.
  • multi-key

Index administration

  • system.indexes 
    ensureIndex() 하게 되면, system.indexes collection에 추가됨. 
    수작업으로 직접, system.indexes에 추가할 수도 있음.
  • deleteIndexs() : ensureIndex()와 반대
    dropIndex
  • getIndexesSpecs() : 조회할 때.
  • Index 생성
    data를 migration하는 것 만큼 신중하게 해라. App에서 Index를 자동으로 생성하게 하면 절대로 안된다. (손으로만 하라는 말인 듯.)
    • sorting을 먼저 하고, 
      B-tree를 balanced로 만드는 효과 ??
    • sorting된 값을 Index로 넣어라. 
      > db.currentOp()    // 현재 진행중인 Index build progress를 볼 수 있음. 
      
    • Background Indexing
      background 에서, lock을 잡긴 하지만, foreground에 양보해가면서~.
      > db.xxx.ensureIndex({open: 1, close: 1}, {background: true})    // 현재 진행중인 Index build progress를 볼 수 있음. 
      
    • Offline Indexing
      엄청 커서, 몇 시간안에 끝이 안난다고 판단될 때. replica node 중 하나를 offline으로 만들고, index 생성하고, online으로 올려서, master replica를 catch up해서 따라가게 하는 방법.
    • Backups
      mongodump, mongorestore 명령어는 index declaration 과 collection만 넣었다 뺐다 하면서, index는 따로 생성한다. 
      data가 크면, 이 시간이 길어져서, 치명적일 수도 있다. 
      굳이 Index를 같이 backup하고 싶으면, option으로 선택.
    • Compaction
      왕창 지우거나, 계속 data를 유지하다 보면, 조각조각 Index가 나눠지는 경우, 필요이상으로 RAM을 많이 허비하게 된다. 
      기존 Index를 버리고, 다시 생성하면, compact 해짐. 
      다시 build 하는 동안, write lock을 가지게 되므로, 주의할 것. offline에서 생성하는 것이 바람직. 
      compact 명령어도 사용 가능함. 
      > db.xxx.reIndex() 
      

Query Optimization

  • optimization 대상이 될 query를 찾자
    통상 query는 100 msec 내에 끝나줘야 한다. 이상이면, MongoDB log에 warning을 찍을 거임. (slow query warning)
    워낙 data가 커서, 100 msec 이상 걸리는 놈은 열외. 
    threshold value로 100 msec이 너무 작으면, 이 값을 수정할 수 있음. ( mongod “–slowms” option0
    • log 대신, query profiler를 이용하는 방법.
    level 2 profiler enable, all query에 대상
    level 1 100ms 이상 걸리는 query에 대상
    level 0 profiler disable
> use xxx

> db.setProfilingLevel(2) 

* profiler 의 결과는 capped collection ( system.profile ) 에 저장됨.

> db.system.profile.find({millis: {$gt: 150}})    // fetch " > 150 msec" 
  • slow query의 원인이 뭔지 찾자.
    Index가 부족하거나, 부적절하거나, 바람직한 query에서 벗어난 경우가 대부분.
  • Query Optimizer 라는 놈이 실제로 MongoDB에서는 돈다. 
    몇 가지 rule 을 정의해 놓고, 그 rule을 만족하면, 그 query pattern 를 optimal로 판단하고, 
    pattern과 비슷한 게 들어오면, 현재 상황 Index 에서, nscanned가 적은 쪽으로 동작한다. 
    동작 순서는 
    일단, 비숫한 유형의 첫 query가 들어오면, 각 Index 별로 한번씩 돌려보고, nscanned가 적은 Index 를 스스로 선택한다. (winner 선택)
  • hint() 를 쓰면, query optimizer에게 optimal이 아닌, 다른 index를 강제로 쓰게 하여, 
    뒤에 explain() 까지 붙여 주면, 강제로 쓴 Index에 대한 nscanned를 보여줌으로써, optimization 으로 얼마나 개선되었는지를 알 수 있다.
  • query pattern을 일정 기간 동안 cache 하기도 하고, expire 함.
    expire 조건 : 100 번 이상 write가 발생한 경우. Index가 추가/삭제된 경우. nscanned가 10 배 이상 차이날 정도로 optimal한 query가 새로 나타난 경우.

8장. Replication

Replication Overview

  • 방법 2 가지 : single primary 에만 write한다는 점은 공통. replication은 asynch 하다. 
    master-slave replication : 
    replica set : automated failover 기능을 추가로 제공하는 것을 제외하면, master-slave 와 거의 동일하다.
  • replication 이 critical 해지는 경우 즉, corruption의 예.
    network connection 비정상
    h/w, s/w upgrade 또는 reboot 
    power loss
    disk fail
  • 주의할 점. 
    write/read 비율이 50 % 가 넘을 경우, load balance 가 별로 의미가 없다.
    consistent 한 read가 필요한 경우, 부적절하다. (replication이 asynch 하므로, secondary에서 read 했을 때, latest update를 반영하지 않을 수 있다.)

Replica Set

  • 통상 node 3 개를 최소 구성으로 함. 
    그 중 2 개를 mongod instance 를 각각 띄우고, 각각이 replica set의 primary로 둔다. 각각 full copy로 data 를 갖는다. 
    나머지 하나는 arbiter, neutral observer로 두고, 판단하는 역할. arbiter 에도 mongod 를 띄운다. 
    data가 특별히 많지 않으면, 30 초 이내 replica set은 모두 산다.
> rs.initialize()                         // 자기 정보 초기화

> rs.add("host:port")                     // 다른 member 추가

> rs.add("host:port", {arbiterOnly: true}) // arbiter 추가   

> db.isMaster()                           // replica set 상태 조회

> rs.status()                             // 각 node 의 상태를 조회
  • 상태 정보
    상태 구분 의미
    RECOVERING  
    PRIMARY  
    SECONDARY  
    ARBITER  
  • 다른 node 의 shell 을 구동하고 싶다면,
$ mongo localhost:port

> show dbs  
...
  • 한쪽 (primary) 에서 insert 하고, 다른 쪽에서, retrieve 해서 정확히 넘어 왔는 지, 볼 수 있다.
  • 한쪽 (primary) 에서 shutdown하고, failover 되는지 확인도 가능함.
> db.shutdownServer()

> rs.status()

  • replica set 의 동작 메카니즘은 oplog , heartbit 에 의존함.
    • oplog : journeling 용도, default db인 local 내부에 oplog.rs, capped colleciton 임. 
      oplog entry 들은 BSON timestamp 가 자동으로 찍힘. 이를 근거로 latest 판단.
      oplog 자체는 replication에서 제외함.
    • heartbit : health monitor, failover triggering 용도
> db.getRelicationInfo()
  • oplog 는 capped collection 이라, 일단 생성된 후, size를 키우는 건 불가. 
    default size는 아래. 변경은 mongod –oplogSize optioon 이용
    32 bit 50 MB
    64 bit 1 GB, 5 % free space 유지
  • oplog가 꽉 차서, 아직 apply 전 data를 overwrite 하는 상황이 발생하면, secondary는 synch할 point를 잃어 버려서, replication halt 상황이 발생할 수도 있음.
  • 각 node 들끼리 서로 2 초 간격으로 ping. (default)
    > rs.status()   // 마지막 heartbit 시간과 상태값으로 healthy 확인. 1: 정상, 0: 무응답
    

master-slave

Drivers and replication

9장. Sharding

10장. Deployment and administration

Appendix

A. Installation

conf 파일 설정 참조할 것.

B. Schema Design Patterns

Patterns

  • embedded vs. reference
    순서가 따로 없으면, embedded 가 낫다. 
    시간순으로 display 해야 되면, 그냥 reference로 만들어라.
  • tree 구성하는 패턴 (materialized path) 
    path 필드를 모든 doc에 넣는다. 이 놈의 역할은 parent를 가리킴. root 는 path가 null임. 
    child는 그냥 array type으로 정의한다.
    이름이 path인 이유는 현재 node에 child가 매달리면, 매달린 child는 path 로 윗노드의 parent : 현재노드 를 연속으로 string 형태로 붙여서, 진짜 path 처럼 보이게 한다.
    path를 조회할 때는, Regular Expression으로 string match로 찾아 낸다. 특정 string을 찾으면, 그 아래 매달린 child는 다 나온다.
  • worker queues : collection 자체를 작업 queue로 정의함.
    queue entry에서 하나씩 꺼내, findAndModify()를 하는 상황이면, 
    모든 entry안에, state, timestamp 필드를 정의하고, 나머지 payload 필드를 정의함.
    처리가 끝난 것은 automatic deletion 기능을 지원함. findAndModify에서, {remove : true} option을 사용.
    • state : string이나 int로 정의하되, int가 메모리를 덜 먹는다. 
      processed, unprocessed 로 구분할 수 있는 값이면, 충분함.
    • timestamp : standard BSON date.
    • payload : plain text.
  • dynamic attributes
    doc 마다, 각기 다른 attribute들을 복수개를 찝어내서, sparse Index 로 정의한다. 
    즉, MongoDB에서는 Index로 설정되는 필드가, 모든 doc에 포함되지 않을 수도 있다. : separated index 라고 부름
    • 다른 접근 방법으로, 필드가 예측블가능한 경우, 필드명을 generic하게 잡아서, 몰아 넣는 방법도 있다. 이 경우엔 굳이 separate index를 쓸 필요도 없다.
      아래의 경우, n, v 를 ensureIndex() 하면 됨.
            { _id: ObjectId("..."); 
      
              attributes : [
                 {n:"color", v:"red"},
                 {n:"name", v:"apple"}, 
                 {n:"shape", v:"rectangle"}
              ]
            }
            
  • transactions
    ACID 제공 안한다. transaction이 필요하면, 다른 Database를 써라. MongoDB는 BEGIN, COMMIT, ROLLBACK 제공 안 한다. 
    단, 개별 document에 대한, atomic, durable update 기능은 제공한다.
    • Atomicity : all or nothing. 중간까지만 실행하는 경우는 없다.
    • Consistency :
    • Isolation : transaction 중간에 다른 transaction이 끼어들지 못한다. transaction 중간 data를 다른 transaction이 보는 일은 절대 없다.
    • Durability :

compensation driven : document에 state 필드를 넣고, 이 필드에 atomic update를 이용하여, transactional strategy를 구성함.
이 필드가 valid면, transaction이 정상 종료한 것으로 판단하고, 그게 아니면, 원본으로 되돌리기.

  • locality and precomputation
    locality : 통계의 경우, 월별로, Collection을 나누면, locality가 올라간다. 이번 달 통계면, collection 하나 안에서만 뒤지면, 끝.
    precomputation : 예) insert 예상 가능한 필드들을 월초에 미리 한꺼번에 초기값으로 insert 해둔다.

Anti-patterns

  • careless indexing : 주로 성능이 안 나올때, index를 잘못 쓴 건 아닌지 의심해라. 
    사용하지 않는 Index 혹은 비효율적으로 정의한 경우가 많더라.
    실제 돌아갈 Query를 분석 좀 하고, 만든 index가 적당하다. 7장 참조.
  • Motleys types : 하나의 collection안에서, key 이름이 같으면, type도 같게 유지해라. 
    같은 이름 field에 대해서, 어떤 document는 int, 어떤 document에서는 string 이러면, 정신 없다. Query도 혼란스럽다.
  • Bucket Collections
    성격이 다른 data를 같은 collection에 넣지마라. 예) User와 Product, …
    collection은 유지하는데, 자원을 많이 먹지 않기 때문에, 용도 별로 쪼개도 결코 무리가 없다.
  • Largely, deeply nested documents
    MongoDB에 대한 오해 2가지 
    – collection들간의 관계를 갖게 만드는 건 쓸데없는 짓이다. (X) 
    – document를 실제 생활의 “문서”와 착각. (X)
    document는 가볍게 만들어라. 100KB 이내로. payload 같이 raw binary data가 들어간다면, 100KB가 넘어가도 무방함. 
    document가 가벼워질 수록, update가 싸진다. 또, readability가 올라간다.
  • One collection per user
    user 1명당 collection 하나, 이런 식으로 만들지 마라. name space (index + collection)가 default로 24,000개 제공되는데, 이 한계를 초과하면, database를 따로 가져야 하는 불상사가 생길 수도 있고, collection과 index는 memory를 많이 먹는다. space를 낭비 하게 될 거임.
    dbname.collectionname 에 해당하는 namespace를 default로 가짐.
  • Unshardable collections
    shard key를 잘 정의해야 sharding이 잘 된다. 아무 생각 없이 sharding 의 잇점을 기대하지는 마라.

JavaScript에서 Java에는 없던, “이상한” 개념들이… 왜 필요할까?

http://mulriver.egloos.com/4666528

: JavaScript에서 Java에는 없던, “이상한” 개념들이… 왜 필요할까?를 아주 간단하게 설명해주는 문서입니다.

원문 저자는 “JavaScript: Good Part” (Oreilly) 의 저자인 듯 합니다.

– Closure와 같은 방식으로 해석하면, “Hoisting” 개념도 결국 OOP의 private member를 흉내내기 위한 개념으로 정리될 것 같네요.

(= function 내부 아무데나 널려 있을 수 있는 private member 변수 정도~)

전에, 원팀장이 알려준 JavaScript code가 들어있는 svn을 대충 봤을땐,

이걸… 그냥 Java로 하나,

JavaScript으로 하나, 무슨 차이가 있을까 싶었는데,

“C++” 컴파일러로 “C”스럽게 코딩하는 상황이랑 비슷한 것 같네요.

실행 시점에, 동적으로 Object가 자기 스스로 모양을 막 바꿔가면서, 만들어가는 상황이 필요한 경우라면,

굳이 JavaScript를 써야 할 이유가 있는 상황이 되는 것 같네요.

즉, Object가 미리 결정된 모양, 그대로 일관성있게 같은 instance를 튀겨서,

free할 때까지 미리 결정된 모양으로 살다가 죽으면,

굳이 JavaScript를 써야 할 이유가 없다는 …. ^^;

MongoDB Oreilly 책 읽기 시작~

http://greenfishblog.tistory.com/101  : 누군가 정리한 것.~

초보자를 위한 보안 Tip

기본 concept 정리

  • documents  : row에 해당하는 개념. MongoDB의 data handling 단위~ 고유의 key 값으로, “_id”를 갖는다. 소속된 collection 내에서 unique하다.
  • collection : table에 해당하는 개념, 단, schema가 없다.
  • database : collection을 모은 것, permission을 갖는다.  하나의 MongDB instance는 여러 개의 독립적인 database를 hosting할 수 있음.
  • JavaScript shell : MongoDB instance admin 용도, data manipuation 용도.

Documents

정의 : ordered set of key with value,
통상 언어별로, hash, map, dictionary라고 부르기도 함. (언어에 따라, 순서가 별로 의미가 없을 수도 있다.)

// 순서가 다르다는 건, relational DB로 따라지면, column이 바뀌는 결과가 되므로, 말 된다.

Key-Value set에서, Value가 다른 Document 통으로 잡힐 수도 있다.

Key는 String(UTF-8)이고, 중간에 ” 이 낄 수 없고, .  $ 는 특별한 용도로 의미가 있고, _로 시작하는 건, reserved이지만, strict하게 챙기진 않는다.  type-sensitive, case-sensitive.

Collections

schema 가 다르다고, collection을 구분하지는 않는다.

근데, collection이 왜 필요한가?

  • Collection이 여럿일 때,  구별은 Name으로. Name은 UTF-8.
    • “system.”으로 시작하는 이름은 reserved.
    • 단, “system.users” 는 가능함.
    • “system.namespaces”는 모든 collection에 대한 정보를 담고 있음.
  • Subcollections : . 을 붙여서 namespace를 쪼개는 것.
    • collection과 child 관계 아님. 그냥 organized하기 위한 것일 뿐.

Databases

이름 길이 : 64 byte 이하.
reserved database 명은 아래 3 가지
  • admin :  root database, user가 admin database에 소속되면, 모든 database에 대해, permission을 갖는다.  shut down이나, 모든 database 를 list하거나 이런 건, 이 permission을 갖는 애만.
  • local :  replication 불가. single server에 있어야 하는 것만 여기에 유지.
  • config : shard에 대한 정보를 두는 곳. shard setup을 잡거나, 변경하거나 할 때.
namespace는 실제로 100 byte정도의 길이를 가짐.
MongoDB shell : JavaScript 이고, 그 자체로서, stand-alone MongoDB client 역할
Collection 접근은 db.baz 와 같이 db 변수를 통해 이루어짐.  여기서 baz 는 현재 DB의 baz collection 임을 의미한다.
즉, db.”collecitonname”
  • db.blog.insert(xxx)     // object xxx를 선언한 후, ( javascript에서 object init하는 거랑 동일)  object  xxx 를 db에 넣음.
  • db.blog.find()                 // list all docs
  • db.blog.findOne()        // list one docs ( =  one row)
  • db.blog.update( { keyX, valueY},  xxx)        // xxx를 일단 바꾼 후, new value “xxx” 를  기준점 {keyX,  valueX} 에 반영함.
  • db.blog.delete( { keyX, valueY})      // {keyX,  valueX} 를 지움.
  • help                                     // shell의 help
  • db.help()                           // db의 help
  • db.foo.help()                   // collection의 help
  • db.foo.update                 //  update 함수의 prototype과 body 정의. 즉, source code를 보여줌. (in JavaScript)
  • db.version()                   //  MongoDB server의 version을 보여줌.  version 자체가 하나의 collection임. 함수처럼 보이지만, 그 자체로 table이다.
  • db.getCollection(“version”)  //  어느 collection인지 찾아줌. test.version 이라고 찍힘. “test”는 default database.
  • x.y                                                   // x[y] 와 동일하다.      .을 syntactic sugar라고 부른다.

Data Types

JavaScript shell에서는 오직 64 bit floating point number만 다룬다. shell상에서 그냥 integer랑 구분하는 syntax는 없다는 말.  32bit integer도 결국엔 64bit floating point로 바꿔서, 처리함.

key/value 에서, value로 사용가능한 것들

  • JavaScript function.  function() {  ….  }    // anonymous function
  • binary data도 value로 사용가능함. 임의의 길이의 “string”으로 간주함. 단, shell상에서 직접 manipulate 불가함.
  • array도 value로 가능함.   { “x” :  [1, 2, 3, …] }
  • 다른 document embedding { “x” :  { “y” :  “foo”  } }

Numbers

JavaScript 는 1 개의 number type을 가짐.

MongoDB는 3개의 number type을 가짐. (4 byte, 8 byte integer, 8 byte float)