JPA 영속성 컨텍스트가 제공하는 기능 중 하나인 '1차 캐싱' 기능을 눈으로 직접 확인해 보자 @Serviceclass FirstService( private val userRepository: UserRepository,) { @Transactional fun callFirst() { var user = userRepository.findById(1).get() println("first call user = $user") user = userRepository.findById(1).get() println("second call user = $user") user = userRepository.findById(1).ge..
# != 연산에 인덱스 태우기where 절의 not equal(!=) 연산은 인덱스를 걸어놔도 효과를 볼 수 없다. 이런 경우 해당 칼럼이 순차적인 코드값으로 되어있는 경우는 인덱스를 태우도록 꼼수(?)를 부릴 수 있다. * 디비는 오라클 기준 예를 들어, my_table이 존재하고 my_column이 이런 분포를 가진다고 해보자. 1000 : 32만 rows2000 : 10만 rows3000 : 5만 rows4000 : 22만 rows5000 : 1만 rows6000 : 43만 rows 그리고 my_column이 1000이 아닌 rows를 select 한다고 가정해보자. 인덱스를 걸어놔도, select * from my_table where my_column != '1000' 으로 하면 full sca..
데이터베이스의 조회 성능을 끌어올리기 위해서 가장 우선적으로 검토되는 것이 "인덱스"다. 칼럼에 인덱스를 설정하면 왜 조회 성능이 빨라지는지 비유적으로 이해해 보자. # 책의 목차 비유 1000장이 넘는 데이터베이스 책이 있다고 가정하자. 책의 구성은 트랜잭션, 인덱스, 샤딩, 파티셔닝 등 다양하다. 이때, 우리는 인덱스 관련 부분만 읽고 싶다. 이럴 때 우리는 목차를 찾아가서 인덱스 관련 부분이 몇 페이지에 존재하는지 확인 후 곧바로 해당 페이지로 이동한다. 목차를 보니, 320페이지부터 인덱스 관련 내용이 시작이다. 이를 바탕으로 처음부터가 아니라 320페이지부터만 훑어보면 된다. 만약 목차가 없다면 어떻게 할까? 처음부터 하나씩 페이지를 훑어보면서 인덱스 관련 주제가 나오는지 찾아봐야 할 것이다. ..
프로젝트에서 복잡한 쿼리를 위해 보통 QueryDSL이나 MyBatis를 사용한다. MyBatis를 사용하는 경우 select 결과를 dto로 바로 받을 수 있는데, 이때 간혹 코틀린의 dto 클래스 (data class)의 프로퍼티 선언 순서와 select 절의 값 순서가 동일해야만 매핑되는 경우가 있다. 이런 경우는 어떤 경우이며 왜 그런지 살펴보자 (데이터베이스는 오라클을 기준) # 예시 세팅 다음과 같은 SQL을 가정해보자 SELECT A_V AS firstValue, B_V AS secondValue FROM table; 그리고 마이바티스로 이를 받아올 DTO는 다음과 같다고 해보자 data class Dto( val firstValue: String, val secondValue: String..
회사에서 사용하는 디비가 용량 이슈가 생겼던 적이 있는데, 그 당시 대강 보니 80TB 정도였다. 그래서 문득 1억건 정도 되는 데이터는 용량이 얼마나 될까?라는 생각을 가지게 되어서 테스트를 해보았다. 테이블 필드 타입, 수 그리고 인덱스 유무에 따라 다르겠지만, 그냥 가늠용으로 참고만 하면 좋을 것 같다. 디비는 MySQL이고 테스트 테이블은 위와 같이 생겼다. 여기에 write_date는 인덱스를 걸어놨다. 테스트 데이터는 7600만건 정도 넣어봤다. 해당 데이터는 AI허브에서 제공하는 인터넷 말뭉치 데이터인데, JSON형태로 제공해줘서 반복문으로 쉽게 집어넣었다. JDBC 배치 insert로 넣으니 50만건 넣는데 10초정도 걸렸다. (7600만 / 50만 = 152 * 10초 = 1520초 = ..
JPA 자체만으로는서 동일한 키 값을 가지는 두 엔티티를 조회했을때 동일성을 보장해주지 않는다. 따라서 로직상 동일한 키를 가지는 두 엔티티를 비교하기 위해선 반드시 equals와 hashCode를 재정의해야한다. # 단일키일 경우 다음과 같은 PK로 하나의 값을 가지는 엔티티를 가정해보자. @Entity data class OneKeyEntity( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Long = 0L ) 이제 이 코드를 실행해서 리포지토리에서 가져온 동일한 두 ID 엔티티가 동일한지 확인해보면, val oneKeyFirst = oneKeyEntityRepository.findById(1).orElseThrow() val on..
회사에서 오라클 디비를 사용하는데, 날짜를 계산하는 비즈니스 로직이 대부분 오라클 내장함수인 add_months()함수를 통해서 이루어지고 있었다. 이 함수는 날짜를 정확하게 계산하지 못 하는 문제가 있었는데 이를 개선했던 경험을 공유하고자 한다. # 계산 일이 말일일 경우 발생하는 오류 우선 비즈니스 요구사항이 6개월후를 의미하는 건, 정확히 응당일로 결과나 나와야 하는 것이였다. 쉽게말해 2023.02.28 -> 6개월 후 -> 2023.08.28로 결과가 나와야 했다. add_months()함수는 이 부분을 충족하지 못하고 있었는데, 'yyyymmdd'포맷의 날짜의 6개월 후를 add_months()함수를 사용해서 나오는 결과를 살펴보자 1월 (31) → 7월 (31) 정상 2월 (28) → 8월 ..