MySQL – 1

이 포스트는 쓸모없이 내용이 깁니다. 틀린 내용이 있을 수도 있구요. 다음과 같은 내용을 정리했었습니다. 가공없이 출력합니다.

  • InnoDB Table and Index Structures
  • MySQL Consistent Nonlocking Reads
  • MySQL ACID
  • MySQL Isolation Level
  • MySQL InnoDB MVCC

MySQL Consistent Nonlocking Reads

consistent read는 트랜잭션에서 첫 select문을 수행 후 다른 트랜잭션에의해 데이터가 변경되었더라도 그것과 상관없이 그 시점의 snapshot을 읽는 것을 보장한다. 또한 InnoDB에서read-committed 또는 repeatable read에서 기본으로 동작하고 lock을 걸리지않고 데이터를 접근할 수 있다. 정리하면 다른 트랜잭션에서 변경사항이 발생하더라도 읽기작업에 영향을 주지 않는다.

             Session A              Session B

           SET autocommit=0;      SET autocommit=0;
time
|          SELECT * FROM t;
|          empty set
|                                 INSERT INTO t VALUES (1, 2);
|
v          SELECT * FROM t;
           empty set
                                  COMMIT;

           SELECT * FROM t;
           empty set

           COMMIT;

           SELECT * FROM t;
           ---------------------
           |    1    |    2    |
           ---------------------
           1 row in set

InnoDB Consistent Read에서 따온 그림이다.

References

* https://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_consistent_read
* https://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html

MySQL ACID

데이터베이스 트랜잭션의 무결성을 보장하기 위한 4가지 속성이며, 각 단어의 첫 머리를 따서 만든 용어다. 데이터베이스에서는 논리적인 명령의 단위로 묶은 것을 트랜잭션이라고 한다. 예제로 많이 나오는 이체를 예로 들면, 한 계좌에서 다른 계좌로 전달되는 것을 이체라고 하는데 이는 계좌에서 출금과 그 금액이 대상 계좌로 입금이되어야 이체라고 부를 수 있다. 이를 트랜잭션이라고 부른다. 하나씩 정리해보자.

Atomicity

번역하면 원자성이며, all or nothing으로 설명할 수 있다. 트랜잭션의 일부 중 하나가 수행하던 중 실패하면 이 트랜잭션은 실패다. 이 속성과 연관된 MySQL 기능으로autocommit setting, commit, rollback등이 있다.

Consistency

트랜잭션이 성공적으로 일어나면 일관성 있는 상태로 계속 유지되어야한다. 이 속성과 연관된 MySQL 기능으로 InnoDB doublewritebuffer, crash recovery등이 있다.

Isolation

트랜잭션을 수행할 때 다른 트랜잭션의 간섭 등의 영향을 받지 않아야한다. 이 속성과 연관된 MySQL 기능으로 autocommit setting, set isolation level statement등이 있다.

Durability

트랜잭션이 커밋이 완료된 후에도 정전, 에러, 크래쉬 등이 일어나도 그 상태는 계속 유지되어야한다. 커밋으로 파생된 결과는 영구적으로 기록되어야한다. 그러기위해서는 커밋이 완료되기 전에 로그를 남기는 절차가 필요하다. 정전 등의 문제가 생기더라도 후에 복구절차를 통해 durability를 유지할 수 있다. 이 속성과 연관된 MySQL 기능으로 InnoDB doublewritebuffer,innodb_flush_log_at_trx_commit option, sync_binlog option, innodb_file_per_table option등이 있다.

References

MySQL Isolation Level

Read Uncommitted

커밋되지 않은 다른 트랜잭션의 데이터를 조회할 수 있다. 이는 정합성에 많은 문제가 생길 수 있는 문제라고 보면 된다. (이 레벨은 내가 쓸 가능성이 매우없는 레벨이라고 본다.) 이렇게 트랜잭션이 완료되지 않은 데이터를 조회할 수 있는 것을 dirty read라고 한다.

Read Committed

이전의 read uncommitted와는 반대로 트랜잭션이 완료되지않은 변경된 데이터를 조회할 수 없다. 동일한 쿼리를 요청했을 때 결과가 달라질 수 있는 문제는 있다. 이 레벨에서는non-repeatable read 현상이 생길 수 있다. non-repeatable read는 한 트랜잭션에서 약간의 시간을 두고 동일한 쿼리를 이용해 조회한다고 할 때 그 사이 다른 트랜잭션에의해 데이터가 변경한 후 커밋하면서 변경된 데이터가 나오는 현상을 말한다.

Repeatable Read

MySQL에서 InnoDB 엔진이 기본으로 사용하는 레벨이며 SQL-92, SQL-99 표준과는 다르게 이 레벨에서 phantom read 현상이 발생하지 않는다. phantom read는 한 트랜잭션에서 약간의 시간을 두고 동일한 쿼리를 이용해 조회한다고 할 때 처음 쿼리의 레코드와 두번째 쿼리의 레코드에서 처음 결과에 존재하지 않는 레코드가 나타나는 현상을 말한다. (보통의 RDBMS에서는serializable 레벨에서 phantom read 현상이 사라진다.) http://en.wikipedia.org/wiki/Isolation_(database_systems)#Phantom_reads

Serializable

다른 레벨 중 처리 속도가 가장 느리고 엄격한 수준이며 select문과 같은 조회시에 read lock을 획득해야만 할 수 있고 다른 트랜잭션에서는 접근할 수 없다.

Isolation Levels vs Read Phenomena

Isolation level Dirty reads Non-repeatable reads Phantoms
Read Uncommitted may occur may occur may occur
Read Committed - may occur may occur
Repeatable Read - - may occur
Serializable - - -

MySQL에서 isolation level 변경 방법

MySQL 트랜잭션내에서 isolation level 변경

mysql> SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)

mysql> SET TRANSACTION ISOLATION LEVEL Read Committed;
Query OK, 0 rows affected (0.00 sec)

mysql> SET TRANSACTION ISOLATION LEVEL Read Uncommitted;
Query OK, 0 rows affected (0.00 sec)

mysql> SET TRANSACTION ISOLATION LEVEL Repeatable Read;
Query OK, 0 rows affected (0.00 sec)

GLOBAL 또는 SESSION내에서 isolation level 변경

mysql> SET GLOBAL tx_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation='REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation='SERIALIZABLE';
Query OK, 0 rows affected (0.00 sec)

설정파일을 통한 isolation level 변경

[mysqld]
transaction-isolation=read-committed

References

 

MySQL MVCC (Multi Version Concurrency Control)

DBMS에서 보통 락을 걸지 않고 동시에 많은 (읽기)요청을 효과적으로 처리하기위해서 많이 사용되는 방법이다.(Consistent Nonlocking Reads) 약자로 유추해볼 수 있는데 현재 레코드결과뿐만 아니라 지난 버전의 레코드도 가지고 있음을 의미한다. 이는 rollback 및 동시성 기능을 제공하기위해서이다. 시스템 테이블스페이스의 일부로 rollback segment라는 undo log를 구조화된 곳에 저장한다.

