2018년 11월 26일 월요일

마이바티스 - mybatis, mybatis-spring

마이바티스만 쓸거면 mybatis를 보고,
스프링에 마이바티스를 연동해 쓸거면 mybatis-spring를 보고,
스프링-부트에 마이바티스를 연동해 쓸거면 mybatis-spring-boot-starter를 보자.

하이버네이트 같은 ORM 방식이 나은지 마이바티스 같은 데이터 매퍼 방식이 나은지는 그때 그때 다르겠다.
꼰대 DBA가 없으면서 심플한 데이터 관계를 갖는 New 프로젝트에선 ORM 방식이 나은 것 같고,
DBA를 설득하기 빡세고, 레가시 도메인이 복잡하면 그냥 마이바티스 쓰는게 최선인 것 같다.


마이바티스 주요 클래스 인스턴스의 스코프와 생명주기

마이바티스의 주요 클래스로 SqlSessionFactoryBuilder, SqlSessionFactory, SqlSession이 있다.
다행인것은 DI 프레임워크가 생명주기에 대해 기억하지 않아도 쓰레드로 부터 안전하게 동작하도록 관리해준다는 것이다.

1) SqlSessionFactoryBuilder (SqlSessionFactoryBean); SqlSessionFactory를 생성한 후 유지될 필요가 없으므로 메소드 스코프.
2) SqlSessionFactory; 한번 생성한뒤 SqlSessionFactory는 애플리케이션이 실행되는 동안 계속 존재하는 것이 좋다. 따라서 애플리케이션 스코프. 가장 간단한 방법은 싱글턴 패턴이나 static 싱글턴 패턴을 사용하는 것인데, 스프링과 같은 DI 컨테이너를 사용하는 것이 일반적이다.
3) SqlSession (SqlSessionTemplate); 각자의 쓰레드는 자체적으로 SqlSession 인스턴스를 가져야 한다. SqlSession 인스턴스는 공유되지 않고 쓰레드에 안전하지도 않기 때문이다. 따라서 요청 단위 또는 메소드 스코프.


mybatis

1) 마이바티스 애플리케이션은 SqlSessionFactoryBuilder로 SqlSessionFactory 인스턴스를 만들어 사용한다.
2) SqlSessionFactory는 SqlSession을 만드는 역할을 한다.
3) SqlSession은 데이터베이스에 대해 SQL 명령어를 실행하기 위해 필요한 모든 메서드를 가지고 있다.


mybatis-spring

1) SqlSessionFactoryBuilder 대신 SqlSessionFactoryBean이 SqlSessionFactory를 만들어 준다. SqlSessionFactory는 DataSource를 필요로 하고, 다른 스프링의 데이터베이스 연결과 동일한 형식으로 DataSource가 설정되어야 한다. 매퍼의 위치는 SqlSessionFactoryBean에 mapperLocations 프로퍼티를 이용해 지정한다.

<!-- sample.config.mappers 패키지 아래와 그 하위 패키지를 모두 검색해서 마이바티스 매퍼 XML 파일을 모두 로드한다. -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath*:sample/config/mappers/**/*.xml"/>
</bean>

2) mybatis-spring에선 SqlSessionFactory를 직접 사용할 필요가 없다. SqlSessionTemplate은 SqlSession을 구현하고 코드에서 SqlSession을 대체하는 역할을 한다. SqlSessionTemplate은 쓰레드에 안전하고 여러개의 DAO나 매퍼에서 공유될 수 있다. 아래의 빈은 DAO에 직접 주입될 수 있다. SqlSessionTemplate은 인자로 ExecutorType을 갖는 생성자를 가지고 있는데 실행 방식을 아래와 같이 배치 형태로 바꿀 수도 있다.

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory"/>
    <constructor-arg index="1" value="BATCH"/>
</bean>

3) SqlSession을 제공하는 추상클래스인 SqlSessionDaoSupport가 있고, MapperFactoryBean 방식으로 DAO를 다룰 수도 있다. 이들은 추가적으로 작성되는 코드를 줄이거나 없애는 역할을 한다.

출처: http://www.mybatis.org/spring/ko/index.html

2018년 11월 15일 목요일

WPF - 빈 버튼 만들기, DataGrid Column 좌측 정렬하기.

1. 빈 버튼 만들기
아래처럼 버튼의 Template을 Rectangle이나 TextBlock 같은 것으로 덮어버리면 지정된 효과를 제거할 수 있다.

<Button>
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Rectangle Fill="Red"
                       Width="{TemplateBinding Width}"
                       Height="{TemplateBinding Width}"/>
            <!-- TextBlock Text="{TemplateBinding Content}"/> -->
        </ControlTemplate>
    </Button.Template>
</Button>

최소한의 효과를 갖게하려면 스타일로 ToolBar.ButtonStyleKey를 지정하면 된다.

<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Background="Blue"/>

