Log/TIL

230928

재영(ReO) 2023. 9. 29. 13:51
due on or before today
short mode
not done

🍒 오늘 공부한 내용

커넥션 풀링 (Connection Pooling)
DataSource 객체를 통해 미리 커넥션을 만들어 두는 것
새로운 커넥션을 생성하는 것이 비용이 많이 들어서 미리 만들어두면 성능상 이점이 있다.
(쓰레드 풀에 미리 쓰레드 만들어 놓는 것과 같은 이점)

  1. 리소스 관리
  2. 쓰레드 재사용
  3. 제어 가능한 병렬처리
  4. 부하 분산

커넥션 풀링에 미리 만들어둔 커넥션은 재사용 가능

Connection Pooling and Statement Pooling

package connectionpool;  
  
import com.mysql.cj.jdbc.MysqlDataSource;  
import com.zaxxer.hikari.HikariConfig;  
import com.zaxxer.hikari.HikariDataSource;  
import com.zaxxer.hikari.util.ClockSource;  
import org.junit.jupiter.api.AfterAll;  
import org.junit.jupiter.api.BeforeAll;  
import org.junit.jupiter.api.Test;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.testcontainers.containers.MySQLContainer;  
import org.testcontainers.utility.DockerImageName;  
  
import javax.sql.DataSource;  
import java.sql.Connection;  
import java.sql.ResultSet;  
import java.sql.SQLException;  
import java.sql.Statement;  
  
/**  
* pooling을 사용한 경우와 사용하지 않은 경우 트래픽이 얼마나 차이나는지 확인해보자.  
*  
* network bandwidth capture  
* 터미널에 iftop를 설치하고 아래 명령어를 실행한 상태에서 테스트를 실행하자.  
* $ sudo iftop -i lo0 -nf "host localhost"  
* windows 사용자라면 wsl2를 사용하거나 다른 모니터링 툴을 찾아보자.  
*/  
class PoolingVsNoPoolingTest {  
  
private final Logger log = LoggerFactory.getLogger(PoolingVsNoPoolingTest.class);  
  
private static final int COUNT = 1000;  
  
private static MySQLContainer<?> container;  
  
@BeforeAll  
static void beforeAll() throws SQLException {  
// TestContainer로 임시 MySQL을 실행한다.  
container = new MySQLContainer<>(DockerImageName.parse("mysql:8.0.30"))  
.withDatabaseName("test");  
container.start();  
  
final var dataSource = createMysqlDataSource();  
  
// 테스트에 사용할 users 테이블을 생성하고 데이터를 추가한다.  
try (Connection conn = dataSource.getConnection()) {  
conn.setAutoCommit(true);  
try (Statement stmt = conn.createStatement()) {  
stmt.execute("DROP TABLE IF EXISTS users;");  
stmt.execute("CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(100) NOT NULL) ENGINE=INNODB;");  
stmt.executeUpdate("INSERT INTO users (email) VALUES ('hkkang@woowahan.com')");  
conn.setAutoCommit(false);  
}  
}  
}  
  
@AfterAll  
static void afterAll() {  
container.stop();  
}  
  
@Test  
void noPooling() throws SQLException {  
final var dataSource = createMysqlDataSource();  
  
long start = ClockSource.currentTime();  
connect(dataSource);  
long end = ClockSource.currentTime();  
  
// 테스트 결과를 확인한다.  
log.info("Elapsed runtime: {}", ClockSource.elapsedDisplayString(start, end));  
}  
  
@Test  
void pooling() throws SQLException {  
final var config = new HikariConfig();  
config.setJdbcUrl(container.getJdbcUrl());  
config.setUsername(container.getUsername());  
config.setPassword(container.getPassword());  
config.setMinimumIdle(1);  
config.setMaximumPoolSize(1);  
config.setConnectionTimeout(1000);  
config.setAutoCommit(false);  
config.setReadOnly(false);  
final var hikariDataSource = new HikariDataSource(config);  
  
long start = ClockSource.currentTime();  
connect(hikariDataSource);  
long end = ClockSource.currentTime();  
  
// 테스트 결과를 확인한다.  
log.info("Elapsed runtime: {}", ClockSource.elapsedDisplayString(start, end));  
}  
  
private static void connect(DataSource dataSource) throws SQLException {  
// COUNT만큼 DB 연결을 수행한다.  
for (int i = 0; i < COUNT; i++) {  
try (Connection connection = dataSource.getConnection()) {  
try (Statement stmt = connection.createStatement();  
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {  
if (rs.next()) {  
rs.getString(1).hashCode();  
}  
}  
}  
}  
}  
  
private static MysqlDataSource createMysqlDataSource() throws SQLException {  
final var dataSource = new MysqlDataSource();  
dataSource.setUrl(container.getJdbcUrl());  
dataSource.setUser(container.getUsername());  
dataSource.setPassword(container.getPassword());  
dataSource.setConnectTimeout(1000);  
return dataSource;  
}  
}

커넥션 풀링 안 한 경우 -> peak 11.6Mb

커넥션 풀링 한 경우 -> peak 6.79Mb

HikariCP

Spring Boot 2.0 부터 HikariCP를 기본 데이터 소스로 채택 하고 있다.

Supported Connection Pools
We prefer HikariCP for its performance and concurrency. If HikariCP is available, we always choose it.

HikariCP 공식 문서를 참고하여 HikariCP를 설정해보자.
HikariCP 필수 설정
HikariCP의 pool size는 몇으로 설정하는게 좋을까?
HikariCP를 사용할 때 적용하면 좋은 MySQL 설정

Spring Boot에서 설정 파일인 application.yml을 통해 DataSource설정 가능
하지만 데이터소스 여러 개 쓰거나 세부 설정 하려면 빈을 직접 생성함
DataSourceConfig 클래스 -> getInstance() 하면 DataSource 인스턴스 가져옴
설정한 커넥션 풀 최대값보다 더 많은 쓰레드 생성해서 동시에 디비 접근 시도해도 최대 풀 사이즈 유지함

쓰레드 10개, 커넥션 5개일 때

쓰레드 15개, 커넥션 5개일 때

쓰레드 15개, 커넥션 10개일 때

=> ??? 다섯개나 10개씩만 들어가야하는거아닌가??

🎶 느낀 점 & 배운 점

오늘 계획 한 것 :

