<< Previous | Home | Next >>

구글은 왜 구글 리더를 죽였는가?

Why is Google killing Google Reader?

구글 리더의 전 PM이 Quora에 올린 글을 통해 구글이 왜 구글리더를 죽였는지 내막을 잘 알수 있게 해 줍니다. 그래서 간략하게 번역해 보았습니다. 자세한 내용은 아래와 같습니다.

이 일(구글이 구글리더를 죽인)이 수익 대 운영비와는 어떤 관계도 없음을 분명히 해 두자. Reader는 단 한번도 직접적인 수익을 올린적이 없다.(Feedburner와 피드를 위한 AdSense 이용 등의 일부 있었지만) 그리고 이것이(수익) 제품의 목표는 아니었다.

Reader는 내가 담당 PM되기 훨씬 전부터, Google내에서 내부 인정과 생존을 위해 싸우고 있었다. 실제로 직원이 줄어들기 전까지 적어도 세번의 위기가 있었다. 대부분 Social과 관련된 것들이었다.
  • 2008년 - OpenSocial를 만들기 위해 팀에서 사람을 데려가려 한 것.
  • 2009년 - Buzz를 만들기 위해 팀에서 사람을 데려가려 한 것.
  • 2010년 - Google+를 만들기 위해 팀에서 사람을 데려가려 한 것.
2010년에, 대부분 엔지니어들은 G+팀에 가고 싶지 않았지만, 결국 중단하기로 마음을 먹었다. 아이러니하게도, Google이 Reader팀의 인력을 다른 소셜 제품을 만들게 하고 싶었던 이유는 Reader팀이 실제로 Social을 이해하고 있었기 때문이라고 생각한다. (게다가 수년에 걸쳐 많은 실험을 했고, 그래서 회사내 다른 더 큰 소셜 기능에 영향을 줬다.) [1]. Reader의 소셜 기능도 Top-down방식으로 설계되는 Google의 다른 프로젝트와는 달리, 사용자의 피드백에 대해 유기적으로 진화되었다. [2]

유지 보수가 더 몰린 뒤에도 한동안 생존 할 수 있었던 것은, G+내의 콘텐츠로 활용할 수 있는 소스가 될 수 있었다고 믿었기 때문이라고 조심스럽게 예상하고 있었다. Reader 사용자는 항상 열정적으로 콘텐츠 소비했고, 그들의 대부분은 콘텐츠를 필터링하고 공유했다.

하지만 UI 리뉴얼과 동시에 공유 기능을 G+로 전환하면서("share-pocalypse"로 불리우는) 개인적으로 사용성이 떨어질 것으로 예상했다. 특히 공유기능 주변에서. 나는 디자인 변경한 후 전혀 공유하지 않았다는 것을 알았다.[3] Google은 결국 많은 UI의 문제를 해결했지만, G+로 컨텐츠를 가게한다는 공유 방식은 다시 복원되지 않았다.

G+의 이용성도 떨어지고, 유지 보수 측면에서 이용자가 줄어들거나 혹은 안정될 수도 있는 상황에서 Google 지난 몇년의 리소스를 "집중"하는 움직임을 감안하면 제품을 죽이는 것 이외의 선택 여지가 있을 수 있었을까?

개인적으로, Reader와 같은 서비스는 아직 많은 가치를 창출할 수 있다고 생각한다. 다양한 소스에서 넘칠 정도의 정보를 제공하는 세계에서 특히. 하지만 Google에서 Reader는 분명히 RSS 리더로서의 ​​자리잡고, 그 이상의 존재로 성장할 기회가 없었다. 그렇다고 해도, 그것은 큰 문제가 아니다.

[1] Reader 친구 기능의 구현 v1, v2, v3, 코멘트, 프라이버시 컨트롤, 공유 기능들.
[2] Rob Fishman의 Buzzfeed 기사에 잘 요약되어 있다. : Google's Lost Social Network.
[3] Reader redesign: Terrible decision, or worst decision?

[참조 사이트]

