2014년 10월 30일 목요일

테스트 임베드

주말살이님의 여행 일정 2014-12-24 ~ 2014-12-24, 0박1일
부산 크리스마스 트리 문화축제!
부산에서 즐기는 색다른 크리스 마스!
아침에 거리를 나서면 입에서 입김이 폴폴 풍기는 계절, 드디어 겨울이 찾아왔다.
겨울하면 추워서 짜증도 아니만 그래도 내심 기대되는건 아마도 크리스마스 때문이 아닐까?
크리스마스를 제대로 즐기고 싶다면 아무래도 축제를 가는게 좋을 것이다.
부산에선 해마다 크리스마스 문화 축제가 열리고 있었다.
눈이 잘 오지 않는 남부지방에서 크리스마스 축제를 즐기라니... 좀 아리러니 하지만(?)
이번 부산 크리스마스 문화축제는 정말이지 다채로운 행사가 많이 마련되어 있다.
길거리 공연은 물론이고 보물찾기나 눈 보기가 드문 부산 시민들을 위해 인공눈을 뿌려주는 행사 등!
놓치기 아까운 이벤트들이 많다.
이러한 행사들은 주로 주말에 진행되니 주말을 부산여행과 함께 크리스마스 행사를 즐겨보는 것도 좋을듯!
[부산 크리스마스 트리 문화축제]
기간 : 2014.11.29(토)~2015.01.04(일)
장소 : 부산 중구 광복로 차없는거리 전역
주최 : 부산크리스마스트리문화축제준비위원회
요금 : 무료
[행사내용]
1. 찾아가는 보물찾기
광복로 거리 쇼윈도우,간판,트리장식물등의 일부분을 홈페이지에 올린뒤 축제 방문객들이 그것들을 찾아 
스마트폰을 찍어 퍼즐을 완성하면 소정의 상품을 증정!
* 축제 기간 중 2회 정도 주말을 이용해 실시
2. 트리를 밝혀라
설치된 전기자전거에 차례로 탑승하여 패달을 돌리면 생산된 전기가 트리를 밝히는 체험형 프로그램!
* 2014년 12월 둘째 주말 오후 6시 ~ 7시
3. 소망트리
여러개의 태그로 만든 트리에 소원을 적는 프로그램!
* 11월 29일~ 2013년 1월 4일
* 광복로 삼성매장 앞
4. 스노우 타임
눈이 보기 어려운 부산지역에 크리스마스 축제를 맞이하여 시간에 맞춰 인공눈을 뿌리는 행사!
* 11월29일 ~ 12월 31일 저녁 7시, 8시(10분씩)
* A구간의 눈 내리는 마을
        
여행 예산 KRW 0
항공료 숙박 음식
0 0 0
쇼핑 교통 입장료
0 0 0
오락 기타
0 0
1 일차 (2014.12.24)
여행국가 : 한국 1일 지출 비용 : KRW 0
광안리 해수욕장 (Gwangalli Beach) 자갈치시장 (Jagalchi Market) BIFF 광장, 구 PIFF 광장 (BIFF Square) 남포동 먹자골목 옥천국밥 남포동 나담
AM 10:30 -
1. 광안리 해수욕장 (Gwangalli Beach)

메모
부산하면 먼저 바다가 떠오르기 마련이다. 부산에 왔는데 바다를 안보고 갈 순 없으니 광안리에서 멋진 다리와 함께 바다를 감상해 보는 것도 좋을 듯 싶다. 부산 현지인들 또한 부산 바다는 여름보다 겨울이 좋고 해운대보다는 광안리를 많이 추천하고 있다!

[명소자세히보기]

PM 01:00 -
2. 자갈치시장 (Jagalchi Market)

메모
부산의 명물이기 때문에 한 번쯤은 꼭 들러줘야 하는 자갈치 시장! 남포동에 있어서 크리스마스 축제장소와 위치적으로도 가깝다. 하지만 잘 모르고 사면 바가지 쓸 수 있다고 하니 조심해야 한다.

[명소자세히보기]

PM 02:30 -
3. BIFF 광장, 구 PIFF 광장 (BIFF Square)

메모
부산국제영화제의 상징이라고 할 수 있는 곳! 길거리에는 유명 배우들과 감독들의 핸드프린트가 붙어있다. 또한 씨앗호떡 맛집도 많다고 하니 호떡 먹으면서 구경하는 것도 좋을 것 같다.

[명소자세히보기]

PM 04:00 -
4. 남포동 먹자골목

메모
남포동에서 또 유명한 먹자골목! 간단하게 떼우기 좋은 길거리 음식들이 한가득이다. 부산의 대표 길거리 음식인 비빔당면과 부산어묵, 그리고 씨앗호떡을 먹기위해 많이들 찾는 곳이다.

[명소자세히보기]

PM 05:00 -
5. 옥천국밥

메모
냉채족발, 밀면 등 유명한 부산 음식이 많지만 겨울이니까 돼지국밥을 선택하는 것도 괜찮을 것같다. 옥천 국밥은 남포동에서 유명한 돼지 국밥집! 추운데서 오래 돌아다녔으니 뜨끈한 돼지국밥으로 속을 달래보자!

[명소자세히보기]

PM 06:00 -
6. 남포동

메모
[광복로] 저녁이 되면 거리마다 걸려있는 화려한 크리스마스 조명과 하이라이트인 거대한 트리에 불이 들어오기 시작한다. 본격적으로 크리스마스 축제를 즐길시간! 1.2km에 달하는 긴 거리에 차가 진입하지 못하도록 막아놓고 축제가 진행된다고 한다. 또한 7시와 8시, 이렇게 두차례 인공 눈을 뿌려주기 때문에 이 시간에는 꼭 광복로로 집합해야 한다.

[명소자세히보기]

PM 09:00 -
7. 나담

메모
크리스마스 분위기와 어울리는 카페라서 추천하고 싶다. 아늑한 분위기와 엔틱한 느낌의 찻잔, 인테리어가 크리스마스 시즌에 들려보면 좋을만한 곳이다.

[명소자세히보기]

위시빈, 여행, 여행일정, 세계 여행, 지도, 길찾기, 관광, 자유여행, 맛집, 여행 가이드북, 위시빈 임베드, 해외여행, 일본일정, 일본여행, 유럽여행
일정자세히보기

2014년 10월 17일 금요일

iframe scrollTop

iframe 에서 스크롤 탑을 구할수 있는가? 구할 수 있다. :)

iframe javascript

 function listener(event){
          self.scrollTop = event.data;
          console.log('Message Received : '+event.data);
        }

parent script

<script  type="text/javascript"
    src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js">
</script>
<script type="text/javascript">
      var win = document.getElementById("myIframe").contentWindow;
      $(window).scroll(function(){
          var wintop = $(window).scrollTop();
          win.postMessage($(window).scrollTop(), 'http://localhost:4000');            
      });
</script>


참고
http://stackoverflow.com/questions/24656292/jquery-accessing-parent-document-scrolltop-from-iframe-and-sop

2014년 10월 12일 일요일

몽고디비2.6 샤딩

1.0 컨피스 서버용 디렉토리 만들기
cd test
mkdir log //로그용 디렉토리
mkdir configsvr
cd configsvr/
mkdir server1
mkdir server2
  mkdir server3

1.1 컨피그 서버 뛰우기
컨피그 서버는 총 3대를 뛰운다
컨피그 서버중 1대라도 죽으면 컨피그 서버의 메타 데이터는 읽기 전용이된다.
샤딩용 데이터 목록만 저장하고 있기 때문에 많은 자원을 소모하지 않는다.
옵션 --configsvr 는 --port 27019 --dbpath /data/configdb 를 추가하는 기능만 있음
덥어 쓸수 있음

구성 서버 데이터를 자주 백업 받고 유지보수 작업시에도 반드시 데이터를 백업 받자

mongod --configsvr --dbpath /test/configsvr/server1  --logpath /test/log/config_server_1 --fork --port 28001
mongod --configsvr --dbpath /test/configsvr/server2  --logpath /test/log/config_server_2 --fork --port 28002
mongod --configsvr --dbpath /test/configsvr/server3  --logpath /test/log/config_server_3 --fork --port 28003

1.2 몽고s 뛰우기
컨피그 서버가 뜬다음에 뛰울수 있다.