Internal

내부적으로 InnoDB는 다음 3가지 필드가 추가된다.

필드 크기 설명
DB_TRX_ID 6-byte 이전에 추가 또는 수정한 레코드의 트랜잭션 ID
DB_ROLL_PTR 7-byte roll pointer라고 부르고 rollback segment에 기록된 undo log pointer
DB_ROW_ID 6-byte 새로운 레코드가 추가될 때 일정하게 증가하는 row ID

Undo logs

rollback segment내에 있는 undo logs는 추가(insert), 수정(update)으로 나뉜다. insert undo logstrasaction rollback의 경우에서만 필요하다. 추가의 경우에는 변경이전의 데이터를 다른 트랜잭션에서 읽을 경우가 없기때문이다. update undo logstransaction rollbackconsistent reads에도 사용된다. 각 update undo log는 필요로하는 트랜잭션이 없을 때 폐기된다.

Guideline

consistent reads 이슈가 있는 트랜잭션을 포함한 모든 트랜잭션은 가급적이면 빠르게 커밋을 해야한다. 그렇게 운영하지 않으면 InnoDB는 이 undo logs를 폐기할 수 없어서tablespace가 비대해진다. 물리적인 rollback segment의 크기는 보통의 추가 또는 수정된 레코드보다는 적은 용량을 차지한다. 위의 정보를 통해 어느정도 rollback segment의 용량을 계산해볼 수 있다. InnoDB에서 MVCC를 사용하는 경우엔 delete SQL문으로 요청하더라도 물리적으로 바로 삭제하지 않는다. update undo log가 폐기될 때 비로소 연관된 레코드와 인덱스를 물리적으로 제거한다. (이 operationpurge라고 부른다.) purge 용어에서 보면 알 수 있듯이 garbage collection의 일종이고 분리된 스레드에의해 실행되는 것을 알 수 있다.

References

 

InnoDB Table and Index Structures

궁금한 점이 있어 InnoDB 테이블, 인덱스, 기타 연관된 메타데이터를 정리한다. 내용 정리는 MySQL Reference를 중심으로 한다.

Role of the .frm File for InnoDB Tables

MySQL에서는 테이블과 관련된 정보를 .frm 파일에 저장한다. InnoDB의 경우에는 .frm을 가지지만 다른 스토리지엔진과는 다르게 메타정보를 .frm이 아닌 system tablespace에 유지한다. (아직 이유까지는 모르겠음… 그리고 hexviewer로 보면 innodb도 컬럼정보는 보인다. 아직 불확실) 좀 더 자세하게 .frm 내부가 궁금하다면 frm internal file format 문서를 보세요.

Clustered and Secondary Indexes

cluster index에서 언급하듯이 cluster indexInnoDB에서 primary key를 의미합니다. InnoDB의 경우 데이터 저장의 중심이 요소로 이 cluster index라고 말할 수 있다. 추가 및 쿼리 등 database operation에 좋은 성능을 내기위해서는 primary key를 잘 지정해야한다. (그 외에도 다양한 포인트가 있겠지만)

  • 겹치지 않고 non-null 컬럼이 없다면 auto_increment한 컬럼을 하나 추가한다.
  • primary key를 지정하지 않고 첫번째 unique indexnon-null 컬럼이 있다면 이 컬럼이 cluster index가 된다.
  • primary key를 지정하지 않고 적절한 unique index가 없을 경우엔 내부적으로 숨겨진 clustered index를 생성하고 데이터가 추가될 때 단순 증가되는 row id가 들어간다. (6-byte)

참고로 MyISAM과는 다르게 InnoDBindex파일과 data파일이 분리되어있지 않다. InnoDBprimary key 조회는 매우 빠르다. primary key가 순차적으로 늘어난다면 추가(insert)는 빠른 성능을 내고 그렇지 않다면 더 느려지고 파편화(fragmentation)의 원인이 된다. 각각의 secondary index는 해당 인덱스와 연관되는 primary key columns정보를 가진다. 그렇기때문에 꽤 큰 primary key를 가진다면 데이터는 커질수록 secondary indexes는 매우 비대해질 수 있다.

Physical Structure of an InnoDB Index

InnoDB에서 인덱스는 B-Tree로 구성된다. 또한 해당 트리의 rootbranch에 기록되는 것이 아니고 마지막 leaf에 정보가 기록된다. 기본 index page 크기는 16kb이다. 또한index page에는 데이터를 기록하지 않고 데이터를 가르키는 주소만 기록한다.

여기까지 내용을 정리하면

  • GUID(UUID) 값은 clustered index로 사용되지 않도록 한다.
  • 대신 sequential한 값이며 unique한 컬럼을 두는 것이 좋다.
  • 가급적이면 primary key는 짧게한다.
  • primary key는 가급적 변경되지않는 값이면 좋다.
  • secondary index를 통한 비용은 secondary index b-tree 조회 비용 + primary index b-tree 조회 비용이다.

나머지는 차차 정리해봅시다.

Insert Buffering

Physical Row Structure

References

 

MySQL 소스 코드

  • mariadbbazaar를 이용해 버전관리한다.
$ brew install bazaar
$ bzr branch lp:maria
$ cd maria
  • git blame과 비슷한 기능을 하는 bzr 명령어는 bzr annotate이다. 예를 들면 bzr annotate btr0btr.h 이렇게 사용할 수 있다.
  • c는 물론이고 cpp도 잘 모르는데 소스를 볼려니까 이거 참 어렵구만.

소스 구성

Understanding the InnoDB Source Code Structure 문서를 보면 간단하게 설명해준다. 근데 내 머리 용량 초과인 듯 어떻게 문서랑 겹쳐서 좀 살펴봐야겠다. 그 외에도 percona 자료도 좋은 것 같다. 어쨌든 source code, internal, user manual, 책이랑 같이 좀 자세히 탐구해봐야겠다. 물론 0.01%라도 봤으면 좋겠다. 특히 row, trx, btr, page, log, buf, ha,data 등은 조금이라도 봐야지ㅠㅠ

지금 현재 보고있는 것은 btr, page인데, 정리하면 난 index쪽을 보고 있다는 말.

row – Row Abstraction, 19,768 lines

sub-modules: Updates, Undo, Undo Modify, Undo Insert, Select, Purge, Mysql Interface

The logic for the mysql row formatting and the innodb row formatting is quite lengthy. This module also seems to have a lot of the high-level business logic for Innodb.

trx – Transactions, 13,138 lines

sub-modules: Rollback, Rollback Segment, Undo, Log Records, Purge

As Innodb is a transactional storage engine there is a lot of logic to implement this

btr – Btree data structure, 12,228 lines

sub-modules: Btree Cursor, Btree Persistent Cursor, Btree Adaptive Search

Btree is the index of Innodb and is core functionality