비즈니스의 세계란?

  • "구체적인 사례가 있어?"라고 말하는 사람은 "다른 데는 어떻게 하고 있어?"라는 물음과 일맥상통해 혁신과는 거리가 멀어지게 된다. 먼저 실패하고 싶지 않은 마음을 우회적으로 빗대고 있다.
  • "컨셉은 뭐지?"라고 질문하는 사람은 컨셉이란게 사람 각자의 독특한 관점에서 나오는 형태인데, 모두 이해할 수 있는 개념의 논리적 잣대로 봐서는 안되는 범주 아닌가? 1%의 생각이 세상을 바뀌게 한다고 말하고 싶다.
  • "우리도 만들 수 있다." 그 수준에 안주해 버리면 그 뒤에 있을 사람들의 삶이 어떻게 바뀌는지 고민하지 못해 혁신을 넘어서는 부가가치를 만들 수 없다. 항상 새로운 기술에 도전하고 칭찬받으면 끝나는 것이 아니라, 그 이후의 비즈니스를 바라보고 그 비즈니스로 사람들의 삶에 어떤 영향을 줄 것인지를 고민하는 거 까지가 비즈니스다.
  • 모든 것이 스피드하게 변화하는 스타트업 비즈니스에서 과거의 성공한 경험은 전혀 도움이 되지 않는다는 사실 하나만 알고 있어도 큰 실패는 하지 않을 것이다.

영업에 실패한 후의 들었던 내 생각 모음. ㅋㅋㅋ
매출과 가입자로써 당신의 가치를 증명해봐라. 개뿔 성공하면 모든게 용서되는 세상. ^^

자존감과 믿음을 타파하자.

Eliminate ego and lore

훌륭한 인재가 되기 위한 좋은 글이 있어 메모해 둔다.

Great growth people do not follow or seek to enhance their own ego. They recognize that they can't rely on their ego and short feedback loops to find something that works - there are other insights that are needed to create a great product. Similarly, you need to constantly question lore that's built into an organization or coming from people within the company. The best growth people are able to collect data to derive insights to constantly question and invalidate this type of (often wrong) information.

"The hard truth - most people don't know what they're talking about" - Chamath
위 내용을 정리해보면..

위대한 사람(인재)은 자신의 자아를 강화(자존감)를 추구하거나 그것을 따르지 않는다.
그들은 자아와 단기 피드백에 의해서는 좋은 제품을 창조할 수 없다는 것을 잘 알고 있다. 다른 인사이트가 필요하다. 마찬가지로 조직내에서 구축된 믿음이나 사람들 사이에서 공유되고 있는 (제품에 대한) 믿음들에 대해서는 의심을 해야한다.
자료를 모으고, 거기에서 항상 질문을 통해 새로운 인사이트를 이끌어내고, 믿음과 자존심에서 오는 잘못된 정보를 항상 사실과 비교해 틀린 가설을 부정해 낼 수 있어야, 최고의 인재라고 말할 수 있다.

대부분의 사람들은 자신이 무슨말을 하는지 잘 알고 있자 못하다. 인정하기 어렵지만 사실이다. - Chamath

[참조 사이트]

이벤트 설정 시에 jQuery의 .on()을 사용하자.

jQuery 1.7에서 .bind(), .live(), .delegate()가 .on()으로 통합되었다길래 좀 살펴보았다.

개요

.bind(), .live(), .delegate(), .on()은 이벤트에 관련된 기능으로, 먼저 각각의 기능들을 살펴보는 것이 순서인것 같아 먼저 기능들을 살펴본다.

.bind()는 가장 많이 쓰는 이벤트 설정 함수이다. DOM이 로드된 후 추가된 element들에 대해서 event handler를 처리할 수 없고, .live(), .delegate()는 나중에 추가된 element들의 이벤트들을 처리할 수 있다.

foo에 이벤트를 설정하고 싶다면 아래와 같이 사용된다.
$(".foo").bind("click", handler);
$(".foo").on("click", handler);

.delegate()는 이벤트 설정을 부모에 대해 한다. 이런 방식을 이벤트 대리자라고 명명한다.

.foo에 이벤트를 설정하면 다음과 같이 사용된다.
$("#container").delegate(".foo", "click", handler);
$("#container").on("click", ".foo", handler);

.live()는 이벤트의 설정 대상이 document가 된다.

foo에 이벤트를 설정하고 싶다면 아래와 같이 사용된다.
$(".foo").live("click", handler);
$(document).on("click", ".foo", handler);

.on()을 사용해야 하는 이유

1. .on()으로 통합 될 예정