mongos --configdb 127.0.0.1:28001,127.0.0.1:28002,127.0.0.1:28003 --logpath /test/log/mongos_1 --fork --port 10000

1.3 이미 있는 복제셋 추가하기
mongo --port 10000
sh.addShard('rs0/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003')

sh.status()
--- Sharding Status ---
 sharding version: {
"_id" : 1,
"version" : 4,
"minCompatibleVersion" : 4,
"currentVersion" : 5,
"clusterId" : ObjectId("543a70e0e74f6e2fe12c8638")
}
shards:
{  "_id" : "rs0",  "host" : "rs0/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003" }
databases:
{  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
{  "_id" : "test",  "partitioned" : false,  "primary" : "rs0" }

1.4 mongos 가 뛰어 졌으면 샤드에 직접적으로 접속 하지 못하게 방화벽 규칙을 설정한다.
1.5 샤딩을 추가할떄는 빈 복제셋을 추가하면된다.
샤드 별로 다른 디비를 만들어서 추가 할수 도 있다.
이럴 경우 디비 별로 샤딩 된다.

1.6 테스트2 복제 셋을 만들어서 샤딩에 추가해보
디렉토리를 만들자
cd test
mkdir rs1
mkdir server1
mkdir server2
mkdir server3

mongod --port 29001 --dbpath /test/rs1/server1 --replSet 'rs1' --logpath /test/log/rs1_server_1 --fork
mongod --port 29002 --dbpath /test/rs1/server2 --replSet 'rs1' --logpath /test/log/rs1_server_2 --fork
mongod --port 29003 --dbpath /test/rs1/server3 --replSet 'rs1' --logpath /test/log/rs1_server_3 --fork

mongo --port 29001
var rsconfig = {"_id":"rs1", "members":[{"_id":1, "host":"127.0.0.1:29001"},{"_id":2, "host":"127.0.0.1:29002"},{"_id":3, "host":"127.0.0.1:29003"}]}

rs.initiate(rsconfig)

use test2
function insertTest(count){
for(var i = 0; i< count; i++){
db.test1.insert({a:i, b:i});
}
}

insertTest(1000);

> mongos 접속
sh.addShard('rs1/127.0.0.1:29001,127.0.0.1:29002,127.0.0.1:29003')

1.7 샤딩하기
디비를 샤딩한다.
> mongos 접속
use test
sh.enableSharding('test')db.enableSharding('test');

sh.status()
 databases:
{  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
{  "_id" : "test",  "partitioned" : true,  "primary" : "rs0" }
{  "_id" : "test2",  "partitioned" : false,  "primary" : "rs1" }

partitioned true 확인

1.8 컬랙션 샤딩하기
db.test1.ensureIndex({a:1})
샤딩 키에 인덱스가 없으면 에러가 발생한다.
sh.shardCollection('test.test1',{a:1})

function insertTest(count){
for(var i = 0; i< count; i++){
db.test1.insert({a:i, b:i});
}
}

insertTest(10000);
sh.status()
databases:
{  "_id" : "test",  "partitioned" : true,  "primary" : "rs0" }
test.test1
shard key: { "a" : 1 }
chunks:
rs1 1
rs0 1
{ "a" : { "$minKey" : 1 } } -->> { "a" : 0 } on : rs1 Timestamp(2, 0)
{ "a" : 0 } -->> { "a" : { "$maxKey" : 1 } } on : rs0 Timestamp(2, 1)
아래와 같이 청크가 샤딩 된걸 확일 할수 있다.





몽고디비2.6 리플리케이션

1.테스트용 디렉토리 만들기
1.1 컨피그 디렉토리 만들기
cd /
sudo mkdir /cofig
sudo chown lee /config

1.2 테스트 디비용 디렉토리 만들기
cd /
sudo mkdir /test
sudo chown lee /test
cd mkdir rs0

mkdir rs0
mkdir rs0/server1
mkdir rs0/server2
mkdir rs0/server3

1.3 레플리케이션 시작하기
mongod --port 27001 --dbpath /test/rs0/server1 --replSet 'rs0' --logpath /test/log/rs0_server_1 --fork
mongod --port 27002 --dbpath /test/rs0/server2 --replSet 'rs0' --logpath /test/log/rs0_server_2 --fork
mongod --port 27003 --dbpath /test/rs0/server3 --replSet 'rs0' --logpath /test/log/rs0_server_3 --fork

옵션 설명
--port
디비가 시작할 포트
--dbpath
디비가 위치할 디렉토리
--replSert
레플리케이션 이름
1.4 레플리케이션 설정하기
몽고 쉘로 접속해서 최초 레플리케이션을 init 한다.

mongo --port 27001
var rsconfig = {"_id":"rs0", "members":[{"_id":1, "host":"127.0.0.1:27001"}]}

rs.initiate(rsconfig)

*테스트를 위해 localhost ,127.0.0.1 을 지원하지만 다른 ip 와 섞어 쓰는건 안된다.

1.5 복제셋 맴버추가
rs.add("127.0.0.1:27002")
rs.add("127.0.0.1:27003")

1.6 복제셋 멤버제거
/*
rs.remove("127.0.0.1:27002")
*/

1.7 복세셋 멤버 확인
rs.config()
{
"_id" : "rs0",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
},
{
"_id" : 3,
"host" : "127.0.0.1:27003"
},
{
"_id" : 4,
"host" : "127.0.0.1:27002"
}
]
}

_id : 복제셋 이름
verstion : 멤버를 추가할때마다

1.8 컨피그 변경이 가능함
/*
var reconfig = rs.config()
reconfig.members[1].host = '0.0.0.0:27003';
rs.reconfig(reconfig)
*/

1.9 세컨더리 접속
mongo --port 27002

2.레플리케이션 상태 확인

2.0 마스터에서 확인
mongo --port 27001
db.isMaster()
{
"setName" : "rs0",
"setVersion" : 5,
"ismaster" : true,
"hosts" : [
"127.0.0.1:27001",
"127.0.0.1:27002",
"127.0.0.1:27003"
],
"primary" : "127.0.0.1:27001",
"me" : "127.0.0.1:27001",
"localTime" : ISODate("2014-10-10T14:59:32.981Z"),
"maxWireVersion" : 2,
"minWireVersion" : 0,
"ok" : 1
}

2.1 마스터 : 예제 컬랙션 insert
use test

function insertTest(count){
for(var i = 0; i< count; i++){
db.test1.insert({a:i, b:i});
}
}

insertTest(1000);
db.test1.find();

2.2 마스터 : 레플리케이션 상태확인
rs.status()

*실제로 레플리케이션에 모든 복제가 완료 되었는지 확인하려면
optimeDate 가 동일한지 확인하면 된다.
{
"set" : "rs0",
"date" : ISODate("2014-10-10T15:04:57Z"),
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "127.0.0.1:27001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2331,
"optime" : Timestamp(1412953460, 1000),
"optimeDate" : ISODate("2014-10-10T15:04:20Z"),
"electionTime" : Timestamp(1412951411, 1),
"electionDate" : ISODate("2014-10-10T14:30:11Z"),
"self" : true
},
{
"_id" : 3,
"name" : "127.0.0.1:27003",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 924,
"optime" : Timestamp(1412953460, 1000),
"optimeDate" : ISODate("2014-10-10T15:04:20Z"),
"lastHeartbeat" : ISODate("2014-10-10T15:04:55Z"),
"lastHeartbeatRecv" : ISODate("2014-10-10T15:04:56Z"),
"pingMs" : 0,
"syncingTo" : "127.0.0.1:27001"
},
{
"_id" : 4,
"name" : "127.0.0.1:27002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 893,
"optime" : Timestamp(1412953460, 1000),
"optimeDate" : ISODate("2014-10-10T15:04:20Z"),
"lastHeartbeat" : ISODate("2014-10-10T15:04:56Z"),
"lastHeartbeatRecv" : ISODate("2014-10-10T15:04:56Z"),
"pingMs" : 0,
"syncingTo" : "127.0.0.1:27001"
}
],
"ok" : 1
}

2.3 슬레이브 : 읽기 시도
db.test1.find()
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }

몽고 디비에서 레플리케이션 셋의 기본 설정은 슬레이브에서 읽는게 안된다.
(슬레이브에서 읽을 경우 예전 데이터를 읽을수 있기 때문에)

어플리케이션에서 예전 데이터를 읽어도 상관없을때는
마스터 > rs.slaveOk()
슬레이브 읽기가 허용된다.