  • 미션 PR 날리기
  • 추석 계획 세우기 - 강의 다시 듣기, 노트 정리, 이력서, 포트폴리오, 우테코에서 배운 것 정리
  • 오픽 준비
  • 어제 못 푼 이분탐색 문제 다시
  • 이벤트 써야하는 거 => 감상 잇는 여행 삭제 안되는 거 해결
  • 쿼리 튜닝 마무리

오늘 한 것 :

  • 미션 PR 날리기

공부할 것 :

펼치기
API versioning<br>

AsyncUncaughtExceptionHandler<br>

TaskDecorator<br>

ThreadPoolTaskExecutor<br>

@ConfigurationProperties<br>

톰캣 기본 8192개 어떻게 쓰이나?<br>

ngrinder<br>  
locust  <br>
jmeter  <br>
k6<br>

gradle로 빌드하면 q타입 읽는데 intellij로 하면 못읽는 문제  <br>
  
cqrs pattern  <br>
  
Modifier<br>

쓰레드 풀 설정 <- 테코블 글 써보기?  <br>
  

native thread, green thread  <br>
  
process lifecycle<br>

역정규화  <br>
  
파티션 프루닝  <br>
  
커버링 인덱스  <br>
  
스케쥴 프로그램, 배치 프로그램  <br>
  

  
TransactionTemplate  <br>
  
Event @Order  <br>
@RecordApplicationEvents<br>

HAProxy  <br>
  
DB 장애 대처  <br>
레플리카  <br>
Binary Log 기반  <br>
GTID 기반  <br>
  
virtual stored <br> 
-> stored는 왜 칼럼 따로 안하고 버츄얼에 있지?  <br>
  
enum 사용하면 reflection을 통한 생성도 방지<br>

싱크홀패턴<br>

JVM의 쓰레드와 운영체제의 쓰레드<br>

리플렉션 자세하게<br>

라운드로빈<br>

DNS<br>

WeakHashMap<br>

nslookup<br>

포스트모뎀<br>

NIO<br>

  
completable future  <br>
Spring Web Flux  <br>
  
자바의 Synchronized -> monitor lock<br>

@PostConstruct  <br>
@HandlebarsHelper<br>

WebContentInterceptor<br>

REST한 요청<br>

DNS<br>

CORS - Preflight <br>

CDN<br>

CAS (Compare and Swap) 알고리즘<br>

Configuration Property Scan<br>

static field 언제 올라감?<br>

Mozilla<br>

ClassLoader가 어떻게 읽음?  <br>
  
spring boot project build package 생기는 이유랑 왜 거기서 클래스패스 찾는지<br>

  
[https://velog.io/@ohzzi/F12%EC%9D%98-%EB%88%88%EB%AC%BC%EB%82%98%EB%8A%94-%EC%BF%BC%EB%A6%AC-%EA%B0%9C%EC%84%A0%EA%B8%B0-%EC%9D%B4%EB%A1%A0%ED%8E%B8](https://velog.io/@ohzzi/F12%EC%9D%98-%EB%88%88%EB%AC%BC%EB%82%98%EB%8A%94-%EC%BF%BC%EB%A6%AC-%EA%B0%9C%EC%84%A0%EA%B8%B0-%EC%9D%B4%EB%A1%A0%ED%8E%B8)오찌 쿼리 개선기<br>

오찌 로깅<br>

[https://velog.io/@saint6839/Controller%EC%97%90%EC%84%9C-HttpRequest-Body-%EA%B0%92%EC%9D%80-%EC%99%9C-%EB%B9%84%EC%9B%8C%EC%A0%B8-%EC%9E%88%EC%9D%84%EA%B9%8C](https://velog.io/@saint6839/Controller%EC%97%90%EC%84%9C-HttpRequest-Body-%EA%B0%92%EC%9D%80-%EC%99%9C-%EB%B9%84%EC%9B%8C%EC%A0%B8-%EC%9E%88%EC%9D%84%EA%B9%8C) getBody 왜 비워져있는지  <br>
  
필터 인터셉터 빈등록  <br>
  
StatementInspector<br>

  
쿼리카운터 -> ThreadLocal  <br>
  
로그인 -> 인터셉터 or 필터<br>

Join Column 공부  <br>
  
createdDate CreationTime  <br>
  
entity listener  <br>
MappedSuperClass  <br>
  
웹소켓  <br>
  
블루그린, 레드블랙, 롤링  <br>
flyway -> 롤백하는 경우 스키마 관리에 문제 생김<br>

@Retention -> [https://jeong-pro.tistory.com/234](https://jeong-pro.tistory.com/234)<br>

  
composite 패턴  <br>
decorate 패턴  <br>
  
검프 도커 테코톡 실습편  <br>
  
@Valid 에서 잡은 예외 허브가 해 놓은거 이해하기  <br>
  
ssh 설정  <br>
  
propagation, isolation 직접 설정 ([https://velog.io/@kdhyo/JavaTransactional-Annotation-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-26her30h](https://velog.io/@kdhyo/JavaTransactional-Annotation-%EC%95%8C%EA%B3%A0-%EC%93%B0%EC%9E%90-26her30h)) -> 이거 보고 감 잡기  <br>
팬텀리드 예시 다시 보기  <br>
DB에서 B-Tree 쓰는 이유  <br>
복합키  <br>
Unique 동시에 걸기  <br>
인덱스 스킵 스캔  <br>
  
springfox, springdoc<br>

CQRS<br>

LAZY Loading (Transactional 사용하지 않으면 못 쓰는 이유)  <br>
Fetch Join  <br>
  
@ContextConfiguration(classes = TestSyncConfig.class)  <br>
  
나아가팀 배포 스크립트문제 -> 쉘 스크립트 프로세스 물고 있는 문제<br>

동욱님 JPA Exists 쿼리 성능 개선 블로그  <br>
동욱님 예외레벨 블로그<br>

나아가팀 AuthInterceptor에서 터지는 예외가 ControllerAdvice에서 처리도 되고, 콘솔에 에러라고 찍히기도 함 -> 해결못함<br>

CD 할 때 도커 허브  <br>
  
토큰 암호화  <br>
  
self-join  <br>
  
certbot   <br>
  
jpa bulk insert 할 때 auto increment 이면 안됨  <br>
  
토큰 세션 장단점  <br>
  
syncronized, 비관적 락 장단점  <br>
  
비동기와 트랜잭션  <br>
  
@AttributeOverride<br>

레디스  <br>
메세지 큐  <br>
R-Tree  <br>
  
커버링인덱스<br>

대칭키 암호화 비대칭키 암호화 복호화  <br>
  
모든 엔티티의 변수를 VO로<br>
</div>

🌸 감정회고

집 내려가서 가족도 보고 로리도 봐야하는데…
후회없게 열심히 하자