Mysql 데이터정렬 collation 이슈 해결하기
Issue
Springboot + jpa + querydsl 환경의 백엔드 개발 도중 당황스러운 에러가 나타났습니다.
queryFactory.select() .from(entityA) .leftJoin(entityB) .on(entityA.entityBIdString.eq(entityB.entityBId.stringValue())) .fetch();
먼저 간단히 환경은
- entityA와 entityB는 논리상 N : 1 관계
- 데이터베이스 설계상 entityB의
id (entityBId)
컬럼은bigint
이고, entityA의entityBIdString
컬럼은varchar
타입 - entityA와 entityB는 jpa entity에서
manyToOne
또는oneToMany
사용을 하지 않음
위와 같았고, 쿼리 실행시
Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='
위와 같은 에러가 나타났습니다.
TroubleShooting
Mysql
의 Illegal mix of collations
에러는 비교를 하려고 시도한 컬럼의 collation
이 서로 달라 비교가 불가능하다는 내용의 에러입니다.
collation
collation
은 문자열을 비교하고 정렬할 때 사용하는 규칙입니다.Character Set
이 문자들의 집합이라면 collation
은 대소문자, 악센트 구분, 문자 정렬 순서 등을 이용해 문자를 비교하고 정렬하는 방법을 결정합니다.
- 문자열간의 비교
- ORDER BY 등의 쿼리에서 결과의 정렬 순서를 결정
- 대소문자 구분여부 결정
ci
: case insensitive, 대소문자를 구분하지 않음ai
: accent insensitive, 악센트가 있는 문자를 악센트가 없는 문자와 같이 취급general
: 일반적인 목적으로 사용되는 비교규칙0900
: Unicode 9.0.0 기준으로 문자 비교, Mysql 8.0에서 도입unicode
: Unicode 표준을 따름
utf8mb4_unicode_ci
는 utf8mb4
를 기본 문자 집합으로 사용하며, Unicode 표준을 따르며 대소문자를 비교하지 않습니다.utf8mb4_0900_ai_ci
는 utf8mb4
를 기본 문자 집합으로 사용하며, Unicode 9.0.0 를 따르며 악센트를 무시하고, 대소문자를 비교하지 않습니다.
현재 사용하고 있는 Mysql 버전은?
현재 이슈가 나타난 Mysql 버전은 8.0버전입니다.
하지만 현재 사용하고 데이터베이스를 초기 설정할 때 몇가지 상황이 있었는데요.
- 제가 일하고 있는 회사에서 기존 운영하던 서비스가 있었고, 신규 서비스를 개발하면서 만든 신규 데이터베이스에서 이슈가 나타났습니다.
- 기존 서비스의 데이터베이스는 5버전 안에서 5.7버전까지 업그레이드하는 과정에서 5.5버전에서 이용하던
utf8mb4_unicode_ci
로 데이터베이스와 테이블이 만들어져 있었습니다. - 아무 생각 없이 기존 서비스의 데이터베이스 collation이
utf8mb4_unicode_ci
인 것을 보고 신규 데이터베이스의 collation도utf8mb4_unicode_ci
으로 설정했습니다.
그러면 왜 단순 형변환된 문자를 비교하지 못했는가
select from entity_a left join entity_b on entity_a.entity_b_id_string = cast(entity_b.entity_b_id as char)
위 querydsl 코드가 실행되었을 때, 쿼리는 위와 같습니다.
querydsl 의 .stringValue()
는 cast( as char)
로 변환되어 값을 char 형태로 변환합니다.
이 때, 숫자를 문자열로 바꾸는 과정에서 collation을 지정하지 않아 mysql 8.0 의 기본 collation인 utf8mb4_0900_ai_ci
을 따르는 문자열로 변환되었고, 비교를 원하던 entity_a.entity_b_id_string
는 테이블에 지정된 utf8mb4_unicode_ci
를 따르기 때문에 서로간의 문자열 비교가 불가합니다.
Solution
Mysql 설정을 utf8mb4_unicode_ci
으로 변경하는 선택지는 데이터베이스 재시작시 초기화되어 선택지에서 뻇습니다.
결국 기존 utf8mb4_unicode_ci
으로 설정한 데이터베이스의 설정을 변경하였는데, 이 경우 데이터베이스에서 다양한 언어나 특수문자를 다루는 경우에는 문제가 생길 수 있고, collation을 수정하는 시기에 성능문제가 발생할 수 있습니다.
하지만 Mysql 8.0의 기본 collation인 utf8mb4_0900_ai_ci
으로 변경하는 것이 추후 개발시 쿼리 정합성에 도움이 되고, 정렬은 별도의 컬럼으로 처리하였기 때문에 안전하다 판단하여 데이터베이스와 테이블의 기본값을 utf8mb4_0900_ai_ci
로 변경하는 것으로 결정하였습니다.
데이터베이스 확인/수정 쿼리
Mysql에 설정된 값 확인
SHOW VARIABLES LIKE 'character_set_server'; SHOW VARIABLES LIKE 'collation_server';
현재 존재하는 데이터베이스(schema)의 character set, collation 확인
SELECT SCHEMA_NAME, DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA;
특정 테이블의 컬럼 설정 확인
SELECT COLUMN_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '데이터베이스이름' AND TABLE_NAME = '테이블이름';
데이터베이스 collation 변경
ALTER DATABASE 데이터베이스이름 CHARACTER SET utf8mb4 COLLATE 'collate명';
테이블 collation 변경
ALTER TABLE 테이블이름 CONVERT TO CHARACTER SET utf8mb4 COLLATE 'collate명';
결국 초기 데이터베이스 설정 시기에 collation에 대해 정확하게 알아보지 않고 수정한 것이 (개발 단계였지만) 큰 이슈를 막을 수 있었다 생각했습니다.
Querydsl이 컴파일 단계에서 에러를 잡아준다는 장점이 있지만, 원초적으로 데이터베이스의 설정이 런타임에 에러를 발생시킬 수 있다는 점을 공유하고 싶었습니다.
감사합니다.
'ETC > 오류해결' 카테고리의 다른 글
zsh no matches found 에러 해결하기 (0) | 2022.05.18 |
---|---|
mac 5000 port가 사용중일 때 (0) | 2022.03.07 |
Jetbrain Intellij가 실행되지 않을 때 (0) | 2021.09.08 |
[Spring] web.xml cvc-elt.1.a: Cannot find the declaration of element 'web-app' (0) | 2021.05.26 |
log4j.xml The file cannot be validated as the XML definition 오류 (0) | 2021.05.24 |
[Spring] Driver net.sf.log4jdbc.sql.jdbcapi.DriverSpy claims to not accept jdbcUrl 오류 (1) | 2021.05.17 |