슬레이브 > db.test1.find(); 결과값이 리턴된다.

2.4 디비가 죽을 경우의 마스터 선출 작업
27001 디비를 죽인다. ctrl+c

슬레이브1 > rs.status()
현재 마스터 이며 member 의 27001 이 죽었음을 확인한다.
27001
"health" : 0,

mongod --port 27001 --dbpath /test/rs0/server1 --replSet 'rs0'
다시 시작한다.

슬레이브1 > rs.status()
좀전의 27001 이 정상동작 하는지 확인
27001
"health" : 1,

* 3 대중 2대가 살아 남을 경우 투표를 통해 하나의 슬레이브를 마스터로 승격 시킨다.
투표를 하기위해서는 과반수가 살아 있어야 한다.
ex)3 대 이면 2대, 4 대이면 3대 ...

* 과반수가 살아 있지 않는다면 투표를 할수 없기 때문에 secondery 상태가 유지된다.
why? 과반수 인가?
a, b 데이터 센터에 몽고디비 1,2,3을 배치했다
현재 마스터는 a:1 이다.
a : 1,2
b : 3

b 데이터 센터가 일시적으로 네트워크가 단절되었다
과반수가 아니라면 b: 3이 마스터가 되고 데이터를 받아드린다.
이때 a:1 도 마스터이기 때문에 네트워크가 복구 되면 데이터의 정합성 문제가 발행한다.

2.5 세컨더리에서 프라이머리로 변경하기
(3개의 멤버 중에서 2개의 맴버가 죽었다고 가정 살아있는 맴버를 프라이머리로 만들기)
var cfg = rs.config()
rs0:SECONDARY> cfg
{
"_id" : "rs0",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
},
{
"_id" : 3,
"host" : "127.0.0.1:27003"
},
{
"_id" : 4,
"host" : "127.0.0.1:27002"
}
]
}

27001 이 세건더리이고 27002, 27003 이 죽었을 경우
cfg.members = [cfg.members[0]]
cfg 로 멤버 확인
{
"_id" : "rs0",
"version" : 5,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
}
]
}

rs.reconfig(cfg, {force : true})
rs.status()
현재 서버가 프라이머리인거 확인




2.6 아비타 더하기
cd /test
mkdir rs0/arb1

mongod --port 27004 --dbpath /test/rs0/arb1 --replest rs0
프라이머리 : rs.addArb('127.0.0.1:27004')

rs.status()
{
"set" : "rs0",
"date" : ISODate("2014-10-12T06:49:55Z"),
"myState" : 1,
"members" : [
{
"_id" : 1,
"name" : "127.0.0.1:27001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1095,
"optime" : Timestamp(1413096584, 1),
"optimeDate" : ISODate("2014-10-12T06:49:44Z"),
"electionTime" : Timestamp(1413096488, 1),
"electionDate" : ISODate("2014-10-12T06:48:08Z"),
"self" : true
},
{
"_id" : 2,
"name" : "127.0.0.1:27004",
"health" : 1,
"state" : 7,
"stateStr" : "ARBITER",
"uptime" : 11,
"lastHeartbeat" : ISODate("2014-10-12T06:49:54Z"),
"lastHeartbeatRecv" : ISODate("2014-10-12T06:49:55Z"),
"pingMs" : 0
}
],
"ok" : 1
}

stateStr 'ARBITER' 확인

2.7 아비터가 실제 동작하는지 또한 실제로 데이터는 없는지 확인
mongo --prot 27004
show dbs
테스트 db가 없는지 확인

use test
test.test1.insert({a:1})

하게 되면 에러가 발생하면서 인설트 되지 않는게 맞다

2.8 프라이머리를 죽였을때 아비터가 실제로 투표에 참여하는지 확인
27001 을 죽인다.
mongo --port 27002
에서 secondery 에서 primary 로 변경 되었는지 확인

2.9 우선 순위로 프라이머리로 설정 하고 싶은 마스터를 설정하기
기본적으로 priority 1 높으면 높을 수록 우선 순위를 가진다.
만약 priority 를 0 으로 하면 해당 멤버는 절대 primary 가 될수 없다.

rs.add({_id:4, host:"127.0.0.1:27003", priority:1.5})

접속 되있던 창은 primary -> secnodery 가 되고
mongo --port 27003으로 접속하면 우선 순위가 높은 애가 프라이머리가 되어 있다.

우선 순위는 rs.config() 에서 확인 가능 하다
{
"_id" : "rs0",
"version" : 25498,
"members" : [
{
"_id" : 1,
"host" : "127.0.0.1:27001"
},
{
"_id" : 2,
"host" : "127.0.0.1:27004",
"arbiterOnly" : true
},
{
"_id" : 3,
"host" : "127.0.0.1:27002"
},
{
"_id" : 4,
"host" : "127.0.0.1:27003",
"priority" : 1.5
}
]
}

이번에는 더하지않고 그냥 프라이머리를 바꿔만 보자
var cfg = rs.config()
cfg.members[0].priority = 2
rs.reconfig(cfg)

mongo --port 27001
프라이머리 확인

3.0 멤버 숨기기
클라이언트에 보이기 싫은 멤버는 아래와 같이 hidden 과 priority 를
조절해서 안보이게 할 수 있다.

var cfg = rs.config()
config.members[1].hidden = 0
config.members[1].priority = 0
rs.recofig(cfg)

rs.isMaster() 에서 사라지게 된다.
클라이언트는 isMaster() 를 호출해서 복제 셋의 멤버를 확인 하기 때문에
해당 부분만 고치면된다.

보이게 할려면 해당 속성 삭제하고 리컨피그 하면된다.

3.1 슬레이브 지연
컨피그에 아래 두개의 속성 추가
slaveDelay = 60 * 60 *24 (하루)
priority = 0

3.2 인덱스 생성안하기
인덱스를 생성하지 않는다.

buildIndexes = false
priority = 0




















2014년 9월 18일 목요일

ie8 date wrapping (iso date parse problem)

https://github.com/es-shims/es5-shim/blob/v4.0.3/es5-shim.js

<script type="text/javascript">
            Wrapper = (function (NativeDate) {
                // Date.length === 7
                function Wrapper(Y, M, D, h, m, s, ms) {
                    var length = arguments.length;
                    console.log(this);
                    console.log(NativeDate);
                    console.log(this instanceof NativeDate);
                    if (this instanceof NativeDate) {
                        var date = length === 1 && String(Y) === Y ? // isString(Y)
                            // We explicitly pass it through parse:
                                new NativeDate(Date.parse(Y)) :
                            // We have to manually make calls depending on argument
                            // length here
                                        length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :
                                        length >= 6 ? new NativeDate(Y, M, D, h, m, s) :
                                        length >= 5 ? new NativeDate(Y, M, D, h, m) :
                                        length >= 4 ? new NativeDate(Y, M, D, h) :
                                        length >= 3 ? new NativeDate(Y, M, D) :
                                        length >= 2 ? new NativeDate(Y, M) :
                                        length >= 1 ? new NativeDate(Y) :
                                new NativeDate();
                        // Prevent mixups with unfixed Date object
                        date.constructor = Wrapper;
                        console.log('there');
                        return date;
                    }
                    console.log('here');
                    return NativeDate.apply(this, arguments);
                }

                Wrapper.prototype = NativeDate.prototype;
                Wrapper.prototype.constructor = Wrapper;

                // Upgrade Date.parse to handle simplified ISO 8601 strings
                Wrapper.parse = function parse(string) {
                    return NativeDate.parse.apply(this, arguments);
                };
                return Wrapper;
            })(Date);
    </script>

2014년 9월 16일 화요일

javascript event wrapper

아래 예제는 jquery 같은 라이블러리에서 어떻게 이벤트를 wrapping 하는지에 대한
가장 기초적인 예제이다.

순수 자바 스크립트를 사용할 경우에 이벤트에 연관된 인스턴스를 event.srcElement 에서
끄내서 써야 된다.
var test1 = document.getElementById('test1');
test1.onclick = function(event) {
      console.log(arguments);
      console.log(event.srcElement);
};

위와같은 경우 불편하다!
그럼 jquery 와 같이 제공해줄려면 어떻게 해야될까?
뤱퍼를 만들어서 해당 인스턴스를 만들고 해당 이벤트 호출시
이벤트 객체와 this 를 같이 넘겨주면 된다.