pars – SQL parser, 11,691 lines

sub-modules: Symbol Table, Optimizer, Lexical Analyzer, Grammar

I am pretty sure you can ignore this directory if you are using innodb with mysql, as it is dead code in that case (please do correct me if I am wrong)

dict – Data Dictionary (meta-data), 10,446 lines

sub-modules: Boot, Creation, Load, Memory

Table names, column names, key names, etc. all in this code

handler – Mysql Storage Engine Interface, 8498 lines

This is the primary interface between Mysql and the innodb storage engine and the entry point for all mysql API calls.

log – Database Log, 8379 lines

sub-modules: Recovery

Database logging is core functionality

buf – Buffer Pool, 7784 lines

sub-modules: Buffer Flush Algorithm, Buffer Replacement Algorithm, Buffer Read Abstraction

os – Operating System Interface, 7659 lines

sub-modules: Files, Processes, Threads, Synchronization

This is the fun stuff, all the low level OS specific code

lock – Transaction Lock System, 6224 lines

sub-modules: Lock Queue Iterator

page – Index Page, 5675 lines

sub-modules: Page Cursor

srv – Main Server Driver, 5469 lines

sub-modules: Startup, Query Execution

Look here for configuration option handling coding and other startup issues

sync – Synchronization, 5361 lines

sub-modules: ReadWrite Lock, Wait Array

fil – Table Space Memory Cache, 5282 lines

rem – Records Abstraction, 4965 lines

sub-modules: Record Manager, Record Comparison Service

fsp – File Space Management, 4405 lines

ibuf – Insert Buffer, 4125 lines

ut – Utilities, 4113 lines

sub-modules: Vector, Random Numbers, Memory, List, Debug, Byte Manipulation, Work Queue

mem – Memory Management, 3598 lines

sub-modules: Memory Debug, Memory Pool

data – Data Element Abstraction, 2867 lines

sub-modules: Data Types

que – Query Graph, 2255 lines

mtr – Mini-transaction Buffer, 1967 lines

sub-modules: Mini-transaction Log

eval – SQL evaluator, 1603 lines

sub-modules: Stored Procedures

ha – Hash table, 1422 lines

mach – Machine Dependent Utilities, 1198 lines

fut – File Based Utilities, 951 lines

sub-modules: File Based List

read – Cursor Read, 788 lines

dyn – Dynamically Allocated Array, 560 lines

thr – Threads, 302 lines

sub-modules: Thread Local Storage

usr – Sessions, 163 lines

 

InnoDB

B+-Tree 중 의문 정리

MyISAMInnoDB에서는 아래와 같은 형태로 인덱스를 구성해서 씁니다. InnoDBleaf nodesdata를 함께 저장하지만 MyISAMdata의 위치를 기록하는 형태입니다. 이 내용과 관련된 의문이 있었는데 밑에서 언급하겠습니다. 아래 그림에서 최상단에 있는 노드는 root node라고 부르고 최하단에 있는 노드를 leaf node라고 부릅니다. 그 외에는internal node 또는 branch node라고 부릅니다. InnoDB에서 clustered indexsecondary index가 있는데, 여기서 clustered indexesleaf nodedata(필드값)를 포함하고 있고 row ID로 정렬되어 저장되어있습니다. secondary indexesleaf nodeprimary key가 포함되어있습니다. secondary index를 이용할 땐 lookup을 2번하게됩니다.

from http://ozark.hendrix.edu/~burch/cs/340/reading/btree/index.html

 

 

351225947357440

의문 1, range scan등에 유리하게 정렬되어있다고 한다. 정렬!? 어떻게!?

하나의 파일에 테이블 인덱스, 데이터까지 저장되어있다고 하는데 어떻게 관리되나 궁금하기도 하다. range scan를 수행할 때 최대한 sequential하게 디스크를 읽어들이는게 성능 상 잇점이 있다는 것은 모두가 아는 사실인데 이를 디스크에 어떻게 관리하는가이다.

데이터를 어떻게 저장할까!?

(물론 테이블별로 따로 저장할 때) MySQL instance가 InnoDB 데이터를 .ibd 파일에 저장한다고 할 때, 파일명은 <table name>.ibd가 됩니다. InnoDB에서는 기본적으로 데이터를 저장하는 단위가 있다. 이를 page라고 부르고 기본적으로 16kb 용량을 가집니다. 이 page는 또 쪼개지는데 innodb-page-structurepage structure 에대해 설명합니다.

innodb-page-structure 문서에 기술된 InnoDB에서는 여러 종류의 page가 있다. 그 중 아래는 index type의 page 구성이다.

21.2.1.1 Fil Header
21.2.1.2 Page Header
21.2.1.3 The Infimum and Supremum Records
21.2.1.4 User Records
21.2.1.5 Free Space
21.2.1.6 Page Directory
21.2.1.7 Fil Trailer

모든 page는 38-byte 크기의 FIL Header로 시작해서 8-byte 크기의 FIL Trailer로 끝난다. 마찬가지로 index type page도 마찬가지로 이 둘로 둘러쌓인다. 고정적인 120-byte 크기의 header 부분(FIL Header, Index Header, FSEG Header, System Records)과 User Records, Free Space, Page DirectoryFIL Trailer로 나뉜다. User Recordsoffset 120-byte 지점에서 오름차순으로 기록되고 Page Directoryoffset 16376(16384-8(FIL Trailer size)-2(directory slot size)) 지점부터 거꾸로 저장된다.page directory 영역엔 4~8개의 User Records를 묶은 개념이라고 보면 되고 그 중 마지막 User Recordoffset을 기록하여 저장하고 n_owned 정보에 소속된User Records의 숫자를 기록한다. (각 User Recordn_owned 정보를 기록하는데 InfimumSupremum을 제외하고 0 또는 4~8 값을 가진다. n_owned 값이 최소 4 또는 최대 8인 것은 소스코드(page0page.h)에 그렇게 적혀있다.) page directorylookup할 때 쓰겠죠.

그 외에 page 종류는 INDEX, UNDO_LOG, INODE, IBUF_FREE_LIST, TYPE_ALLOCATED, IBUF_BITMAP, TYPE_SYS, TYPE_TRX_SYS, TYPE_FSP_HDR, TYPE_XDES,TYPE_BLOB, TYPE_ZBLOB, TYPE_ZBLOB2 등이 있다. (mysql 소스 중 fil0fil.h 파일에 정의된 타입 기준으로 정리했다.)

아직 index type이 끝나지 않았다. (디렉토리 설명도 건너뛰고 헤더도 건너뛰고 ㅋㅋ) The Infimum and Supremum Records는 쉽게보면 User Records의 시작과 끝을 기록한 정보라고 본다. Infimum record는 해당 페이지에서 가장 작은 key를 가진 user recordnext offset값을 통해 가르키고 있고 supremem record는 마지막 user record가 가르키고 있는 레코드이고 더 이상 이 페이지에서 더 큰 key를 가진 user record는 없다는 의미다.