.live()와 .die()는 jQuery 1.9에서 빠졌고(1.7 Deprecations), .delegate()도 향후 없어질 예정이어서 .on을 사용하는 습관을 가지자.

2. .on()으로 여러 종류의 이벤트 설정이 가능

- 첫번째 인자에 공백로 구분자로 해 여러 이벤트에 대응이 가능하다.
$(".foo").on("change blur", handler);

- 각 이벤트에 여러 처리자를 둘 수 있다.
. 하나의 elements에 여러 이벤트 설정
$(".foo").on({ 
  "change" : function(){...},
  "blur" : function(){...} 
});

. 이벤트 대리자에서의 여러 이벤트 설정
$("#container") on ( { 
  "change" : function(){...},
  "blur" : function(){...} 
}, ".foo");
Tags :

SHA1 + salt로 패스워드 보안 이슈 회피가 가능한가?

최근 들어서 개인 정보 유출 등으로 인해 개인 정보 암호화 방법에 대해서 많이들 고민할 것입니다.
그 중에서 가장 중요하게 생각하는 것 중에 하나가 사용자의 패스워드인데, 과거에는 대부분 해시 MD5, SHA1 또는 SHA-256을 사용하고 있고, 좀 더 안다는 고급 개발자들이 있는 기업들만이 패스워드에 salt를 넣어 사용하고 있는 것으로 파악됩니다.(제가 잘못 알고 있을수도 있습니다.) 물론 최근들어 KISA의 제재로 salt + SHA1로 가는 추세이긴 합니다.
그러나 SHA1 + salt가 안전하다고 생각하는 분들은 아마 없을 듯 합니다. 그저 정부의 제재 수단을 회피하고자 적용하는 기업들이 많을 겁니다.

SHA1 + salt로 패스워드 보안 이슈 회피가 가능한가?

MD5, SHA1 또는 SHA-256 알고리즘은 보안이 우수하지 않다는 것은 다음 사이트에서 테스트해 보면 알 수 있습니다.
"http://google.com"을 MD5 인코딩한 값(c7b920f57e553df2bb68272f61570210)을 md5.rednoize.com 사이트에 넣고 검색 버튼을 클릭하면 바로 복호화되어 나옵니다.
md5.rednoize.com은 원본 데이터와 해시값의 데이터베이스를 참조해, 등록된 것은 즉시 해시값에서 데이터로 변환되어 출력하게 되는 구조입니다. 딕셔너리가 많이 확보되면 쉽게 패스워드를 풀 수 있게 된다는 소리도 됩니다.
이처럼 Rainbow Table("해시값이 이것이면, 암호는 이것"라는 테이블)을 사용해 일방향 암호화 기술을 무력화하게 됩니다. 이것만으로 보아도 일방향 암호화 알고리즘으로 MD5, SHA1 또는 SHA-256은 이제 한물간 일방향 암호화 알고리즘이라는 것을 쉽게 알 수 있죠.

그래서 보완책으로 나온 것이 SHA1 + salt입니다. salt를 붙이는 것으로, Rainbow Table 사용 접근 방식을 실질적으로 사용할 수 없게 하는 것으로 생각해 왔습니다.
SHA1 + salt의 처리방식은 아래와 같습니다.
$salt = "this is a salt";
$password = 'this is an password';
$hash = sha1($salt.$password);

자! 이 방법 또한 여기를 보듯이 GPU가 장착된 디바이스로 병렬처리하면 초당 수억건의 처리가 가능해 무력화 기술에 의해 무너질 수 있습니다. 이렇게 되면 딕셔너리 공격과 무력의 기술을 결합하여 많은 계정을 가진 대형 사이트에서도 상당한 양의 암호를 해독하는데, 그리 오랜 시간이 걸리지 않을 것입니다.