그럼 어떻게 구현해야 될까?
답은 클로저이다 클로저의 경우 정의 시점에 넘어간 파라미터를 scope chain 을 통해
참조 할수 있음으로 정의 시점과 실행 시점을 분리할수 있다
또한 내가 원하는 argement 를 실행 시점에 넘겨 줌으로 jquery 와 같은 뤱핑을 할수 있다.

var test3 = new DhtmlObject('test3');
위와 같이 DhtmlObject을 생성할 경우 해당 element  를 가져와서 이미정의된 브라우저
(여기서는) 클릭 펑션에 associateObjWithEvent 를 실행해서 할당한다.
associateObjWithEvent 의 경우 최초 아규먼트를 받아 자신의 activation obj에 할당하고
inner function 을 리턴한다.(이벤트 처리 펑션) inner function 의 scope chain 에는  outer
function 의 scope 가 들어가 있기 때문에 outerfunction 이 리턴된 후 에도 outer function 의  아규먼트에 접근 가능 하며 실제 실행될때는 리턴된 inner function 이 실행된다.

     

http://jibbering.com/faq/notes/closures/#clSto
상세 설명은 위의 글을 참고 바란다.


<html>
<head lang="en">
 
    <title></title>
    <script>

        function associateObjWithEvent(obj, methodName){
            return (function(e){
                e = e|| window.event;

                return obj[methodName](e, this);
            });
        }



        function DhtmlObject(elementId){
            var el = document.getElementById(elementId);
            if(el){
                el.onclick = associateObjWithEvent(this,'doOnClick');
                el.onmouseover = associateObjWithEvent(this, "doMouseOver");
                el.onmouseout = associateObjWithEvent(this, "doMouseOut");
            }
        }

        DhtmlObject.prototype.doOnClick = function(event, element){
            alert(event);
        }
    </script>

</head>
<body>
    <button id="test1">testButton1</button>
    <button id="test2">testButton2</button>
    <button id="test3">testButton3</button>
    <button id="test4">testButton4</button>

    <script>
        var test1 = document.getElementById('test1');
        test1.onclick = function(event) {
            console.log(arguments);
            console.log(event.srcElement);
        };

        var test2 = document.getElementById('test2');
        test2.onclick = function(event) {
            console.log(arguments);
            console.log(event.srcElement);
        };


        var test3 = new DhtmlObject('test3');
        test3.doOnClick = function(event, element){
            console.log(arguments);
            console.log(element);
        }

        var test4 = new DhtmlObject('test4');
        test4.doOnClick = function(event, element){
            console.log(arguments);
            console.log(element);
        }

    </script>
</body>
</html>

2014년 6월 13일 금요일

node async parallel error 처리시 주의사항

async.parallel([
      function(callback){//1번펑션
         async.parallel([
             function(callback){
                작업1
             },
             function(callback){
                작업2
             }
         ],
         function(err, results){
             
         });
      },
      function(callback){//2번펑션
        async.parallel([
            function(callback){
              작업3
               callback(new Error())
            },
            function(callback){
               작업4
            }
          ],
          function(err, results){

          });
      }
  ],
  function(err, results){
    if(err) res.send(404);
  });

}

페레럴은 동시에 작업을 처리한다.

문제는 아래와 같은 상황에서 발생된다.

1번 펑션은 작업 1까지 처리 되었고
2번 펑션에서 작업 3번후 에러가 발생했을때 이다.

이때 에러가 발생 되었음으로 마지막 펑션을 호출하고 페이지는 리턴된다.

그 후 작업 2가 진행된다.

*즉 에러가 발생 될 경우에 다른 작업이 완료되지 않았어도 마지막 펑션이 호출된다.

그럼 어떻게 사용해야 되는가? 로직에 따라 그냥 에러는 던지지 않고 무조건 널을 리턴하고


async.parallel([
      function(callback){
         callback(null,isSuccess)
      },
      function(callback){
         callback(null,isSuccess)
      }
  ],
  function(err, results){
     if(results 체크){
       res.send(400)
     }
  });

}

callback 함수의 파라미터에 isSuccess 등의 속성을 던져서 마지막 results 부분에서 처리하면 된다

map 등 내부적으로 parallel 을 사용할때는 한번더 생각해보자~

2014년 5월 20일 화요일

node cycle 관련

노드에서 global 영역에 require 를 사용하면 해당 모듈을 init 하는대 필요한 모듈이라고
가정하기 때문에 cycling 을 발생한다.

그렇기 때문에 메서드 안에서 runtime 시 디펜던시가 호출되게 하는 방법이 있다.
(require 는 한번 호출시 캐슁하기때문에 성능 문제는 없다)

1.메서드 안에서 require 사용
//a.js
function a(){

}

a.prototype.onlyAFunction = function(){
  return 'a';
}

a.prototype.withBFunction = function(){
  return require('./b').onlyBFunction();
}

module.exports = new a();

//b.js

function b(){

}

b.prototype.onlyBFunction = function(){
  return 'b';
}

b.prototype.withAFunction = function(){
  return require('./a').onlyAFunction();
}

module.exports  = new b();

//main.js
var a = require('./a');
var b = require('./b');


console.log(a.onlyAFunction());
console.log(a.withBFunction());

console.log(b.onlyBFunction());
console.log(b.withAFunction());

결과
a
b
b
a

2.한쪽에만 의존성이 있을경우 dependency injecttion 을 사용할수 있으나
코드간 의존성이 생기기때문에 개인적으로 비추다..
//a.js
var b = require('./b');

function a(){

}

a.prototype.onlyAFunction = function(){
  return 'a';
}

a.prototype.withBFunction = function(){
  return new b(this).onlyBFunction();
}

module.exports = new a();
//b.js
function b(a){
  this.a = a;
}

b.prototype.onlyBFunction = function(){
  return 'b';
}

module.exports = b;

//main.js
var a = require('./a');
var b = require('./b')(a);


console.log(a.onlyAFunction());
console.log(a.withBFunction());

console.log(b.onlyBFunction());
console.log(b.withAFunction());
3.객체를 생성하지 않고 exports의 property로 사용하는 경우
아래의 경우 객체 생성시 사용되는 의존성이 아니기때문에
(userService, planService 등과 같이 서비스로 사용할시)
가장 깔금하나 모듈 생성시 function(생성자) 를 사용해야 할 경우 문제가 발생함으로 공통코딩 규칙으로 잡기 머하다.


//a.js
console.log('a starting');
exports.done = false;

var b = require('./b4');

exports.onlyAFunction = function(){
  return 'a';
}

exports.withBFunction = function(){
  return b.onlyBFunction();
}

console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

//b.js

console.log('b starting');
exports.done = false;

var a = require('./a4');

exports.onlyBFunction = function(){
  return 'b';
}

exports.withAFunction = function(){
  return a.onlyAFunction();
}
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

//main.js
console.log('main starting');
var a = require('./a4');
var b = require('./b4');


console.log(a.onlyAFunction());
console.log(a.withBFunction());

console.log(b.onlyBFunction());
console.log(b.withAFunction());

console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

결과
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
a
b
b
a
in main, a.done=true, b.done=true

5.위에서 언급한 생성자 사용시 발생하는 문제다.
아래 문제는 노드에서 require 싸이클이 발생할 경우
중간에 완성이 들된 object 를 리턴하기 때문에 발생한다.
참조 http://nodejs.org/api/modules.html#modules_cycles



//a.js
console.log('a starting');
exports.done = false;

var b = require('./b5');

function a(){

}

a.prototype.onlyAFunction = function(){
  return 'a';
}

a.prototype.withBFunction = function(){
  return b.onlyBFunction();
}

module.exports = new a();
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

//b.js
console.log('b starting');
exports.done = false;

var a = require('./a5');

function b(){

}

b.prototype.onlyBFunction = function(){
  return 'b';
}

b.prototype.withAFunction = function(){
  return a.onlyAFunction();
}


module.exports = new b();
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

//main.js
console.log('main starting');

var a =  require('./a5');
var b =  require('./b5');

console.log(1);
console.log(a.onlyAFunction());
console.log(2);
console.log(a.withBFunction());
console.log(3);
console.log(b.onlyBFunction());
console.log(4);
console.log(b.withAFunction());
console.log('end');
2
결과
a starting
b starting
in b, a.done = false
b done
in a, b.done = undefined
a done
1
a
2
b
3
b
4