User Records엔 실제 데이터가 들어간다. 내 두뇌로 감당하기에는 겁나게 복잡하다. secondary indexes 또는 clustered indexes 모든 leaf, non-leaf 상관없이 최소 5 bytes 또는 그 이상의 header가 있다.

크기 설명
2 bytes next record offset (상대주소이기때문에 origin + next record offset 하면 실제 주소)
3 bits 3 bits record type: 000=conventional, 001=node pointer (inside B-tree), 010=infimum, 011=supremum, 1xx=reserved
13 bits 페이지별로 부여된 heap id
4 bits 이 레코드가 소유한 레코드 수 (위에서 언급한 n_owned)
4 bits info-bits로 현재 2가지만 있고, 0x10ULnon-leaf 노드 중 해당 레벨에서 가장 작은 key를 가진 첫 레코드인지 여부를 가르키고, 0x20UL은 지워진 레코드인지 여부를 가르킨다. 이 정보는 rem0rec.h 파일에 정의되어있다.
가변 nullable 필드 유무를 가르킵니다. 5개 필드가 있더라도 8 bits로 구성됩니다. 그런 필드가 없다면 생략해도 된다.
가변 가지고 있는 가변 길이 필드의 각각 length를 기록하며, 한 필드 당 1 byte 혹은 2 bytes 이다. 여기서 1 byte 로 기록 가능한 길이는 0~127 이고 그 이상일 때는 2 bytes 로 표현해서 길이를 기록해야한다. 그 값을 구분하는 방법은 첫 bit가 0이면 1 byte, 1이라면 2 bytes 이다. 가변 길이 필드의 각 길이를 0x4000와 AND 연산했을 때 0값이 아니라면 이건externalindex에 다 포함시킬 수 없어서 reference를 가르키게된다. BLOB과 같은 page type을 가진 넘을 연결한다. 그래서 2 bytes라고 해도 표현할 수 있는 범위는0~2^14-1개이다.

여기까지가 공통이고 leaf 여부에따라 항목이 조금 달라진다. non-leaf의 경우엔 child page의 가장 작은 key 값과 child pagepage number를 기록한다. leaf의 경우엔cluster key fields 값과 트랜잭션을 처리하기위한 추가 필드인 transaction id, roll pointer가 있고 그 외에 cluster key fields 이 외의 fields를 기록한다.

그렇다면 여기서 clustered index의 4 bytes 고정 크기를 가진 key type이라면

record 당 5 bytes (record header) + 4 bytes (key) + 4 bytes (page number) 가 된다.

non-leaf 노드에 (16384 - 8 (FIL Trailer) - 120 (Index page header)) bytes 여유 공간에서 대략 1200여개 정도의 record와 디렉토리가 포함될 수 있는 구조가 된다. (실제로는 여유를 두고 꽉 채우진 않는다.)

그럼 아까 가진 의문을 정리해보자. range scan등에 유리하게 정렬되어있다고 한다.는 정리하면 각 indexB-Tree 형태로 저장이 되고 실제 데이터는 leaf 노드에 정렬된 형태로 존재한다. 각 노드 묶음 단위로 정렬된 record가 저장되어있다. 이를 읽어들이는거지 뭐.. ㅎㅎ 무조건 100% disk에 순차적으로 정렬되어있는건 아니다. 정렬의 의미는 순차기록이 아니라linked list와 같은 형태로 각 record가 연결되어있다는 의미지(page도 마찬가지). index page안에서 record가 순차적으로 기록되지 않더라도 문제는 없다. 어짜피 page 단위로 캐쉬를 하고 그 말은 page 단위로 디스크에서 읽어들인다는 의미니까.

그럼 뭐가 좋다는건가! 싶겠지만 MyISAM 엔진과 다른 점을 보면 알 수 있다.

이건 이대로 정리하고 끝내자. 네버엔딩이다. ㅋㅋㅋ

의문 2, 최대 row 크기 64kb VS page size 16kb

Limits on Table Column Count and Row Size 이 문서를 보면 제한이 있다. InnoDB와 상관없이 MySQL의 모든 테이블은 최대 row 크기가 65,535 bytes 이다. 그렇지만 지금까지 배운건B-Tree leaf노드에 데이터가 저장되어있다고 하지않았나? 그럼 어떻게 담아낸다는거냐!(버럭) InnoDB는 담아내더라. 그 이전에 한가지 더 궁금한 것이 생겼다. 위 제약에서 row는 64kb 제약과 함께 InnoDB는 최대 컬럼 수가 문서에 1000개라고 적혀있다. 하지만 실제로는 1024 – 1 – 3(DB_ROW_ID, DB_TRX_ID, DB_ROLL_PTR) * 2 (더미 테이블 생성할 때 씀)가 실제 저장할 수 있는 최대 컬럼 수이다(rem0types.h 파일에 정의되어있다. 찾느라 힘들었…)

저 같이 시간 남는 사람은 1017개 컬럼을 생성해봤을테지만, 실제로 저장해보면 에러를 맞이한다. 문서에 나와있는 1000개 컬럼 제약 조건 밑에 적혀있는게 그 때문인데 이유는 page size의 반 정도 되는 크기를 가질 수 없다. 생성은 시켜놓고 에러를 뱉는 꼴이다. (이유 알아볼꺼다.) (approximately 8000 bytes) 라고 언급되어있는데 정확히 크기를 말한다면, 8126 bytes를 넘을 수 없다. 이 숫자가 어떻게 계산되는가 궁금하지 않은가!? 근데 힌트는 이미 위에서 줬다.

16kb = 16384

16384 - 8 (FIL Trailer) - 120 (Index Page Header) = 16256

16256 bytes 공간만 쓸 수 있다. `infimum``supremum` 두개의 디렉토리때문에 4 bytes 먹는다.

16256 - 4 = 16252 bytes 가 실제로 사용할 수 있는 영역이다. page 용량의 반만 쓸 수 있다고 한다. 2로 나눠보자.

두둥!!

16252 / 2 = 8126 bytes 위에서 제시한 크기가 나온다.

row format에따라 다르다. (Redundant: 8123, Compact: 8126, Compressed: 8126, Dynamic: 8126)

문서에서 VARBINARY, VARCHAR, BLOB, TEXT 타입은 제외시켰다. 왜 제외를 시켰을까 궁금하다.

위의 타입의 경우 768 bytes 크기를 초과했을 경우 external reference를 통해 다른 page를 가르키도록 내부적으로 관리한다. 다시 말하면 지금까지는 index type page 설명을 했지만 별도로 다른 page가 있음을 밝힌다. external reference는 총 20 bytes의 용량(space id (4 bytes), page id (4 bytes), offset (4 bytes),length (8 bytes))을 가진다.

btr0cur.h 파일에 정의되어있다.