현재까지의 좀 더 강화된 방법으로는 뭐가 있을까요?

  • 각 사용자에게 고유 salt 값과 반복 횟수를 마련하는 것이고.
  • 좀 더 강력한 PBKDF2(http://en.wikipedia.org/wiki/PBKDF2), Bcrypt(http://www.openwall.com/crypt/), HMAC(http://en.wikipedia.org/wiki/HMAC)를 사용하는 것.
  • 불편하더라도 사용자에게 강한 강도의 패스워드를 받도록 유도하는 것.
정도가 될 것으로 보입니다.

PBKDF2 사용 샘플(Java)

public class PBKDF2 {

	//임의 salt를 생성
    private static byte[] createSalt() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[32];
        random.nextBytes(salt);
        return salt;
    }

    private static byte[] pbkdf2(char[] password, byte[] salt) 
     throws InvalidKeySpecException, NoSuchAlgorithmException {
        SecretKeyFactory sf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        // 반복 횟수 : 10000 번 결과 길이 : 256bit
        KeySpec ks = new PBEKeySpec(password, salt, 10000, 256);
        SecretKey sk = sf.generateSecret(ks);
        return sk.getEncoded();
    }

    private static void logging(String format, Object ... args) {
        System.out.printf(format + "%n", args);
    }

    public static void main(String[] args) 
     throws NoSuchAlgorithmException, InvalidKeySpecException {
        String p1 = args[0];
        String p2 = args[1];
        logging("password 1= %s", p1);
        logging("password 2= %s", p2);

        byte[] salt = createSalt();
        logging("salt: %s", Arrays.toString(salt));

        byte[] d1 = pbkdf2(p1.toCharArray(), salt);
        byte[] d2 = pbkdf2(p2.toCharArray(), salt);
        logging("derived 1= %s", Arrays.toString(d1));
        logging("derived 2= %s", Arrays.toString(d2));
    }
}
위의 10000이라는 숫자가 반복횟수이므로 이를 증가시켜 안전성을 높일 수 있습니다. 이 수를 사용자별로 데이터베이스에 저장하여 두면 중간에 반복 횟수를 높이고 안전성을 향상시킬 수도 있습니다.

[참조사이트]
Tags : , ,

MySQL에서 무중단으로 스키마 변경하기(Alter)

중단없이 온라인상에서 MySQL의 테이블 스키마 변경 작업에 대해서 알아본다.
무중단으로 스키마를 변경할 수 있는 방법으로는 현재, openark kit의 "oak-online-alter-table", Percona의 "pt-online-schema-change"와 Facebook의 "OSC(Online Schema Change for MySQL)"를 가장 많이 사용할 것 같다.

oak-online-alter-table과 pt-online-schema-change 차이는 oak-online-alter-table에는 지원안하는 포린키와 슬래이브 상태 고려 등을 빼고는 비슷하다. 그래서 좀 더 기능이 보완된 pt-online-schema-change 도구를 가지고 하는 방법을 설명해 본다. Facebook에서 사용하는 툴은 아직 분석하지 못했다. ^^

pt-online-schema-change는 Percona Toolkit의 여러 기능 중에 하나이고, Percona Toolkit은 Maatkit과 Aspersa를 기반으로해서 탄생되었다.
참고로 MySQL 5.6에서는 ALTER TABLE ... ADD INDEX / DROP INDEX 중 이어도 참조·갱신이 함께 실행이 될 수 있다고 하니 참고하면 될 듯 하다.
그리고 TokuDB를 사용할 경우에도 온라인상에서 스키마변경이 자유롭다.


Alter Table의 이슈

Alter Table 명령을 날리면 보통 아래와 같은 순서대로 진행된다.(innodb의 경우)
  1. 해당 테이블을 TL_WRITE_ALLOW_READ로 잠근다. 즉, READ는 허용하고 WRITE를 차단한다는 의미이다.
  2. 새로운 테이블의 빈 임시 테이블을 만들고.
  3. 이전 테이블에서 새 테이블에 데이터를 복사하며.
  4. 새 테이블을 이전 테이블과 같은 이름으로 바꾸고 이전 테이블은 삭제한다.
  5. 새로운 테이블에 차단했던 WRITE를 푼다.
이렇듯 Alter를 진행하게 되면 해당 테이블의 쓰기 잠금이 발생하여 일반적으로 온라인상에서는 테이블의 스키마 변경은 할 수 없는 구조이다. 스키마 변경을 할 시에는 데이터를 복사하게 되므로 해당 테이블의 사이즈만큼 스토리지 공간이 필요하게 된다.
Alter는 테이블의 행 수가 증가에 따라 선형적으로 시간이 길어지는 것은 아니다. 실제는 더 늦어질 수 있다는 의미이다. 왜냐하면 인덱스 재구성은 O (N log N)의 시간이 걸리기 때문이다. 그래서 테이블의 건수를 기준으로 절대적인 시간을 예측하기 보다는 실제 건수를 만들어서 테스트를 해보기를 권장한다. 거기에 Alter는 한번의 명령에 몰아서 처리해야 테이블 복사를 한번만 할 수 있다. 예를 들면 아래와 같다.
mysql> ALTER TABLE t1 DROP INDEX idx01, DROP INDEX idx02... ADD INDEX (idxN);