/Users/yeonghuni/workspace/wishbeen/test/test5/b5.js:19
  return a.onlyAFunction();
           ^
TypeError: Object #<Object> has no method 'onlyAFunction'
    at b.withAFunction (/Users/yeonghuni/workspace/wishbeen/test/test5/b5.js:19:12)

4번후 b.withAFunction()을 호출할때 에러가 발생한다.
왜냐하면 최초 a 생성시  b가 필요해서 b를 생성한다.
이때 b에서 다시 a 를 호출하는대 싸이클링이 생성되기 때문에
덜만들어진 a의 객체를 리턴한다. (이때 a.onlyAFunction)이 존재하지 않는다.
그러므로 실재로 코드를 호출하게 되면 런타임시 에러가 발생한다.

이때 정의된 a 가 왜? 런타임에서 에러가 발생하는가?
자바스크립트에서 idetifier resoultion 은 scope chain 을 대상으로 이루어진다.

각각의 함수는 실행시 자신의 execution context 에서 실행되게 되는대 이때 execution context는 activation/variable object + 부모[[scope(chain)]]이다.

(scope chain 은 function 이 정의 될때 자신이 호출되는 function 의 scope 체인(리스트) 속성  맨앞에 activavion/variable object 를 추가하여 만들어진다.)

위의 경우 new b() 를 하게 되면 b의  activavion/variable object  의 a는 생성되다만 object 이다.

b.withAFucntion 을 할경우 b를 호출 한다 이때 b에는 함수가 없음으로 prototype을 뒤지는대 이때 b.prototyp.withAFunction 이 호출된다.

이때 b.prototyp.withAFunction 은 b가 require 될때 생성되었기 때문에 scope 의 a 객체는
정의 되다 만 객체이다.

정의되다만 객체에는 a의 메서드가 없기 때문에 에러가 발생한다.

https://github.com/joyent/node/blob/master/src/node.js#L738

물런 a가 레퍼런스로 넘어갔으니 b가 완성되고 a 가 완성된 후
코드가 호출 되니 a에 메서드가 호출되야 하지 않느냐 라고 물어볼수 있다.

activation/variable object 는 해당 object 의 현재 모습을 copy 해서 저장하기 때문이다.

즉 main에서 b.withAFunction 은 함수 생성 순간에 복사된 a 오브젝트를 지속적으로 참조한다.







2014년 1월 28일 화요일

netty 소스 파일 설치(intellij)

netty 소스 파일 추가
- dev 에 보면 intellij를 기본 툴로 쓴단다
  대충 써보니 이클립스보다 좋은거 같당 :)

1.intelJ down

2.https://code.google.com/p/msysgit/downloads/detail?name=Git-1.8.5.2-preview20131230.exe&can=2&q=
down

3.환경변수 추가
GIT_HOME
C:\Program Files (x86)\Git\bin
PATH에추가
%GIT_HOME%;

4.protocol https not supported or disabled in libcurl 해당 에러가 발생하면
C:\Windows\sysWOW64 and/or C:\Windows\System32에 libcurl.dll 의 이름을 변경
--http://stackoverflow.com/questions/17694502/libcurl-dll-error-with-git-push


5.C:\Program Files (x86)\JetBrains\Intelli\bin
idea64.exe.vmoptions 옵션 변경
idea64.exe 64비트로 실행

6.VCS 에서 네티 받기

2014년 1월 27일 월요일

java concurrency in practice ch 8 Applying Threads Pools

8.1.1Thread Starvation Deadlock

Whenever you submit to an Executor tasks that are not independent, be aware of the possibility of thread starvation deadlock, and document any pool sizing or configuration
constraints in the code or configuration file where the Executor is configured

Executor 에 서로 의존 하는 일을 submit 할때는 쓰레드 기아 데드락의 가능성을 인지해야한다. 그리고 Executor 의 설정 파일에 관해서 꼭 문서화 해야 한다.

아래 코드는 싱글 쓰레드 에서는 언제나 데드락이 걸린다. 또한 쓰레드 풀이 충분하지 못해 꽉차있다면 서로 기다리기때문에 데드락이 걸릴수 있다 ( 이와 같은 상황을 thread starvation deadlock) 이라고 한다.
public class ThreadDeadlock {
  ExecutorService exec = Executors.newSingleThreadExecutor();

  public class RenderPageTask implements Callable {
   public String call() throws Exception {
    Future[String] header, footer;
    header = exec.submit(new LoadFileTask("header.html"));
    footer = exec.submit(new LoadFileTask("footer.html"));
    String page = renderBody();
    // Will deadlock -- task waiting for result of subtask
    return header.get() + page + footer.get();
   }
  }
 }
8.1.2 Long-running Tasks
테스크가 너무 긴게 많다면 어느순간 모든 쓰레드가 긴 테스크로 채워질꺼다
해당 사항은 문제가 발생하기 때문에 긴테스크 등에 시간제한을 두고 다시 requeue 하는 방법이 있다

Thread.join, BlockingQueue.put, countDownLatch.await, Selector.select등

8.2.Sizing Thread Pools

2014년 1월 24일 금요일

mysql 강의 1~7강 중 아키텍처, 트랙잭션, 모델링

1.아키텍처

2.트랜잭션

6.모델링

high_performance_mysql_3rd_edition 쿼리튜닝

쿼리 튜닝
원칙1.서브 테스크를 없애거나 줄여라

Slow Query Basics: Optimize Data Access
보통 데이터 량이 문제다
해결
1.필요한것보다 더 많은 양의 데이터 (로우, 컬럼)을 요청하지 않는가?
2.mysql 서버가 필요한 양보다 더 많은 양을 분석하고 있지 않은가?

더 많은 데이터를 요청하는 예
1.Fetching more rows than needed
많은 app개발자가 MySQL에서 착각하는 부분은
로우 100줄 호출 후 10줄후 close 해버리면 10줄만 온다고 생각하는건대
Mysql은 실제로 100줄 다보낸다

그러니 LIMIT 써라
ex) top page 10


2.Fetching all columns from a multitable join
SELECT * FROM A, B
* 쓰지 말자!
꼭 필요한 컬럼만 호출하자

3.Fetching the same data repeatedly
같은 데이터 디비에 여러번 요청 하지 말자

mysql 서버가 너무 많은 양을 분석할때
query 정확할때
측정 기준
반응시간
조사된 데이터
리턴된 데이터

Response time
service time + queue time

service time
실제로 실행 된 시간

queue time
기다린 시간
ex)I/O, lock등

quick upper-bound estimate (QUBE)
플랜과 인덱스의 데이터 량을 보고 몇번의 io 가 필요할지
측정 한다 그래서 이 시간보다 빠른가 비교..
자세한 내용 책참조 하란다..
Relational Database Index Design and the Optimizers (Wiley).
In a nutshell:
examine the query execution plan and the indexes involved, determine how many
sequential and random I/O operations might be required, and multiply these by the
time it takes your hardware to perform them. Add it all up and you have a yardstick to
judge whether a query is slower than it could or should be


Rows examined and rows returned
몇개가 조사되고 몇개가 리턴 되었느냐인대
별 쓸모 없다 테이블 조인 하게 되면 1:X
X가 엄청 크다면? 필요가 있는가..

Rows examined and access types
쿼리 코스트를 생각 할때 테이블에서 row 하나 읽는 cost 를 생각해봐라
access 방법은 EXPLAIN의 type 에 나타남

읽는 양  많음 --> 적음
full table > index scan > range scan > unique index look up > constants


MySql이 where 를 적용 하는 방법
1.검색조건을 index look up 에 사용하여 적용되지 않는 로우를 없앰
*storage engine 레벨에서 적용됨

2.using index
storage engine에서는 인덱스를 읽고
server에서 필요없는 로우 없앰

3.using where
테이블에서 로우들을 읽고
server에서 필요 없는 로우를 없앰

*result row 에 비해 examined 로우가 정말 많다면?
1.커버링 인덱스를 사용하게 하라(테이블 접근을 줄여라)
2.summary 테이블을 사용해라
3.쿼리가 최적의 플랜으로 실행 되게 재작성하라

쿼리 재작성 방법
1.동일 결과를 같고 더 빠르게 할수도 있다
2.성능의 이득이 있다면 결과값을 변경 할수도 있다
2.어플리케이션 코드를 변경 해야 할 수도 있다.