#define BTR_EXTERN_SPACE_ID        0    /*!< space id where stored */
#define BTR_EXTERN_PAGE_NO        4    /*!< page no where stored */
#define BTR_EXTERN_OFFSET        8    /*!< offset of BLOB header on that page */
#define BTR_EXTERN_LEN            12    /*!< 8 bytes containing the length of the externally stored part of the BLOB.

BTR_EXTERN_LEN 의 상위 2 bits 예약되어있다.

이 의문을 정리해보자. 한 page에 들어있는 하나의 record8126 bytes가 한계지만, extern을 통해 768 bytes만 기록하고 external reference만 기록하여 외부BLOB page에 기록한다. 그렇다면 extern 여부는 어떻게 알 수 있을까? -> 위에서 컬럼의 길이를 표현하는 정보가 1 byte 혹은 2 bytes라고 했다. 2 bytes일 때 상위 2번째 bit가 설정되어있으면 extern이다.

column-count-limit 에서 제시한 제한을 다음과 같이 다르게 정리할 수 있다.

  • 1017 개 컬럼이 한계이다.
  • 8126 bytes 를 초과할 수 없다.
    • VARBINARY, VARCHAR, BLOB, TEXT768 bytes를 초과하면 788 bytes를 차지한다.
    • 1017 개 컬럼이 타입이나 길이에따라 8126 bytes 초과가 runtime에 결정된다.

압축에대한건 안 해봤다. 틀린 것 있다면 가르쳐주세요. MySQL을 잘 몰라요. OTL

 

Algospot

국내에 매우 좋은 사이트가 있어서 소개한다. 알고리즘/자료구조에관한 문제를 풀어보고 확인해볼 수 있는 사이트인데 시간 떼우기에 꽤 좋은 사이트인 듯 싶다. 나 같은 경우엔 머리가 회전이 안되서 그런지 한 문제 풀려면 시간이 꽤나 오래걸린다. 어려운 문제는 못 풀겠다. 특히 그래프쪽은 나중에 공부해보고 풀어야할 것 같다. OTL 좀 풀었는가 싶어서 올리면 “시간초과”, “RTE (nonzero return code)”가 마구마구 뜬다. OTL

http://algospot.com / 내 계정

Yobi!

내가 회사에서 10명짜리 쓰면 싼 설치형 git 도구를 도입했는데 나중에 10명보다 많은 사람이 쓰면 비용의 문제가 심하다. 그래서 덕홍이형이 있는 조직에서 비슷한 프로덕트를 만들기때문에 트위터로 이런 말을 남겼었다.

장난 삼아 굴러달라는 표현을 썼지만 “그냥 가져다 쓸래” 모드다. 아직 10명도 안되고 미래의 고민일테니 말이다.

근데 대명님이 페이스북에 요비에 커밋했다고 요비후드받고 인증샷을 올린거다. 나도 5달전에 이런 버그 하나를 고쳤었다. 실제로 1++ / 1– 짜리 코드다. https://github.com/nforge/yobi/pull/254 진짜 간단한 수정이다. 그래도 동네형 Insanehong 에게 나도 달라고 우겨서 받았다. (이거 뭐 공식적인 것 같지 않고 몰래 하나 빼준 것 같다. ㅋㅋㅋ)

아 근데, 2일 입고다녔다. 써보지도 않으면서 고쳤던 1줄짜리가 코드 고치고 입고다니는게 민망한거다. 그래서 오늘 생각난김에 설치를 해봤다. 최초로 레파지토리도 만들었다.

버그라고 말하긴 애매하지만 메세지 문구가 이상해서 한개 고쳤다. (앗싸.. 발견) 과연 머지해줄지? 그리고 영문일 때, 한글일 때 상관없이 timeago 값이 한글인거다. 그래서 이것도 고쳐서 풀리퀘스트 올릴꺼다. Play2를 써본적이 없어서 찍어 맞추기식으로 하고 있다. 일단 이슈부터 등록해야지. php만하다가 간만에 이런 코딩하니까 재밌네ㅋㅋ

오늘은 아침에 요비나 보면서 수정해야지. (안 받아주면 말고!!! ㅋㅋㅋ)

인증샷!

1454675_10151973643821870_383614582_n

공중파 방송에 등장한 나

짧지만 임팩트 있는 등장으로 사람들을 놀라게 합니다.

handling

MyPeople – NodeJS Client Library & Hubot-MyPeople

hubot의 막강한 기능을 쓰기위해서는 IRC 같은 장소가 필요한데, 주변에서 PC도 되는 장점으로 인해서 그룹 채팅을 마이피플을 이용하곤 한다. 근데 어제 우연하게 마이피플이 봇을 만들 수 있도록 기능을 알았다.

일단 오픈한지 얼마되지 않아서 그런걸까.? 불편한 점이 몇 가지 있다.

  • bot용 계정을 따로 만들어야 한다는건 조금 귀찮았다.
  • debug 메세지가 아쉽다. 에러코드로 검색해서 문제를 알아가는데 시간이 너무 오래걸린다.
  • 가끔 계정 연동 해제하고 다지 가입하는데 에러가 난다. 메일에서 온 URL로 접근해도 에러가 나서 계속 시도해보니, 계정해제하고 로그아웃하고 다시 로그인하고 재신청하니 된 것 같다.

문제

  1. 이유를 전혀 알 수 없었는데 UCloud VM에서 되지 않았다.
  2. 해외에서 접속시도하는 경우 에러 결과로는 전혀 문제를 유추할 수 없다. (서버가 해외에 있다.)
  3. 다음 마이피플 서버에서 콜백서버로 메시지를 보내주지 않는 문제. 적어도 봇 관리에서 접속이 안되는 이유를 한줄로라도 적어줘야할 것 같다. (또는 보내지 않는 이유)

2번은 개인정보 설정에서 해외 접속 허용을 해야한다.

3번은 방법이 없다. (나는 되는데 다른 사람이 되지 않았다. 그래서 안되는 사람 콜백서버를 내 서버로 지정하고 콜백서버로 proxy를 할 때는 정상적으로 됐다. 그 말은 보내기는 되는데 마이피플에서는 어떠한 이유료 보내기를 실패한 것으로 추정된다.)

그리고 다양한 기타 등등 휴봇 개발보다 등록하고 테스트하는 과정이 더 복잡했다. 그리고 2000건은 좀 적은 것 같다. 어쨌든 이렇게 말해도 적당히 만족한다.

Hubot은 github에서 공개한 프로젝트인데, 마이피플도 API를 공개했으니 마이피플에서도 할 수 있도록 연동했다. 실제로 내가 짠 adapter코드 양은 별로 안되는데 hubot 소스 보는 시간이 더 길었다.(이건 또 복잡해 보이는 커피스크립트로 구현되어 있어서 나도 그렇게 짰다.) 대부분의 코드는 구현도 안했다. (되게끔만… 굳이 다른 기능이 필요할까? ㅋ)

그 hubot을 아래 nodejs 샘플을 이용해서 처음엔 짰다.