pt-online-schema-change의 방식은?

이 방식은 기존 MySQL의 Alter 방식의 프로세스를 수작업으로 자동화(스크립트화)시켰다고 보면 된다. Write 잠금 기능을 제거하고 그 기능을 trigger로 풀어서 원본 테이블의 R/W를 가능하게 했고 수행 순서를 보면 알겠지만, trigger를 통해 데이터 동기화를 이루었다. pt-online-schema-change의 방식의 기본적인 프로세스는 아래와 같다.

1. 새로운 임시 테이블 생성
기존 테이블에 앞에는 prefix 인자를 넣으면 prefix가 붙고 _에 원본 테이블명, 그리고 뒤에는 _new를 붙여 네이밍한 다음 임시 테이블을 만든다.
create_new_table()함수를 보면,
my $prefix = $args{prefix} || '_';
my $suffix = '_new';
my $table_name = $orig_tbl->{tbl}.$suffix;
$table_name = $prefix.$table_name;

2. 새로운 임시테이블 Alter
alter 인자로 주어진 ddl구문을 실행해서 임시 테이블에 칼럼 및 인덱스 등을 생성한다.
my $sql = "ALTER TABLE $new_tbl->{name} $alter";
print $sql, "\n" if $o->get('print');
PTDEBUG && _d($sql);
eval {
  $cxn->dbh()->do($sql);
};

3. 트리거 생성
원본 테이블의 변경사항을 임시 테이블에 반영해서 동기화하는 부분에 해당된다. 원래 테이블에 AFTER INSERT, AFTER UPDATE, AFTER DELETE 등 세 트리거를 만든다.
실행부분은 아래와 같고.
my @trigger_names = eval {
    create_triggers(
    orig_tbl => $orig_tbl,
    new_tbl => $new_tbl,
    columns => \@common_cols,
    Cxn => $cxn,
    Quoter => $q,
    OptionParser => $o,
  );
};

실제로 트리거문 구성을 살펴보면 아래와 같다. 참고로 MySQL의 트리거는 각각의 이벤트에 하나 밖에 만들 수 없기 때문에 이미 트리거 존재하는 테이블에 pt-online-schema-change을 사용할 수 없다.
my $insert_trigger
= "CREATE TRIGGER `${prefix}_ins` AFTER INSERT ON $orig_tbl->{name} "
. "FOR EACH ROW "
. "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";

my $update_trigger
= "CREATE TRIGGER `${prefix}_upd` AFTER UPDATE ON $orig_tbl->{name} "
. "FOR EACH ROW "
. "REPLACE INTO $new_tbl->{name} ($qcols) VALUES ($new_vals)";

my $delete_trigger
= "CREATE TRIGGER `${prefix}_del` AFTER DELETE ON $orig_tbl->{name} "
. "FOR EACH ROW "
. "DELETE IGNORE FROM $new_tbl->{name} "
. "WHERE $del_index_cols";

INSERT 이벤트에 대해서는 REPLACE문, UPDATE 이벤트는 REPLACE 문, DELETE 이벤트에 대해 DELETE 문을 발생시킨다. 그리고 DELETE 문은 임시 테이블에 레코드가 없을 경우 무시해 버리게 해 놨다.



4. 데이터 복사
INSERT LOW_PRIORITY IGNORE INTO ... 구문을 가지고 nibble_iter를 통해 원본 테이블의 데이터를 임시 테이블로 복사를 시작한다.
이렇게하면 응용 프로그램 측의 처리가 잠금 대기 시간에서 실패하는 것을 막으려하고 있다. 또한 시스템이 과부하되는 것을 방지하기 위해 1,000 레코드 처리할 때마다 대기 시간을 가질 수 있게 되어 있다.