복잡한 쿼리 vs 심플 쿼리
마이 sql은 일반 서버 에서 초당 100,000건의 간단한 쿼리를 쉽게 처리한다.
gigabit network 는 초당 2000건이상을 처리한다.

즉 네트워크 비용이 예전 만큼 중요하지 않다(?.. 중요한탠대..)

물런 지금도 가능하면 네트워크 비용을 줄이는게 맞다
하지만 불가능 하다면 싱글 쿼리를 여러개의 심플한 쿼리로 변경하는걸 겁내지말라
(그정도 효능이 있다면)

데이터를 잘게 쪼개 처리하기
한번에 너무 많은 데이터를 처리하면 문제가 생길 수 있다.
DELETE FROM messages WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH);

이렇게 말고 아래 처럼 처리하자

rows_affected = 0
do {
rows_affected = do_query(
"DELETE FROM messages WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH)
LIMIT 10000")
} while rows_affected > 0

Join Decomposition
성능을 위해 테이블에 싱글 쿼리를 날리고
조인을 어플리케이션 서버에서 하는 방법도 있다
이유
1.어플리케이션에서 캐쉬를 사용할 경우 큰 성능 효과를 기대할수있다
2.분리해서 사용할경우 lock 경쟁이 적어 질수 있다
3.DB를 확장할때 유리하다
4.MySql이 optimizer 를 사용할때 성능을 최적화 할 수 있다
5.디비에서 한번만 읽어오기 때문에 디비의 메모리 사용량과 네트워크 트래픽을 줄일수 있다
6.다른 확장 프로그램을 받는다면 nested loop join 이 아니라 hash join 으로 변경 할수 있다

MySQL이 쿼리를 처리하는 순서
1.클라이언트가 server에 sql문장을 쏜다
2.서버는 query cache 를 확인하고 존재한다면 cache에서 데이터 찾아 리턴
3.parse -> prepocess -> optimizes(sql to query execution plan)
4.query execution engine 이 플랜을 실행 (storage engine 의 API 콜)
5.결과를 클라이언트에 리턴

Mysql Clinet/Server Protocol
half-duplex 언제든 받거나 보내거나 둘중 하나를 할수있다 단 2개 동시에는 안된다.

문제
1.flow controll 할수없다
즉 클라에서 최초 몇줄이 필요하다고 해도 서버가 보내주는 모든 데이터를 받아야 한다.
why?
클라에서 서버로 쿼리를 던지는 순간 서버가 공을 가지게 된다.
그러면 클라는 서버에게 명령 할수 없다.
(통신 방법이 half-duplex니까)

2.서버가 클라로 데이터를 던질때 전체 데이터가 클라에 도착할때까지
쿼리에 필요했던 락과 리소스를 풀수 없다
(the query state is sending data)

3.클라 라이블러리를 설정하여
buffer를 사용할지 오는대로 처리할지 지정 할수 있지만(default buffer)
오는대로 처리하게 설정하면 server 와 clinet의 interact 가 끝날때가지 자원과 리솟스를
풀수 없다.

쿼리상태(query states) 값 설명
SHOW FULL PROCESSLIST .command 컬럼
Sleep
thread 가 클라에서 새로운 쿼리가 올때까지 대기
Query
쿼리를 실행 중이거나 클라한태 돌려주는중(server is sending data to client)
Locked
서버 level에서 table lock이 걸려서 thread가 대기하는 상태
(storage engine에서 직접 lock 을 거는 엔진의 상태는 표시 안됨(InnoDB 등)
Analyzeing and statistics
storage 엔진의 통계 를 확인해서 최적하는 하는중

Copying to tmp table[to disk]
group by , solt 등을 하기 위해 임시 테이블을 만들고 결과를
복사해 놓는 과정
[to disk 라고 적혀있다면 memory에서 -> disk 테이블에 쓴느중)


Sorting result
리절트를 솔팅하는 중

Sending data
서버에서 쿼리 실행 단계중에서 서로 데이터를 보내는것
또는 클라에게 보내는것

statistics 상태가 나온다면 서버 전체적으로 프로 파일링을 해봐야 한다. ㅠ_-

Query cache
1bit 라도 다르다면 매치 되지 않는다.
만약 매치 된다면 권한체크(파싱하지 않고)만하고 리턴

Query optimization process
서버 실행 계획 생성
= parsing -> preprocessing -> optimization

paring
sql을 트리로 만들면서 문법 체크

preprocessor
1.테이블, 컬럼 존재여부 애매한 컬럼등 조사
2.권한 조사

optimization
cost-based 사용
측정 기준
4KB page read

*SHOW STATUS LIKE 'Last_query_cost' 좀전에 실행된 쿼리의 코스트
964 random page 를 읽은거 같다는 뜻
1.(통계값임)
2.(캐쉬되서 리턴되는 값은 고려 안함 무저건 랜덤 I/O로 진행된다고 가정)

참조되는 값
테이블,인덱스 크기
cardinality(number of distinc values)
indexes
rows length , key length





최고의 플랜이 선택이 보장되지 않는 이유
1.통계가 틀릴경우
2.통계가 정확해도 예측값과 실제 실행 되는 자원의 값은 다르다
(mysql은 page가 메모리에 있는지 디스크에 있는지 알지 못한다.)
3.mysql은 최고로 빠른속도를 지향하지 않고 최소의 코스트를 지향한다.
4.해당 쿼리외에 동시에 실행 되는 다른 쿼리들이 영향을 줄수 있다
5.where 절에 특정 키워드가 있을 경우 최적화 안한다(FULLTEXT MATCH 등)
6.시스템 펑션이나, 유저 펑션등 mysql이 측정할수 없는애들도 있다
7.가능한 모든 플랜을 만들수 있는건 아니다. 즉 최적의 플랜을 못만들수도있다

최적화의 종류
static
parsing 할떄 where 절을 대수학의 동일 식으로 변경
dynamic
where절, 테이블 또는 인덱스의 로우 등에 따라 변경

*store procedure OR prepare statment를 실행 시킬떄
static 은 한번 하지만 dynamic늘 다시 한다.
최적화 할때 하는짓
reordering joins
테이블 조인 순서 변경
outer join to innerjoin
innerjoin으로 풀수 있다면 변경

필요없는 where 조건 정리
ex) where 5 = 5 and a>5 -> where a>5

COUNT(), MIN(), and MAX() optimizations
min일 떄 인덱스 오른쪽 끝값 읽음 등
해당 최적화가 이루어지면 아래처럼 표현됨
EXPLAIN plan : Select tables optimized away

커버링 인덱스

서브쿼리 최적화

Early termination
쿼리가 실행되는 도중에 멈출수 있다
LIMIT
DISTINCT, NOT EXISTS(), and LEFT JOIN

Equality propagation
SELECT film.film_id
-> FROM sakila.film
-> INNER JOIN sakila.film_actor USING(film_id)
-> WHERE film.film_id > 500;

film, film_actor에 둘다 film_id > 500을 적용한다.
*현재는 MySql만 지원하는 기능
IN
MySql에서는 In안의 벨류를 정렬시킨 후
바이너리 검색을 한다.
OR의 경우 O(n)
MySql 이 바이너리 검색을 할경우 O(log n)

옵티마이저는 똑똑하니 이길려고 싸우지 말고 옵티가 잘 못하는게 있음
그부분을 수정해라

*테이블, 인덱스의 통계정보는 storage engine이 가지고 있다!(서버가 아님!!)

MySql에서 join 이라는 단어는 범위가 넒음
모든쿼리를 뜻함(한테이블에 날리는 싱글 쿼리는 조인)

why join이라고 하는가

모든 쿼리를 아래같은(수도코드) 코드로 실행 하기 때문에

mysql> SELECT tbl1.col1, tbl2.col2
-> FROM tbl1 LEFT OUTER JOIN tbl2 USING(col3)
-> WHERE tbl1.col1 IN(5,6);

outer_iter = iterator over tbl1 where col1 IN(5,6)
outer_row = outer_iter.next
while outer_row
inner_iter = iterator over tbl2 where col3 = outer_row.col3
inner_row = inner_iter.next
if inner_row
while inner_row
output [ outer_row.col1, inner_row.col2 ]
inner_row = inner_iter.next
end
else
output [ outer_row.col1, NULL ]
end
outer_row = outer_iter.next
end

* inner it는 필러링이 됨으로 == 조건으로 무조건 밖에 숫자가 작은게 유리하다!
조인시 로우수가 적은 테이블이 outer 에 있어야한다.
즉 driving 되는 테이블이 로우 수가 적어야된다!

*서브 쿼리일경우
서브쿼리를 먼저 실행시키고 그걸 temp table로 만든후(그래서 derived table!)
다시 위 루틴을 적용 시킨다.
*right일 경우는 전부 left 쿼리로 바꿈
*full outer join 이 안되는 이유임

Mysql은 쿼리를 실행 시키기 위해서 byte-code를 만들지 않는다.
EXPLAIN EXTENDED SELECT * FROM sakila.film A inner join film_actor B using(film_id);
SHOW WARNINGS;

***** 을 하게되면 최종적으로 실행된 쿼리를 확인 할수 있따!!!
어떤 쿼리도 트리로 나타낼수 있다
Mysql은 무조건 테이블을 조인해 하나의 테이블로 만들고 그걸또 조인시킨다

ex)
select * from A, B, C, D where~
일경우
A,B JOIN->temp1 ,C JOIN-<temp2 ,D JOIN->result set