https://github.com/daumdna/apis/tree/master/Samples/8.Mypeople/BotAPI/Node.js/mypeople/lib/bot.js

지금 짠 hubot은 위의 샘플이 아닌 새롭게 구현한 클라이언트로 재작성했다. (https://github.com/dgkim84/node-mypeople)

다시 재작성한 이유

  1. 첨부파일 보내기에서 fs.readFileSync 를 사용한다. 외부에서 다양한 방법으로 접근할 수 없다. (물론 재작성한 것도 Readable stream만 된다.)
  2. 다운로드도 마찬가지로 지정된 방법만 써야한다.
  3. npm package로 라이브러리를 제공하지 않는다.

그 외에는 사용하는데 문제는 없을 것 같습니다.

기타 아쉬운 점.

  1. JPG 이미지 첨부만 되는 것 같다.(?)
  2. 2000건 너무 적다.
  3. 디버깅 하기 힘들다. (메세지를 보내지 못하는 이유가 뭔지 모르겠다. 지금은 알지만 다른 사람도 과연 쉽게 알 수 있을까 궁금.)
  4. 꽤 반응이 느리다.
  5. 콜백서버로 접속이 되지 않으면 왜 안되는지는 알고 싶다.

마지막으로 2000건 제한 좀 풀어주시죠. (주세요)

최종적으로 이걸 작성한 이유는 MyPeople 용 Hubot을 확장하기 위함이다. https://github.com/dgkim84/hubot-mypeople 에서 소스는 볼 수 있습니다.

hubot 사용하는 방법을 소개하진 않으며, mypeople용 어댑터를 사용하는 방법은 다음과 같이 설정한 후에 가능하다.

package.json에 dependencies에 “hubot-mypeople” : “0.1.1” 을 추가한다. HUBOT_MYPEOPLE_KEY만 필수 항목이다. 이게 맞던가 기억이 가물가물 하지만. ㅋㅋ


$ PORT=3000 HUBOT_MYPEOPLE_KEY="xxx" HUBOT_MYPEOPLE_NICK="hubot" HUBOT_MYPEOPLE_CALLBACK="/mypeople/callback" bin/hubot -a mypeople

Start-Up

최근 소규모 스타트업으로 자리를 옮겼습니다.

이전 회사에 퇴사의사를 전달한 후 어쩌다 보니 시작된 면접이 3곳의 회사에 최소 2번의 면접을 거쳤지만, 원래 계획은 2~3개월 쉬고 알아 볼 생각이었는데 주변에서 모두 걱정되었는지 소개 및 추천을 통해 면접 기회를 얻었다.

소개를 통해서도 3번의 기회를 얻었지만, 혼자서도 여러 채널을 통해 여러 곳을 알아봤다. 그런데 딱히 가볼만한 곳은 없더라. 생각해보면 보통의 구인 사이트나 스타트업들만 따로 모집하는 구인 사이트를 이용해서는 결과적으로는 내가 추구하는 스타트업은 찾을 수 없었다.

* 생각해보면 구인 사이트를 통하기 전에 창업자 주변 사람을 찾게 되어있다. 어느 정도 이루고, 어느 정도 갖춰진 후 충원할 사람을 찾는다.
* 뭐 기타 등등.

나름 선택하게된 이유는 여럿 있지만 그걸 언급하기 전에 이 회사에 조인하게된 과정을 적어볼까 한다.

지금 다니는 회사는 가장 먼저 만나 본 회사다. 이전 회사도 다니고 있고 지쳐있기도 하고 이전 회사에서 뭐 해본 것도 없이 나가게되는게 찝찝해서 거절했었다. 그 후에 이전 회사를 퇴사의사를 전달하게 되고 면접을 하나 씩 봤다. 그 다음은 퇴사의사를 전달한 후 (다니고 있을 때) 면접인데 결국 거기는 떨어졌다. 떨어진 이유는 모르지만, 스스로가 거기에서 이루고자 하는 것과 하고 싶었던 것이 없었다. (단지, 배울 수 있는 기회와 환경이 무엇보다 좋았다는 점은 매우 큰 장점이므로 그 점이 끌렸다.) 자신감도 없고 이전 회사에 지쳐있어서 오히려 쉬고 싶다는 생각만 가득하니 면접이 제대로 될 수는 없었던 듯 싶다. (물론 실력도 없고 커리어 관리도 제대로 되어있지 않은 점도 있다. ㅋㅋ) 그 면접으로 인해 내가 원하는 바를 좀 더 찾으려고 노력한 계기가 됐고 명확해졌다.

그 다음 면접은 한달동안 3번의 면접과 메일을 주고 받았다. 가장 많은 고민을 한 업체고 가장 복잡한(?) 절차를 밟은 회사이기도 하다. (3곳 중 유일하게 코딩테스트도 하고 그랬다.) 미안함도 많은데, 이 분들 시간도 많이 빼앗고 2번의 식사까지 얻어먹었기에 더욱 그렇다. (다음에 만나게 되면… 커피라도…) 내가 철이 없다고 볼 수 도 있지만, 내가 두번째로 보는 low risk high return(난 투자자가 아니라서, 내 기준은 돈만 포함되는건 아님) 이 가능한 회사라고 보지만, 내가 첫번째로 보는 (내 기준에서) 두근거림이 없었다. (좀 더 도전적인걸 찾는 걸 수도…) 물론 이 회사에 합격을 한건지는 알 수 없지만, 중간에 아쉽지만 추후의 면접을 거절 의사를 밝혔다.

그 다음 면접은 예전에 거절했던 그 회사고 지금 다니는 회사다. 그 땐 창업자 중 CTO를 만났었는데 이번엔 CEO도 함께 만났다. 그 때 같이 일하는 동료도 두명 왔는데, 놀라운건 전체 풀타임 직원을 다 본 자리였다. (난 아주 적은 인원을 좋아한다. 풀타임은 아니지만 외부에서 도와주시는 분들을 포함하면 7명이다.) 점심때였는데 피자를 먹으면서 같이 얘기했었고, 커피도 마셨다. 근데 그 때까지 난 이분들에게 이력서를 준 적이 없다. 그 말은 내가 지금까지 뭘 했는지 이 사람들은 모른다는 점이다. 무엇을 보고 나와 면접을 봤을까.? (이것도 물어봤다. 적긴 그래서 생략. 내 이력서는 웹에서도 볼 수 있어서 따로 주소를 알려줬다.) 커피숍에선 오히려 우리가 무엇을 만들고 어떻게 해나갈 것인지 관련된 자료와 함께 프리젠테이션도 봐서 오히려 내가 면접관이 된건가 생각이 들 정도였다.

솔직히 이 회사로 마음을 정한 이유는 내가 기여할 수 있는 부분이 많을 것 같고, 얻을 수 있는 것이 많다고 생각해서다. 물론 런칭한 서비스가 있고 아직은 회원수가 내가 들어갈 때 20만명을 갓 넘겼다. (이제 점점 증가되고 있다.)

이 회사의 장점은

* 서로간에 매우 신뢰하고 있다는 점. (3시부터 5시에만 사무실에 있으면 되고 나머지는 어디서든 일해도 된다는 점인데 결국 이건 신뢰문제다.)
* 점심/저녁 식대 지원한다. (한도는 15,000원)
* 젊음. (창업자는 모두 나와 동갑. 다른 분들 중 두명빼면 다 20대)
* 컴퓨터/인테리어비 400만원 지원.
* 런칭한 서비스
* 해외진출 가능성
* 도전할 만한 많은 미션
* 풀스택을 경험해볼만한 곳
* 이사가는건 알았지만 어디로 가는지는 몰랐는데, 얼마전에 구경갔는데 가는 곳 사무실이 아주 마음에 든다. +_+ (다음 포스트는 다음달에 이사가는 사무실 자랑을…)

난 진짜 능력있는 소프트웨어 개발자가 되기보다는 널리 이롭게 쓰이는 소프트웨어나 서비스를 만드는 사람이고 싶다는데 있다. (이렇게 말하긴 하지만 기존 캐릭터가 어딜 가겠습니까.)

앞으로 매우 설레이고 기대된다.

창업지원센터내에서 내 자리. 한동안은 내 컴퓨터만 써야 한다.

922880_10151612863326870_692738846_n

HBase – Configuration Part 1

주말에 코드 보면서 기록했던건데 주중에는 할 시간이 없을 것 같아서 여기서 마무리, 나중에 다시 손 볼 예정입니다. 실제로 HBase가 궁금해서 본거지 업무에 쓰는 사람은 아닙니다. 관심은 항상 있어서 보고있습니다. 틀린 부분이 있다면 귀찮더라도 말씀해주시면 반영하겠습니다. 조금 날림이라도 이해해주세요. 아직도 보고 싶은 부분이 많이 남아있는데, Block Cache/Bloom Filter/Log Split (distributed)/Recovery/HFile/Perforamcen Tune(Read/Write) 등등. 차근차근 정리합니다.

코드보면서 설정 부분이 좀 보이길래 정리했습니다. 중요해서 기록한게 아니고 보여서 기록한겁니다. :-)