5. 테이블 리네임(swap)
원본 테이블과 임시테이블을 교체한다. 기존 테이블은 _기존테이블_old로 바꾸고 신규 임시 테이블은 기존 테이블로 리네임한다.
eval {
  $old_tbl = swap_tables(
    orig_tbl => $orig_tbl,
    new_tbl => $new_tbl,
    suffix => '_old',
    Cxn => $cxn,
    Quoter => $q,
    OptionParser => $o,
  );
};
RENAME TABLE.. TO .. TO ...

6. foreign key 업데이트
존재할 경우 처리.

7. OLD 테이블 DROP


온라인상에서 스키마 변경 테스트

간단하게나마 pt-online-schema-change 살펴보았다. 다음으로는 실제 이 스크립트가 제대로 동작하는 지 살펴볼 차례다. 다음은 pt-online-schema-change을 사용하기 위한 방법을 기술한다.

#perl 모듈 설치(설치 안되었을 경우)
cpan> install DBI
cpan> install DBD::mysql
cpan> install Term::ReadKey
# 해당 스크립트 단운로드
> wget http://www.percona.com/redir/
 downloads/percona-toolkit/LATEST/percona-toolkit-2.1.7.tar.gz

# 테스트 테이블
CREATE TABLE t1 (
username char(25) not null primary key,
pass char(25),
uid integer not null,
gid integer not null
) Engine=InnoDB DEFAULT CHARSET=utf8";

아래는 super-smack을 통해 update 트랜젝션을 일으키면서 실제 온라인상에서 테이블 스키마 변경을 테스트 해 본다.
> /database/server/super-smack/bin/super-smack \
./update-select.smack 10 100 >> /samples/data/log5.txt

> /percona-toolkit-2.1.7/bin> ./pt-online-schema-change \
--execute --ask-pass --user=root --chunk-time=1 \
--nodrop-old-table --alter-foreign-keys-method auto \
--alter "add column t_date char(15) default null" D=test,t=t1,h=localhost

Enter MySQL password: 
No foreign keys reference `test`.`t1`; ignoring --alter-foreign-keys-method.
Altering `test`.`t1`...
Creating new table...
Created new table test._t1_new OK.
Altering new table...
Altered `test`.`_t1_new` OK.
Creating triggers...
Created triggers OK.
Copying approximately 90000 rows...
Copied rows OK.
Swapping tables...
Swapped original and new tables OK.
Dropping triggers...
Dropped triggers OK.
Successfully altered `test`.`t1`.

간단하게 옵션에 대한 설명을 곁들인다.
  • alter : 실행 alter 문.
  • h : 호스트 정보.
  • D : 데이터베이스 명.
  • t : 테이블 정보.
  • ask-pass : 암호를 표준 입력 가능함.
  • alter-foreign-keys-method : 변경 대상 테이블에 외래 키 설정이 되어있는 경우 처리 방법. auto, rebuild_constraints, drop_swap, none을 지정할 수 있음. 외부 참조의 데이터 건수가 많으면 drop_swap 됨.
  • nocheck-replication-filters : replication의 filter 설정.
  • execute : 이를 지정하지 않으면 실행되지 않음. 또한, dry-run에서 테스트 할 수 있음.

[참고 사이트]

트랜잭션이 중요한 비즈니스에서의 MySQL에 대한 고민들

MySQL에서 데이터 정합성이 중요한 비즈니스에서 데이터 불일치에 대한 고민을 적어 본다. 보통 MySQL은 Replication 방식으로 부하 분산을 대응하고, 여기에 성능의 이유로 sync-binlog=0으로, 비동기 방식(준동기보다)으로 운영을 많이 하는데, 여기서 문제가 발생한다. 장애시의 데이터 불일치, 평상시의 데이터 동기화(마스터에서 슬래이브까지) 지연 등의 현상이 발생하고 나중에 오래 운영하다보면 Master-Slave간의 데이터 불일치도 발생하게 된다.
이런 문제점을 성능을 조금 양보하면서 데이터 정합성을 찾을 수 있는 방법은 없을까?에 대한 생각을 적어 본다. 다른 좋은 의견이 있으시면 코멘트 부탁드립니다. ^^

관련 요소들 그리고 이슈

