For an EL6-based system, the command is in the form of:
shell> sudo yum localinstall mysql-community-release-el6-{version-number}
.noarch.rpm
For an EL6-based system, the command is in the form of:
shell> sudo yum localinstall mysql-community-release-el6-{version-number}
.noarch.rpm
글로벌 서비스 준비중인데, 모든 시간을 KST 로 작업하고 있었다는..
그래서 모두 UTC 기준으로 변경을 하고 싶어서 프로그래머들끼리 가볍게 이야기를 나눠봤다.
여기서 전제조건!
1. 퍼블리셔는 OS 시간을 변경해줄 수 없음
2. 기존 Code 의 변경은 최소화 (로직을 변경하면서 버그를 만들 수 있는 상황이 아님)
우선, 기준이 되는 UTC 를 사용하려면, DB 에서 쓰고있던 Procedure base logic 들도
CURRENT_TIMESTAMP() -> UTC_TIMESTAMP() 로 변경 (1) 이 필요했고,
Java Code 에서 Calendar, Date, Timestamp 를 쓰는 코드를 모두 점검 (2) 해야 했다.
(1) 은 replace 가 용이한 상황이어서 난이도 하하하
(2) 는 일일이 봐야해서 난이도가 높다고 생각했지만 생각보다 코드가 많지 않아서 중하하
라고 판단함
시간이 그리 많지 않아서 신속하게 결정하고, 아래와 같이 진행했다.
1. TimeZone 기반으로 Calendar 를 하나의 함수를 통해 얻어오도록 수정
기존에는 Calendar.getInstance() 를 여기저기서 호출했는데,
특정 날짜관련 클래스를 하나 만들고 거기서 getNow() 를 통해 Calendar 를 얻어가도록 수정
2. DB 의 모든 CURRENT_TIMESTAMP() 를 UTC_TIMESTAMP() 로 수정
기존에는 CURRENT_TIMESTAMP() 를 사용하고 있어서
MySQL 의 서버 시간 설정에 의한 시간값을 얻어가고 있었는데
UTC_TIMESTAMP() 를 이용함으로써, 서버 시간설정에 영향을 받지 않도록 수정
그 후, 원하는대로 값이 저장되는지 확인해봤다.
그런데, 좀 이상했다..
분명, WAS Code 에서 2015-07-01 00:00:00 을 얻었는데,
DB 에 2015-07-01 09:00:00 이 저장됨
그리고 WAS Code 에서 그 값을 읽어서 클라이언트로 전달하면서
클라이언트는 그 시간이 UTC 라고 믿고 기기가 KST 를 표시해야 하니, +09:00 를 추가로 함 -_-
서버가 클라로 줘야하는 시간이 UTC 로 약속되어있기 때문에,
서버에서 DB 의 값을 읽을 때 UTC 로 변환해야할 필요가 있는것 처럼 느껴졌었는데,
Calendar 를 얻어가는 함수 내부에서 TimeZone 을 UTC 로 설정하고 있기 때문에
코드상의 문제는 아닌것 같았고, 좀더 debugging 해서 결국 문제점을 찾았다.
핵심 문제는 Timestamp 를 사용해서 DB 쪽으로 인자를 전달하는 과정에서
Timestamp 값이 Calendar의 TimeZone 에 의해 변경되지 않고
JVM의 TimeZone 에 의해 결정된 시간으로 저장되는 것을 몰랐던 것이 문제였다.
깊이 몰랐던 것이 문제여서, 이번 기회에 정리를 해두려고 한다.
Calendar 는 setTimeZone 에 의해 설정된대로 충실히 동작한다.
예를 들어, 2015-07-01 00:00:00 KST 인 값도 TimeZone 만 변경하면
get(Calendar.HOUR_OF_DAY) 를 했을때 설정된 TimeZone 에 의한 값을 return 한다.
set(Calendar.HOUR_OF_DAY, hour) 도, 내부적으로 설정된 TimeZone 에 의한 시간으로 set 한다.
그래서 현재 Calendar 가 무슨 TimeZone 을 갖는지 확인하고,
비교하는 데이터가 동일한 TimeZone 의 값인지 조심해야 한다.
Date 는 TimeZone 과 Independent 하다.
Calendar.getInstance().getTime() 의 Date 는, 내부적으로 fastTime 을 갖고 있고,
이 값이 TimeZone 에 의해 해석되는 형식이다.
여담인데, CallableStatement 의 인자를 setDate 로 넘기면, 날짜만 넘긴다.. -_-
MySQL 에도 TimeZone 이 엄연히 존재하지만,
JDBC 의 setTimestamp 를 통해 전달된 인자를 변경 없이 충실히 저장해주기 때문에
MySQL 내부에서 UTC_TIMESTAMP() 를 사용하면, 사실상 TimeZone Independent 한 작업을 할 수 있을 것 같다.
여하한 이유로 OS 의 시간을 변경할 수 없다면,
JVM 에서 UTC 를 사용하고, MySQL 에서 UTC_TIMESTAMP() 를 사용하는게 좋은 것 같다.
만약, JVM 에서 UTC 를 설정하기조차 할 수 없었다면,
너무 슬펐을 것 같다. -_-
마지막으로, Tomcat6 에서 TimeZone 설정 변경법
/etc/tomcat6/tomcat6.conf ... # Cannot resolve user database reference - naming-factory-dbcp # the real problem is a cnfe that is avoided by configuring # the -Djavax.sql.DataSource.Factory. This fixes the rpm install. JAVA_OPTS="${JAVA_OPTS} -Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourceFactory -Duser.timezone=GMT” ... |
__author__ = 'novice'
import pymysql
host_addr = '아이피'
user_acc = '디비 계정'
passwd = '비번'
db_name = '데이터베이스'
conn = pymysql.connect(host=host_addr, user=user_acc, passwd=passwd, db=db_name)
cur = conn.cursor(pymysql.cursors.DictCursor)
cur.execute('select * from information_schema.tables where table_schema = %s', db_name)
for row in cur:
table_name = row['TABLE_NAME']
print('-------------------------------------------------------------------------------------')
print('TABLE NAME : ', table_name)
print('-------------------------------------------------------------------------------------')
t_cur = conn.cursor(pymysql.cursors.DictCursor)
t_cur.execute('select * from information_schema.columns where table_schema = %s and table_name = %s',
(db_name, table_name))
for columns in t_cur:
print(columns['COLUMN_NAME'], '\t', columns['COLUMN_TYPE'], )
print('-------------------------------------------------------------------------------------')
print()
t_cur.close()
cur.close()
conn.close()
library(RMySQL)
# connect to db
conn <- dbConnect(MySQL(), user='', password='', dbname='', host='')
# select tidy data for stage funnel from stats log db
rs <- dbSendQuery(conn, "select cid, max(cd6) as field, 'lose' as result from _stats_log
where ts > '2015-01-01 00:00:00'
and ea like '%lose%'
and el = 'player'
and ec = 'combat'
group by cid
union all
select cid, max(cd6), 'win' as result from _stats_log
where ts > '2015-01-01 00:00:00'
and ea like '%win%'
and el = 'player'
and ec = 'combat'
group by cid;
")
# fetch result set
table <- fetch(rs, -1)
# clear db resources
dbClearResult(rs)
dbDisconnect(conn)
# result and field are factor
table$result <- as.factor(table$result)
table$field <- as.factor(table$field)
# show summary information
summary(table)
# draw result
library(ggplot2)
p <- ggplot(table, aes(field, fill=result))
p + geom_bar(position="dodge")
R 에서 의사결정나무 (Decision Tree) 를 간단하게 그려봤다
g1_grade, g1_type 의 값에 따라 succeeded 가 결정된다고 가정하고
이를 위한 예제용 데이터를 만들어봤다
의사결정 나무를 그리는 코드는 여기를 참고해서 만들어봤음 : http://www.statmethods.net/advstats/cart.html
코드 예제에서 rpart method=“anova” 로 되어있는데, 그렇게 그렸더니 원하는 결과가 나오지 않아서
박장시님 도움으로 rpart method=“class” 라고 써야한다는 것과 rattle package 에 대해 배움! - 장시님, 감사 (_ _)
# install prerequisite packages
# 이건 처음에 한번만 하면 됨
install.packages("rpart")
install.packages("rpart.plot")
install.packages("rattle")
# load library of rpart
library(rpart)
library(rpart.plot)
library(rattle)
# make sample data
df <- data.frame(g1_grade=3, g1_type=1, succeeded=1, seq=1:1000)
df <- rbind(df, data.frame(g1_grade=3, g1_type=3, succeeded=0, seq=1:800))
df <- rbind(df, data.frame(g1_grade=3, g1_type=2, succeeded=0, seq=1:900))
df <- rbind(df, data.frame(g1_grade=3, g1_type=1, succeeded=0, seq=1:100))
df <- rbind(df, data.frame(g1_grade=2, g1_type=1, succeeded=1, seq=1:100))
df <- rbind(df, data.frame(g1_grade=2, g1_type=1, succeeded=0, seq=1:800))
df <- rbind(df, data.frame(g1_grade=2, g1_type=2, succeeded=0, seq=1:800))
df <- rbind(df, data.frame(g1_grade=2, g1_type=2, succeeded=1, seq=1:100))
df <- rbind(df, data.frame(g1_grade=2, g1_type=3, succeeded=1, seq=1:200))
df <- rbind(df, data.frame(g1_grade=2, g1_type=3, succeeded=0, seq=1:900))
# show structure of df
str(df)
'data.frame': 5700 obs. of 4 variables:
$ g1_grade : num 3 3 3 3 3 3 3 3 3 3 ...
$ g1_type : num 1 1 1 1 1 1 1 1 1 1 ...
$ succeeded: num 1 1 1 1 1 1 1 1 1 1 ...
$ seq : int 1 2 3 4 5 6 7 8 9 10 ...
# make decision tree
tree <- rpart(succeeded ~ g1_type + g1_grade, data = df,
method = "class", control = rpart.control(minsplit = 10))
# draw it with just simple plot!
plot(tree)
text(tree, use.n=TRUE, all=TRUE, cex=.8)
# check working directory
getwd()
# create post script file to export tree to pdf
post(tree, file = "tree.ps", title = "How to success")
# draw it with fancy tree!
fancyRpartPlot(tree)
그림들은 모두 같은 그림이고 포장만 좀 다르다.
fancyRpartPlot 이 가장 보기 좋다!
데이터를 해석해보자면,
* 전체 데이터 중 75% 는 succeeded = 0, 25% 는 succeeded = 1
이게 조금 헛갈릴 수 있는데, 각 노드의 두번째 줄에 표시된 값이
평가 대상 변수의 분포 비율이라고 보면 됨
* 1번 -> 2번 : 65% 가 넘어갔음. 이 데이터는 g1_type = {2, 3}.
succeeded 분포는 0 이 92%, 1 이 8% 로,
이를 통해 g1_type 이 2, 3 인 데이터는 succeeded = 1 이 될 확률이 8% 밖에 안된다고 볼 수 있음
* 1번 -> 3번 : 35% 가 넘어갔음. g1_type = 1
succeeded 분포는 0 이 45%, 1 이 55% 로,
g1_grade 기준으로 분기가 또 이루어짐
* 3번 -> 6번 : 3번 조건인 데이터 중 (전체의 35%),
전체의 16% 에 해당하는 데이터가 g1_grade < 2.5
이를 통해 g1_type = 1 && g1_grade < 2.5 인 경우 succeeded = 1 이 될 확률이 11%
* 3번 -> 7번 : 3번 조건인 데이터 중 (전체의 35%),
전체의 19% 에 해당하는 데이터가 g1_grade >= 2.5
이를 통해 g1_type = 1 && g1_grade >= 2.5 인 경우 succeeded = 1 이 될 확률이 91%
목적상 succeeded = 1 을 원한다면,
데이터의 g1_type = 1 로 만드는 것이 가장 큰 도움이 되고, 그 다음으로 g1_grade 를 2.5 이상으로 만들어야 함
의사결정 나무에 대해 살짝 알아봤는데,
코드 작성 없이 데이터 만으로 의사결정 나무를 만들어주고 시각화 해주는 R 이 매력적으로 느껴짐!
# load libraries for access and draw
library ("DBI")
library ("RMySQL")
library ("ggplot2")
# connect to database server
conn = dbConnect(MySQL(), user=’user', password=‘password', dbname=‘dbname', host=‘host')
# get table list
dbListTables(conn)
# query table content
rs = dbSendQuery(conn, "SELECT * FROM table")
# fetch and make data frame from ResultSet
table = fetch(rs)
# get column names
names(table)
# prepare plot for drawing
d = ggplot(table, aes(factor(column)))
# draw simple bar
d + geom_bar()
# release ResultSet resource
dbClearResult(rs)
# release DB Connection resource
dbDisconnect(conn)
# load library
library ("ggplot2")
# set data frame with x and y
# y is random from 10 to 20
d = data.frame(x=1:100, y=runif(100, min=10, max=20))
# sort data of y
d$y = sort(d$y)
# draw step
p = ggplot(d, aes(x, y))
p + geom_step()
- Download & Install R
R Homepage
http://www.r-project.org/
서울대 mirror
http://healthstat.snu.ac.kr/CRAN/
- R Studio Download
http://www.rstudio.com/products/rstudio/download/
R Studio 가 참 훌륭한것 같음!
- 시각화 Plugin (ggplot)
http://docs.ggplot2.org/current/
- install ggplot
> install.packages("ggplot2")
- 처음에 실행해서 ggplot2 를 설치하려고 하면
> tar: Failed to set default locale
뭐 이런 메시지 나오는데, Mac Terminal 에
$ defaults write org.R-project.R force.LANG en_US.UTF-8
http://davidprakash.blogspot.kr/2011/05/r-error-tar-failed-to-set-default.html
이렇게 하고 재시작 하면 됨..
- 간단한 그래프 그려보기
> x = 1:1000
> y = x^2
> plot(x, y)
void mongo_bson_test_time_t( mongo& m )
{
// > db.foo.find()
// { "_id" : ObjectId("4f26266439584a7aa0bfce4e"),
// "time" : ISODate("2012-01-30T05:11:00.594Z") }
//
// time 의 값을 time_t 로 읽어서 출력하기
mongo_cursor cursor;
mongo_cursor_init( &cursor, &m, "test.foo" ); // collection 을 test.foo 로 지정
while( mongo_cursor_next( &cursor ) == MONGO_OK )
{
bson_iterator iterator;
// time 필드의 값 읽어오기
if ( bson_find( &iterator, mongo_cursor_bson( &cursor ), "time" ) )
{
time_t t = bson_iterator_time_t( &iterator );
printf( "time: %s\n", ctime( &t ) );
}
}
mongo_cursor_destroy( &cursor );
}