WAL

HLog

hbase.regionserver.hlog.keyclass

HLog Writer/Reader를 구현하지 않는 이상 알 필요없는 설정입니다. HLogKey 클래스를 상속 받아 구현된 클래스여야 오류가 나지 않습니다. 기본값은 HLogKey 입니다.

HDFS보다 안전한 스토리지가 있어서 거기에 보관하고 싶다면 아래 Writer/Reader를 상속하여 개발하면 됩니다. hbase.regionserver.hlog.keyclass 는 그 과정에서 필요하다 싶으면 만들면 됩니다.

hbase.regionserver.hlog.writer.impl

HLog.Writer 인터페이스를 상속받은 클래스를 지정하며, 기본적으로 SequenceFileLogWriter 클래스입니다. init/close/sync/append/getLength 메서드만 구현하면 됩니다. 이름에서 알 수 있듯이 SequenceFileLogWriter는 SequenceFile을 사용합니다. 굳이 재구현이 필요하진 않겠죠.

hbase.regionserver.hlog.reader.impl

HLog.Reader 인터페이스를 상속받은 클래스를 지정하며, 기본적으로 SequenceFileLogReader 클래스입니다. init/close/next/seek/getPosition/reset 메서드를 구현하면 됩니다.

hbase.regionserver.hlog.enabled

org.apache.hadoop.hbase.regionserver.wal.HLog 에서 사용하며, HLog 활성여부를 나타냅니다.

hbase.regionserver.hlog.replication

HLog를 복제수를 지정합니다. 기본값은 FileSystem.getDefaultReplication() 입니다.

hbase.regionserver.hlog.blocksize

hbase.regionserver.logroll.multiplier와 함께 계산할 때 쓰는 기준치이자, HLog 파일의 blocksize 입니다. 기본값은 FileSystem.getDefaultBlockSize() 입니다.

hbase.regionserver.logroll.multiplier

sync될 때 log 크기가 blocksize * multiplier 보다 큰 값인지 검사하고 넘었을 경우 HLog.requestLogRoll 메서드가 호출됩니다. 기본값은 0.95입니다. 내부적으로는 WALActionsListener 에게 이벤트를 전달합니다. 대상 리스너로는 LogRoller와 Replication 이 있습니다. HLog를 생성할 때 Listeners를 등록하며, 생성은 HRegionServer에서 합니다. (hbase.hregion.memstore.block.multiplier 와는 다른 항목입니다.)

관련 이슈

HLog 코드를 보다가 히스토리가 궁금하여 찾아보는데, 아래 이슈를 찾아봤습니다.

https://issues.apache.org/jira/browse/HBASE-2312

이미 수정된거고, 자주 나오는 케이스는 아니라고 하는데, (1) RS1이 HLog 롤링하려다가 (2) GC로 멈추고 (3) HMaster가 log splitting을 되고 (4) RS1이 다시 깨어나면서 수정사항은 추가될 때

그게 유실될 수 있다는 말인 듯. 그래서 Cloudera 블로그 포스트(HBase Log Splitting) 에서 언급했듯이 -splitting suffix가 붙여 rename되는게 이 때 추가된 것 같고, SequenceFile.createWriter에 createParent 라는 기능을 먼저 추가시킨 후 (HDFS-617, HADOOP-6840, HADOOP-6886), reflection을 이용한 createWriter를 생성하여 사용합니다. 이 부분은 뭔가 다시 쓸 필요가… TBD

https://issues.apache.org/jira/browse/HBASE-2467

아주 예전 이슈이고 관련 이슈는 HDFS-895가 있습니다. sync/flush/write 등을 할 때 synchronized하게 수행했는데 flush/sync 등에서 병렬 수행할 수 있는 부분을 분리하여 write 성능을 개선한 기능입니다.

https://issues.apache.org/jira/browse/HBASE-2283

WAL 관련 코드 중 WALEdit 클래스를 봤는데, HBASE-2283 이슈가 해결되기전 과거 버전에서는 WALEdit가 아닌 KeyValue 였네요. row level atomicity 트랜잭션의 경우, 하나의 트랜잭션이지만 KeyValue로 저장하다가 장애가 난 경우 복구를 못하는 경우가 생겨서 변경되었네요.

https://issues.apache.org/jira/browse/HBASE-4387

HLog Writer가 이미 close된 경우 DFSOutputStream 에러가 발생할 수 있다. updateLock을 걸지 않고 하기때문에 rolling 될 때 close되는 경우가 있습니다. 그래서 수정된 이슈입니다. 해결은 IOException이 발생하면 다시 하는거죠. 이때 코드는 재밌는건 실패여부랑 상관없이 updateLock을 걸고 성공여부를 검사함. (지금은 catch문 안에 존재)

https://issues.apache.org/jira/browse/HBASE-5623