1. Replication 방식의 변천사 그리고 지연
MySQL의 Replication은 보통 비동기 방식으로 운영한다. 비동기 방식은 마스터 측은 슬레이브를 업데이트했는지 여부 및 업데이트 로그가 전송되었는지 여부는 신경쓰지 않는 방식이다. 만약 마스터의 장애나, 슬레이브 간의 네트워 장애가 발생해도 마스터 측과 슬레이브와의 데이터 차이는 커질 것이다.



구체적으로 지연은 "네트워크 패킷이 통신 시간 + 슬레이브에 업데이트 하는데 걸리는 시간 + α "의 지연 시간이 존재한다. 그 후 5.5 버전부터는 Semi-Synchronous 복제는 슬레이브 릴레이 로그에 업데이트가 기록된 것을 확인 한 후, 클라이언트에 응답을 반환하는 방식으로 트랜젝션 보완을 한 경우이다.



즉, 마스터 변경이 슬레이브 릴레이 로그에 기록되지 않는 한 클라이언트 에 응답을 반환하지 않는다. 마스터 업데이트 및 슬레이브 릴레이 로그 기록까지룰 "동기화" 범주로 보고 슬레이브 릴레이 로그에서 데이터베이스의 업데이트까지는 비동기이므로, "준 동기 (Semi-Synchronous)"라고 불린다.
여기서도 구체적으로 "슬레이브에 업데이트하는데 걸리는 시간 + α"시간이 지연된다고 볼 수 있다.

복제의 경우 지연, 그리고 마스터와 슬래이브 동기화 과정에서의 크래쉬 발생 등으로 데이터 불일치 요소들이 발생하게 된다.

2. sync_binlog
sync_binlog는 몇번의 트랜잭션마다 바이너리 로그를 디스크에 동기화를 설정하는 매개 변수이다. AUTO COMMIT 모드는 몇개의 명령문마다 동기화 할 것인지를 결정한다. 디스크에 동기화에는 fdatasync()를 사용한다. 디스크로의 동기화는 바이너리 로그의 안전성을 높이지만, 동기화 횟수가 많으면 성능이 떨어진다. 그러나 바이너리 로그의 손상, 특히 다수의 슬레이브를 사용하여 부하 분산을 수행하는 경우 아주 중요한 문제가 되기 때문에 최대한 피하고 싶을 것이다.
sync_binlog가 1로 설정되면 MySQL은 특별한 동작을한다. 트랜잭션이 InnoDB만 이루어진 경우, 2 단계 커밋을 사용하여 바이너리 로그가 InnoDB의 로그와 동기화하게되는 것이다.
이는 트랜잭션 파일 시스템(ext3, zfs 등)과 함게 사용하고 바이너리 로그를 데이터 디렉토리와는 다른 디스크에 저장하게 해서 안정성을 높일 수는 있다.

성능상 대부분 0으로 셋팅해서 운영한다. 그러면 sync_binlog = 0에서는 페일 오버시에 바이너리 로그가 손실되므로 복제시에 데이터 손실이 있을 수 있다.

3.innodb_flush_log_at_trx_commit
InnoDB 로그 파일에 대한 쓰기시의 움직임을 조정하는 매개 변수이다. 가능한 값은 0,1,2의 3 종류로, 각각 다음과 같은 의미가있다.
  • 0 : 로그 파일에 기록을 1초마다, 디스크의 플래시를 1초마다 실행한다. 커밋 때 아무것도하지 않는다.
  • 1 : 트랜잭션이 커밋 할 때마다 (커밋 완료하여 클라이언트에 응답을 반환하기 전에) 디스크에 기록하고 플래시를 실행한다. ACID의 D=Durability를 보장 할 수 있다.
  • 2 : 커밋 시 로그 파일에 기록하고, 디스크 플래시는 1 초에 한 번이다.
기본값은 1이며, 어지간한 일이 없는 한 기본적으로 사용되는 것을 권장한다. 또한 0보다 2 편이 낫다.

개선 방안은 어떤게 있는가?

MySQL의 Replication에서 슬레이브로 데이터 전송을 비동기적으로 수행함으로 인해 지연이 발생하고 네트워크나 마스터, 슬래이브 장애시 데이터 불일치가 존재한다.
sync_binlog = 0에서는 페일 오버시에 바이너리 로그가 손실되므로 복제시에 데이터 손실이 있을 수 있다. 즉, 트랜젝션이 중요한 비즈니스에서는 위험한 일이 아닐 수 없다.
그럼 대응책은 없을까?