*옵티마이징에서 가장 중요한건 조인 순서를 정하는거다
STRAIGHT_JOIN 순서대로 조인 하라는 힌트절
SELECT STRAIGHT_JOIN * FROM A

n 테이블을 조인할 경우 plan은 n!이 나온다
즉.. 10개 만 조인 하면 362800이 나온다..-> 즉 느려진다.
optimizer_search_depth 로 조절 가능

옵티마이징 안되는 쿼리들
left join
subquery
결과가 나와야 조인을 시킬수 있음으로

sort optimization
메모리를쓰던 안쓰던 file sort 라고 표시된다

file sort 종류
Two passes (old)
1.row point와 order by 컬럼을 읽고
2.order by로 정렬하고
3.정렬된 순서대로 reread 한다.

느린이유
1.테이블에서 row 를 두번 읽는다
2.다시 읽을때 random I/O가 발생할 가능성이 높다
single pass (new)
1.쿼리에 필요한 모든 컬럼을 읽고
2.정렬하고
3.result 필요한 컬럼만 선택후 출력

단점
많은 데이터를 읽어 함으로
file에 쓰고 merge 하는 작업을 해야 한다.

MySql은 filesort 할때 많은 공간이 필요하다
why?
왜냐하면 정렬시 고정 된 길이의 레코드를 사용하기 때문에
1.가장큰 길이의 레코드에 의해 결정된다.
2.varchar의 경우 최대 길이로
3.utf8로 되었을 경우 무조건 3바이트를 할당함

그래서 템프 테이블은 실제 디스크에 저장된 테이블의 전체 용량보다
더큰 경우도 많다.

첫번재 테이블로 sort 가 결정될 경우 정렬후 조인 Using filesort
결과같으로 정렬할 경우 Using temporary Using filesort
*limte가 외부에 설정되었어도 만들고 하는거기때문에 별 쓸고가 없다

쿼리 실행 엔진
옵티마이징 끝나면 execution plan 이 나온다
그냥 이 plan 대로 storage engine 에 api를 호출한다.

returning results to the client
만약 캐쉬가 on 되어있으면 이때 적재

서버는 result 가 생성되면 첫줄부터 클라이언트로 보낸다(지속적으로)
why?
서버 메모리에 적재하지 않아 메모리 사용량을 줄일라고

mysql 옵티마이저의 한계
상관관계 서브쿼리
SELECT * FROM sakila.film
WHERE film_id IN(
SELECT film_id FROM sakila.film_actor WHERE actor_id = 1);

이렇게 풀릴거라고 예상한다.
SELECT * FROM sakila.film
WHERE film_id
IN(1,23,25,106,140,166,277,361,438,499,506,509,605,635,749,832,939,970,980);

한대 직접 SHOW WARNINGS로 보면
SELECT * FROM sakila.film
WHERE EXISTS(
SELECT * FROM sakila.film_actor WHERE actor_id = 1
AND film_actor.film_id = film.film_id);

이렇게 풀려있다 - -...
만약 film테이블이 크다면.. 답안나온다.

서브쿼리 변환 방법
DISTICT 나 group by 를 써야 한다면 EXIST 를 고려하자

SELECT DISTINCT film.film_id FROM sakila.film
INNER JOIN sakila.film_actor USING(film_id);

SELECT film.film_id FROM sakila.film
WHERE EXIST(SELECT * FROM sakila.film_actor
WHERE film.film_id = film_acotr.film_id)

Parallel Execution
하나의 쿼리를 여러개의 cpu 에서 병렬로 처리하는거 지원안함
Query Optimizer Hints
우선순위 변경
InnoDB등 락킹 잘관리하고 MVCC 하는애들에는 안쓰는게 좋음
MyISAM 에 조심
concurrent insert에 매우 않좋은 영향을 줄수 있음

HIGH_PRIORITY
해당 쿼리를 큐의 맨앞부분으로 이동(lock 우선순위 변경)

LOW_PRIORITY
해당 쿼리를 큐의 맨뒤부분으로 이동(lock 우선순위 변경)
기아 상태에 빠질수 있음

DELAYED
INSERT, REPLACE 문에 사용가능
클라에서 INSERT 오면 ok 응답 보내주고
BUFFER에 저장 후 테이블이 FREE 상태일때 INSERT

BULK INSERT를 위해 만들어짐
각각의 stmt에 I/O를 피할수 있음
*LAST_INSERTED_ID 사용 못함

STRAIGHT_JOIN
순서대로 조인하게 강제
*옵티가 오래걸릴(조인 테이블이 많을 경우)떄 사용하면 좋음

결과량
for SELECT, temp table, GFOUP BY, DISTINCT 할때
어떤 플랜을 짜면 좋은지 옵티에게 알림
SQL_SMALL_RESULT
작은 결과가 리턴될거임
SQL_BIG_RESULT
큰결과과 리턴될거임
그러니 디스크에 테이블을 만드삼
SQL_BUFFER_RESULT
쿼리 결과를 나오는대로 클라로 흘리는게 아니라
서버 메모리에 저장 후 결과 다 나오면 리턴
(즉 클라 메모리가 아닌 서버 메모리에 저장)
SQL_CACHE, SQL_NO_CACHE
cache에 적재 시킬래? 안시킬래
SQL_CALC_FOUND_ROWS
LIMIT 가 있어도 무저건 전체 로우를 사용
왠만하면 사용말자
FOR UPDATE and LOCK IN SHARE MODE
for SELECT
로우레벨 락킹을 지원하는 엔진에 쓸수있음
정말 잘 알지 못하고 사용하면 심각한 락 문제를 가져올수 있음

USE INDEX,  IGNORE INDEX and FORCE INDEX
오더에 INDEX 사용하기 위해
FOR ORDER BY, FOR GROUP BY 힌트 사용 가능

USE INDEX
왠만하면 인덱스 쓰렴
FORCE INDEX
무저건 INDEX 쓰렴

*아래 부터는 서버 옵션임
optimizer_search_depth
옵티가 만들수 있는 플랜들 조정(낮게 설정)
크게하면 가능 플랜 만드느라 느려짐
optimizer_prune_level
옵티는 테이블의 로우수에따라 특정 쿼리를 사용하지 않게 되어 있다
default enabled
optimizer_switch
인덱스 관련 상세 옵션
ex)
index merge query plan 쓸래 등

*힌트는 왠만하면 쓰지 말자
1.옵티는 겁나 많은 사람들이 만든거다 즉 똑똑하다
2.MySql 버전 upgrade할때 힌트가문제가 될수있다
*버전 업그래이드시 pt-upgrade 툴을 쓰면
해당 쿼리들이 새 버전에서도 문제 없이 동작하는지 확인 할 수 있다

특수 타입 쿼리 튜닝
*버전마다 방법이 다르다

COUNT Queries
카운트가 하는 종류
1.벨류들에서 NULL을 제외한 벨류를 센다
2.결과에서 로우 수를 샌다
COUNT(*)

*로우를 세고 싶다면 COUNT(*)을 사용(칼럼명 쓰지 말아라!)


MyISAM의 카운트가 빠르다?
노우
단 전체 로우수를 셀대만
where 절이 있으면 안됨