HBASE-2312 이슈에서 writer를 close 후 writer = null 바꾸면서 생긴 일. 지금의 tempWriter를 써서 하는 코드가 된 이유.

HLogSplitter

hbase.hlog.splitter.impl

HLogSplitter를 별도로 재구현할 필요가 있을 때 사용합니다. HLogSplitter를 상속받고, 생성자로 (conf: Configuration, rootDir: Path, srcDir: Path, oldLogDir: Path, fs: FileSystem) 을 받을 수 있어야 합니다.

hbase.regionserver.hlog.splitlog.buffersize

HLogSplitter가 사용하는 최대 힙 크기로 기본값은 128MiB 입니다.

hbase.hlog.split.skip.errors

HLogSplitter가 동작하다가 장애가 날 경우 무시여부를 결정합니다. 예외적으로 EOFException의 경우는 RS가 장애일 가능성이 있어서 값과 상관없이 뒤에 로그는 무시되고(info 로그 “EOF from hlog <path> continuing”), parse 실패인 ParseException, ChecksumException 도 무시(warn 로그 “Parse exception <e> from hlog <path> continuing”)됩니다. 그 외 에러는 throw하고, skip 했을 경우엔 CorruptedLogFileException 을 발생시킵니다. CorruptedLogFileException의 경우는 corruptDir(hbase.regionserver.hlog.splitlog.corrupt.dir)로 이동, 처리를 제대로한 것은 oldLogDir(변경 불가)로 이동합니다. 아직은 split 쪽은 잘 몰라서, 자세히는 나중에 또 조사하려고 하고… TBD

사용하는 곳은

HMaster가 RS shutdown 감지할 때 처리하는 핸들러인 ServerShutdownHandler와 SplitLogWorker, HMaster 시작 후 MasterFileSystem.splitLogAfterStartup, HLog.main 등이 있다.

관련 이슈

https://issues.apache.org/jira/browse/HBASE-3323

hbase.regionserver.hlog.splitlog.buffersize 항목과 관련된 내용인 듯 보이며, HMaster에서 OOME (Out of memory exception)으로 인한 수정입니다.

https://issues.apache.org/jira/browse/HBASE-1364

구글 빅테이블에서는 분산되어 split되는 반면에 HBase는 과거에 그러지 못했고, 이를 구현한 이슈입니다. SplitLogManager/SplitLogWorker/ZKSplitLog 등이 이 때 생기고, SplitLogWorker의 경우는 HRegionServer에서 하나의 Thread로 동작하고 SplitLogWorker-[NAME] 이름으로 동작합니다. 이건 나중에 따로 보고 이번엔 넘어갑니다.

LogRoller

org.apache.hadoop.hbase.regionserver.LogRoller 에서 처리하며, HRegionServer에서 사용합니다.

주로 스레드의 상태는 TIMED_WAIT 형태로 동작합니다. (hbase.server.thread.wakefrequency, LogRoller에서는 10초가 기본값) 이 스레드가 제 역할을 수행하는 것은 주기적으로 실행되거나 지정한 blocksize의 비율보다 초과했을 때 수행됩니다. 후자는 위에서 먼저 언급했던 방법이며 내부 구현은 HLog에서 비동기적으로 LogRoller에 요청합니다.

LogRoller는 FlushRequester 로 등록된 MemStoreFlusher에게 requestFlush 메서드를 호출합니다.

hbase.regionserver.logroll.period

어떤 주기로 로그를 rolling할지 ms 단위로 기록합니다. 기본값은 3600000 ms로 1시간입니다. 정확히 지정한 시간이 아니라 쓰레드가 깨어나는 주기인 hbase.server.thread.wakefrequency 로 결정됩니다. 마찬가지로 HLog에서 요청한 것도 이 주기에 맞게 깨어나고 나서 수행됩니다. (겹쳤을 땐 하나만 수행되면 됩니다. 그래서 HLog에서 요청한것이 수행되고 주기는 무시되고 다음 주기에 수행됩니다.)

MemStoreFlusher

제한된 메모리에서 Flush할 대상을 찾는 알고리즘(문제해결 방법)

upperLimit 또는 lowerLimit를 초과한 경우 getBiggestMemstoreRegion 메서드를 통해 가장 큰 멤스토어를 찾고 그 과정에서도 컴팩션을 줄이기위한 노력을 합니다. 간단하게 순서를 나열하면 (1) MemStore 크기 순으로 정렬 후 (2) StoreFile이 hbase.hstore.compactionThreshold 보다 큰 애들은 제외하고 가장 큰 MemStore를 찾고 (3) StoreFile 수와는 상관없이 가장 큰 MemStore를 찾고 (4) StoreFile이 적은 것에 2배의 가중치를 줘서 flush할 MemStore를 찾습니다.

여기서 참고할 만한 지표를 코드상으로 보니 updatesBlockedSecondsHighWater 라고 있습니다. 이는 upperLimit 초과해서 발생한 블럭킹한 시간을 누적한 카운터 메트릭입니다. 심하다면 조율이 필요할텐데 제가 써본적이 없으니 뭐라 말할게 없네요. (전 경험이 없어서 ㅋㅋ)

hbase.regionserver.global.memstore.upperLimit

기본값은 0.4이며, 값의 범위는 (0.1, 0.9) 이고 범위를 벗어난 경우 0.4로 설정됩니다. 한 RegionServer에서 사용할 수 있는 MemStore의 Heap 크기로 보면 된다. 이 수치를 초과했을 경우 블럭킹되고 MemStore는 강제로 flush 됩니다. put/delete 등과 같은 변경을 하는 커맨드에서 reclaimMemStoreMemory 메서드가 호출됩니다.

hbase.regionserver.global.memstore.lowerLimit

기본값은 0.35이며, 값의 범위는 (0.1, 0.9) 이고 범위를 벗어난 경우 0.35로 설정됩니다. 또한 upperLimit 보다 클 때는 upperLimit와 동일한 값으로 설정됩니다. upperLimit와 다른 점은 블럭킹이 되지 않습니다. lowerLimit을 초과한 경우는 비동기적으로 Thread를 깨웁니다. (음. notify/notifyAll 이나 signal/signalAll 등을 이용해 깨운다는 말은 아닙니다. 그렇게 오해는 마세요.)

hbase.regionserver.maxlogs

HLog클래스에서 쓰지만 영향을 미치는 클래스는 MemStoreFlusher로 판단됩니다. HLog.rollWriter -> HLog.cleanOldLogs 에서 반환하는데, 실제로 호출하는 애들은 많지만 쓰는 애는 MemStoreFlusher 하나 뿐인 것 같습니다.

TBD

hbase.hstore.compactionThreshold

TBD

hbase.hstore.blockingStoreFiles

기본값은 7이며, -1인 경우는 1 + hbase.hstore.compactionThreshold 수 입니다. TBD

hbase.hstore.blockingWaitTime

기본값은 90000. TBD