1. Replication 지연 대응
  • 롱 트렌젝션을 제거하고
  • Slave 개수를 잘 조절하고
  • 부하량이 적은 환경에서 마스터와 슬레이브 간의 데이터 정합성을 강화시키는 Semi-syncronous 권장.
  • 작업 집합이 InnoDB 버퍼 풀보다 훨씬 큰 경우는 Slave pre-fetching 기술도 활용(SSD 포함). 자세한 설명은 아래에 기술한다.
*. Slave pre-fetching 기술
Slave pre-fetching 중 Replication Booster for MySQL가 가장 좋은 예여서 이부분에 대해서 기술해 본다.



MySQL에서 복제를 설정 한 경우, 마스터의 바이너리 로그를 IO 스레드가 읽어, relay-log에 기록하고 SQL 스레드가 relay-log를 읽어 테이블에 갱신을 하면서 동기화해 나간다. 이 때 SQL thread는 Random 디스크 I/O가 발생해서 지연이 생기게 된다. 그래서 랜덤 디스크 I/O를 발생시키지 않기 위해서 SQL Thread가 실행하기 전에 모든 변경 대상자를 캐시에 올린다면 SQL Thread는 랜덤 디스크 I/O가 발생하지 않는다. 그런 아이디어에 착안한 Replication Booster는 relay-log를 읽고, 업데이트 쿼리를 Select 쿼리로 재 작성해 SQL 스레드보다 먼저 테이블에 액세스한다. 따라서 InnoDB의 buffer_pool 등 메모리에, 앞으로 업데이트되는 데이터들을 실어 업데이트 쿼리 실행 시간을 줄이자는 것이다.

2. sync_binlog=1의 성능 개선
MySQL 5.6에서 sync_binlog=1의 성능이 크게 개선 된다고 한다. 기다려야 한다. ^^

3. Group commit for the binary log
MySQL은 5.6에서 지원 예정이고 MariaDB는 바이너리 로그에 대한 그룹 커밋을 지원한다. 이를 통해서 성능 개선 효과를 볼 수 있다. 그럼 그룹 커밋이 어떤 것일까?
그룹 커밋이 없는 경우, 아래 그림처럼 여러 클라이언트에서 커밋 요청은 순차적으로 처리 된다. DBMS의 특성인 ACID를 충족시키기 위해서 바이너리 로그에 기록할 수 있는 쓰레드는 동시에 하나다.



이렇게 되면 디스크에 동기쓰기를 해야돼 많은 성능 제약을 가져오게 된다. 이런 부분의 문제점을 개선해 주기 위해서 MariaDB 5.3에 이미 지원되고 있고, MySQL은 아직 정식 릴리즈 안된 5.6 버전에 안정화 버전이 포함되어 있는 것이 바로 Group commit이다.



Group commit은 말 그대로 클라이언트로부터 먼저 온 커밋 요청을 처리하는 동안, 다음 커밋 요청 받았을 경우, 디스크에 동기 쓰기를 한꺼번에 처리하는 것을 말한다. 이는 바이너리 로그에 기록 할 수있는 쓰레드가 동시에 하나 뿐이라는 점은 변함 없지만, 한 번의 fsync(2)로 여러 커밋 요청을 처리함으로써 초당 커밋 수를 크게 늘릴 수 있어 성능을 개선할 수 있다.
Group commit인 경우 대개 MariaDB에서는 트랜젝션이 중요한 비즈니스에서는 innodb_flush_log_at_trx_commit=1, sync_binlog=1 설정으로 운영하게 된다.

그리고 Group commit성능이 MariaDB가 가장 우수하다. 근거는 Facebook에서 비교해서 올린 자료를 보면 알 수 있다.



MariaDB의 활용성가 증가할 가능성 있는 한 요소다.

결론적으로 케이스별 테스트를 통해 최선의 선택을 해야할 듯 하다. MariaDB의 Group commit과 prefetching 기술을 적용하면 좋을 거 같다는 생각이 든다.
물론 benchmark 테스트와 데이터 정합성 테스트를 해 봐야 알겠지만.. ^^

[참조 사이트]