꼼수
정확한 숫자의 카운트가 필요없다면
explain에서 가져오는것도 방법
JOIN Queries
1.where 컬럼에 들어가 있는 애는 인덱스가 있는지 확인
2.group by, order by만 사용해라록 해라(\
그럼 메모리에서 할수 있다.

sub Queries
최대한 조인으로 변경해라

GROUP BY and DISTINCT
둘다 비슷한 기능을 하며 내부적으로 옵티 할때는 MySQL 내부에서 자주 바꿔쓴다.

SQL_BIG_RESULT and SQL_SMALL_RESULT를 사용해라

만약 LOOK up 테이블에서 벨류를 가져와 그값으로 조인해야 할때는
group by에 look up table 컬럼 을 쓰는게 더 좋은 방법이다.

만약 정렬할필요가 없는대 정렬이 일어 난다면
자동 정렬을 피하기 위해서
ORDER BY NULL 사용

WITH ROLLUP
원하지 않는 실행 계쇡이 나올때가 많다
가능하면 어플리케이션 코드로 해결하자

LIMIT and OFFSET
인덱스를 안타면 file sort하게 된다.

LIMIT 10000,200 하게되면 10200 로우를 가져와 10000 로우를 버린다
이럴때는 좀더 최적화한다.

1.deferred join 이라고 도 부른다.

SELECT film.film_id, film.description
-> FROM sakila.film
-> INNER JOIN (
-> SELECT film_id FROM sakila.film
-> ORDER BY title LIMIT 50, 5
-> ) AS lim USING(film_id);

2.미리 페이지 별로 인덱스를 만들고 인덱스를 조건으로 타는 방법도 있다

SELECT film_id, description FROM sakila.film
-> WHERE position BETWEEN 50 AND 54 ORDER BY position;

3.삭제가 되지 않는다면 AUTO_INCREMENT PK는 증가만 한다는걸 사용가능하다.

mysql> SELECT * FROM sakila.rental
-> ORDER BY rental_id DESC LIMIT 20;

This query returns rentals 16049 through 16030. The next query can continue from
that point:
mysql> SELECT * FROM sakila.rental
-> WHERE rental_id < 16030
-> ORDER BY rental_id DESC LIMIT 20;


*가장좋은건 아무리 깊이 페이징을 해도 별 영향이 없다

4.여분의 테이블을 만든다.
ORDER BY에서 필요한 컬럼들로만 만들어진
그후 원테이블이랑 조인 때린다.

5.클라쪽에서 페이지 크기보다 큰 로우를 받고 20개 보일때 1000개를 받음
그냥 보여주다가 1000개 넘어가면 요청하는거


*토탈 페이지수를 EXPLAIN에서 가져오는거(구글도 토탈 페이지를 ++ 표시)

UNION
**Mysql은 무조건 템프테이블 만든다.(결과를 순차적으로 클라로 안보낸다!)

UNION ALL을 써라!

UNION만 쓴다면 DISTICT 를 하게 되는대
템프테이블은 인덱스가 없기 때문에 전체 테이블에 대해서 유니크를 조사해야한다.

Static Query Analysis
pt-query-adviosr 를 사용해라
대략 나쁜 패턴 이라고 생각 되는 쿼리를 뽑아 낼수있다

Using User-Defined Variables P256
유저 정의 변수 == 컨넥션이 살아 있을때까지 살아 있음

RDB는 전체를 set으로 동시처리한다 하지만 유저 변수를 쓰면 순차적 처리도 가능하다

단점
*사용하면 쿼리 캐쉬 사용못한다.
*컨넥션당이다 inter컨넥션에서 안된다.
*컨낵션 풀링이나 영속적 연결 사용하면 문제 될수 있다
*옵티가 제거해버릴수도 있다
* := 의 할당 연산자는 우선순위가 가장 낮다

예제
유저 변수에 할당되는 순서는 순차적이지 않다
그러므로 할당 후 할당되야 되는 애들은 서브쿼리로 빼야된다.

mysql> SET @curr_cnt := 0, @prev_cnt := 0, @rank := 0;
-> SELECT actor_id,
-> @curr_cnt := cnt AS cnt,
-> @rank := IF(@prev_cnt <> @curr_cnt, @rank + 1, @rank) AS rank,
-> @prev_cnt := @curr_cnt AS dummy
-> FROM (
-> SELECT actor_id, COUNT(*) AS cnt
-> FROM sakila.film_actor
-> GROUP BY actor_id
-> ORDER BY cnt DESC
-> LIMIT 10
-> ) as der;

조금전에 변경된 로우의 값을 리턴한때 사용가능(UPDATE RETURNING)
UPDATE t1 SET lastUpdated = NOW() WHERE id = 1 AND @now := NOW();
SELECT @now;

INSERT ON DUPLICATE KEY UPDATE 할때 몇개의 로우가 듑에러가 나서 업데이트 되었는가?
INSERT INTO t1(c1, c2) VALUES(4, 4), (2, 1), (3, 1)
ON DUPLICATE KEY UPDATE
c1 = VALUES(c1) + ( 0 * ( @x := @x +1 ) );

*벨류는 업데이트 되지만 @x에 더하고 0을 곱하므로 카운트는 올라가고 실제 값은 변하지 않는다.


유저변수는 적용되는 부분에 따라 다르게 배치된다. where 절 걸리고 select 되고  order by걸리는것처럼
*안되는거 볼래면 EXPLAIN 의 EXtra 컬럼을 확인해라
동작안함

mysql> SET @rownum := 0;
mysql> SELECT actor_id, @rownum := @rownum + 1 AS cnt
-> FROM sakila.actor
-> WHERE @rownum <= 1
-> ORDER BY first_name;

동작됨
mysql> SET @rownum := 0;
mysql> SELECT actor_id, @rownum AS rownum
-> FROM sakila.actor
-> WHERE (@rownum := @rownum + 1) <= 1;

이런방식으로 hack해서 사용하기도 한다.
mysql> SET @rownum := 0;
mysql> SELECT actor_id, first_name, @rownum AS rownum
-> FROM sakila.actor
-> WHERE @rownum <= 1
-> ORDER BY first_name, LEAST(0, @rownum := @rownum + 1);

GREATEST(), LENGTH(), ISNULL(), NULLIF(), IF(), COALESCE() 를 사용할수있다
Building a Queue Table in MySQL
queue table을 하나만 만들면 시간이 지날쓰록 쓰기 힘들다
그렇기 히스토리 테이블을 따로 만들자

워커 프로세스가 쉴때
SELECT /* waiting on unsent_emails */ SLEEP(10000);
이렇게 하고 process list 에서 커먼트가 있음 죽인다.

이메일을 보내는 큐테이블을 만들어 보자~
CREATE TABLE unsent_emails (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
-- columns for the message, from, to, subject, etc.
status ENUM('unsent', 'claimed', 'sent'),
owner INT UNSIGNED NOT NULL DEFAULT 0,
ts TIMESTAMP,
KEY (owner, status, ts)
);

owner = connection process id임 (CONNECTION_ID()값이 들어감)
0이면 자기꺼라고 주장한애가 없는것

내일임 주장하기(락최소화)

SET AUTOCOMMIT = 1;
COMMIT;
UPDATE unsent_emails
SET status = 'claimed', owner = CONNECTION_ID()
WHERE owner = 0 AND status = 'unsent'
LIMIT 10;
SET AUTOCOMMIT = 0;


SELECT id FROM unsent_emails
WHERE owner = CONNECTION_ID() AND status = 'claimed';
-- result: 123, 456, 789

*SELECT FOR UPDATE 왠만하면 쓰지 말고 위에 꺼처럼 써라

문제가 생겨 워커가 주장만 하고 처리 못한거 처리하기
SHOW PROCESSLIST
현재 일 처리하는 애들의 id얻어와서

UPDATE unsent_emails
SET owner = 0, status = 'unsent'
WHERE owner NOT IN(0, 10, 20, 30) AND status = 'claimed'
AND ts < CURRENT_TIMESTAMP - INTERVAL 10 MINUTE;

문제생긴놈꺼 풀어주기

*인덱스에 주목해라 (owner, status, ts)
ts 가 마지막에 있기때문에 range 까지 쓸수 있다

Computing the Distance Between Points
P258
MySQL에서 거리 계산하기

Using User-Defined Functions
C나 C++에 아주 익숙하다면 펑션을 만들어서 써도 된다.