2. DataGrid Column 좌측 정렬.
DataGridTemplateColumn으로 처리하면 되는데 정석은 아닌 것 같지만 동작한다. :(
CellTemplate에 왜 Top Padding이 필요한지는 잘 모르겠다.
디자이너가 없어서 일하기 힘들다..

<DataGridTemplateColumn IsReadOnly="True" CanUserSort="False">
    <DataGridTemplateColumn.HeaderTemplate>
        <DataTemplate>
            <TextBlock Text="Header" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </DataTemplate>
    </DataGridTemplateColumn.HeaderTemplate>
    <DataGridTemplateColumn.CellStyle>
        <Style TargetType="DataGridCell">
            <Style.Setters>
                <Setter Property="TextBox.BorderBrush" Value="Transparent"/>
                <Setter Property="TextBox.Background" Value="{x:Null}"/>
            </Style.Setters>
        </Style>
    </DataGridTemplateColumn.CellStyle>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Padding="10, 5, 0, 0" Text="{Binding CellData}" HorizontalAlignment="Left"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

2018년 11월 12일 월요일

Java의 NIO (New I/O)

IO : NIO = Stream : Channel

Java의 기존 I/O는 느렸다. 다행히 어렵진 않았다. Buffered 계열의 클래스를 이용해 쉽게 I/O 작업을 할 수 있었다.
 - FileReader / FileWriter
 - PrintReader / PrintWriter

NIO 이전의 I/O는 Stream 기반으로 input/output 스트림을 구분하는 방식이다. 읽기를 수행할 땐 InputStream을 사용해야 하고, 쓰기를 수행할 땐 OutputStream로 구분해서 사용해야 한다. 그리고 읽기/쓰기가 끝나야 return 되는 blocking 방식이다.

기존의 I/O 방식이 느렸던 까닭은 커널 버퍼에 직접 접근하는 Direct Buffer를 핸들링할 수 없었기 때문이다. 이로 인해 커널에서 JVM 내부로 Buffer를 복사할 때의 CPU 소모, 복사한 Buffer에 대한 GC 등의 오버헤드가 발생하였다.

JDK 1.4부터 NIO가 생겼다. NIO는 New I/O의 줄임말이다. (Non-blocking I/O를 뜻하는 것이 아니다.) 기존의 I/O는 Blocking방식으로, 해당 블럭이 끝나기 전에는 아무 것도 수행할 수 없었다. 왜 1.4 이전엔 이런게 없었을까? 멀티 플랫폼을 지향하는 자바 특성상 각 OS의 System Call이나 커널에 맞춰 Non-blocking I/O를 지원하기 어려웠을 것이다.

NIO가 되면서 커널의 Direct Buffer에 접근할 수 있는 클래스가 생겼다. Buffer는 시스템 메모리를 직접 사용할 수 있는 클래스로 기본 데이터 타입을 저장할 수 있다. 형태는 배열과 유사하고 제한된 크기에 순서대로 데이터를 저장한다. Buffer에는 read/write 위치에 대한 position, read/write 최대 값인 limit, 그리고 크기에 해당하는 capacity 등의 속성이 있다. 메서드로는 rewind(), flip(), compact(), slice()등이 있다.

Buffer 자체는 추상 클래스이며 이에 대한 Concrete 클래스로
 - ByteBuffer
 - MappedByteBuffer
 - CharBuffer
 - DoubleBuffer
 - FloatBuffer
 - IntBuffer
 - LongBuffer
 - ShortBuffer
가 있다. 이 중 ByteBuffer는 시스템 메모리를 직접 사용하는 Direct Buffer를 만들 수 있는 유일한 Buffer이다.

NIO에서는 기존 I/O 방식과 다르게 Channel이라는 것을 통해 양방향 읽기/쓰기를 동시에 할 수 있게 지원한다. Channel도 Stream과 마찬가지로 데이터가 흘러다니는 통로지만 Stream과 달리 input/output을 구분할 필요가 없다.

Channel은 Buffer를 통해서만 데이터를 읽거나 쓸 수 있다. 또 하나의 특징으로 Channel은 Non-blocking operation도 지원한다. NIO의 일부가 Non-blocking을 지원한다는 것이지 NIO의 모든 동작이 Non-blocking 방식으로 동작하는 것은 아니다. Blocking 모드로 동작하더라도 성능상 유리한 System Call을 활용하기 때문에 기존의 Stream I/O에서 병목을 유발했던 몇가지 레이어를 건너뛸 수 있다. 그리고 NIO에서는 Selector라는 것을 통해 모든 연결을 중앙집중식으로 관리하게 된다. 이벤트를 Selector가 감지하고, 이벤트가 발생한 I/O와 관련된 Channel을 불러와 작업을 수행하는 방식이다.

2018년 11월 7일 수요일

엘라스틱서치 로그스태쉬(Logstash)

데이터 흐름을 위한 오픈 소스 중앙 처리 엔진.
데이터 Flow 파이프라인을 구축. 이벤트 데이터의 변환 및 스트림을 설정.

# 일반 구성.
beats - lightweight / edge shippers
logstash - central / dataflow engine
ealsticsearch

# 주 기능.
1. 데이터 스트림의 실시간 변형, 제거 및 정규화
2. 조건을 이용한 데이터 흐름 분할 및 라우팅
3. 파이프라인 성능 및 활성 상태 모니터링.

# 일반적인 흐름.
input > filter > output

1. input - files, syslog, sql queries, http requests, elasticsearch, beats, metric systems, and more..
2. filter (데이터에 변형을 가함)
 - log parsing
 - 데이터 확장
 - 태그 추가. and more..
3. output - elasticsearch, data 보관소 (amazon), alert & monitoring system, and more.. # 코덱 지정 가능함. (ex. rubydebug)

# filter example.
grok {
  match => { "message" => "Hello %{WORD:name}" }
...
date {
  match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
  remove_field => "timestamp"
...
geoip {
  source => "clientip"
  field => ["city_name", "country_name", "location", "region_name"]
..

엘라스틱서치 샤드.

메모했던 내용을 옮겨 적어본다. 엘라스틱서치는 홀로 외로이 실무에 도입해보려 했다가 접었..


1. 엘라스틱서치에서 인덱스는 한 개 혹은 여러 개의 샤드로 구성된다.

2. 샤드는 루씬 인덱스, 엘라스틱서치가 데이터를 클러스터 내에 분산 저장하기 위한 단위다.

3. 샤드는 결국 클러스터 내에 인덱싱을 하거나 데이터 일부를 조회하기 위한 독립적인 검색 엔진의 역할을 갖는다.

4. 샤드는 한개 혹은 여러 개의 세그먼트로 구성되고, 세그먼트 개수가 많아지면 주기적으로 더 큰 세그먼트로 병합된다. 병합 작업은 리소스에 민감하며 디스크 I/O에 큰 영향을 받는다.

5. 매우 큰 샤드를 만드는 것은 피하자. 실 사용 관점에서 50GB를 넘기지 말자.

6. 데이터 보관 주기를 관리하기 위하여 가능한한 시간 기반 인덱스를 사용하자. 보관 주기에 따라 데이터를 그룹핑하여 인덱스를 저장하자. 시간 기반 인덱스는 프라이머리 샤드와 리플리카의 개수 조정을 쉽게 해주며, 후속 인덱스 생성시 이를 쉽게 변경할 수도 있다. 또한 데이터의 볼륨과 요구사항을 쉽게 반영할 수도 있게 한다.

7. 작은 샤드는 작은 세그먼트를 만들며 부하를 증가시킨다. 평균 샤드 크기를 수 GB와 수십 GB 사이를 유지하는 것이 권장된다. 시간 기반 데이터를 사용한 사례를 보면 20GB ~ 40GB 정도의 사이즈가 적당했다.

8. 각 샤드의 부하는 세그먼트 개수와 크기에 따라 결정된다. 피크 시간을 피해 인덱스에 더 이상 데이터가 입력되지 않으면 forcemerge 기능을 사용해 큰 세그먼트로 병합시키는 것이 권장된다.

9. 하나의 노드에 설정된 힙은 1GB당 20~25개 샤드가 적당했다.

10. 쿼리에서, 샤드당 단일 쓰레드가 쿼리를 실행한다. 많은 개수의 작은 샤드를 조회하면 각 샤드마다 처리 속도는 빨라지지만 더 많은 작업을 큐에 넣고 순서대로 처리해야 하므로, 적은 개수의 큰 샤드를 검색하는 것보다 반드시 빠르다고 보장할 수 없다. 결론적으로 1개 샤드의 권장하는 크기를 넘지 않는 선이라면, 작은 크기의 여러 샤드보다는 큰 크기의 적은 샤드가 더 효율적이다.

10. 시간 기반 인덱스에서, 데이터 볼륨에 맞게 일 단위, 주 단위, 월 단위 인덱스를 고려하자. 이는 시간이 지남에 따라 클러스터에 저장하는 인덱스와 샤드의 개수를 적게 유지하는데 도움이 된다.

11. 데이터 인덱싱량이 빈번하게 변하는 경우라면 rollover, shrink api를 활용해서 유연하게 대응할 수 있도록 하자. rollover 인덱스 api는 클러스터가 저장해야 하는 도큐먼트와 인덱스의 개수를 지정하거나, 저장해야할 도큐먼트의 최대 기간을 지정할 수 있다.

12. shrink api는 기존 인덱스를 더 적은 개수의 프라이머리 샤드를 가진 신규 인덱스로 shrink하게 해준다.

윈도우 10 (Windows 10) 프로그램 경로

프로그램 경로는
%ProgramData%\Microsoft\Windows\Start Menu\Programs
%AppData%\Microsoft\Windows\Start Menu\Programs

시작 프로그램 경로는
%ProgramData%\Microsoft\Windows\Start Menu\Programs\StartUp
%AppData%\Microsoft\Windows\Start Menu\Programs\Startup