SELECT 쿼리를 한 번만 실행하고, 동일한 쿼리의 이후 실행 결과는 캐시에서 직접 제공할 수 있습니다.
쿼리 유형에 따라 ClickHouse 서버의 지연 시간과 리소스 사용량을 크게 줄일 수 있습니다.
배경, 설계 및 제한 사항
- 트랜잭션 일관성을 보장하는 캐시에서는
SELECT쿼리의 결과가 변경되었거나 변경되었을 가능성이 있는 경우 데이터베이스가 캐시된 쿼리 결과를 무효화(폐기)합니다. ClickHouse에서 데이터를 변경하는 작업에는 테이블에 대한/에서의 삽입/업데이트/삭제 또는 축약 머지가 포함됩니다. 트랜잭션 일관성을 보장하는 캐싱은 특히 OLTP 데이터베이스에 적합하며, 예를 들어 MySQL (v8.0 이후 쿼리 캐시가 제거됨)와 Oracle이 있습니다. - 트랜잭션 일관성을 보장하지 않는 캐시에서는 모든 엔트리에
유효 기간이 할당되어 해당 기간이 지나면 만료되고(예: 1분), 그 기간 동안 기반 데이터가 거의 변경되지 않는다는 가정하에 쿼리 결과의 약간의 부정확성을 허용합니다.
이러한 방식은 전반적으로 OLAP 데이터베이스에 더 적합합니다. 트랜잭션 일관성을 보장하지 않는 캐싱으로도 충분한 예로,
여러 사용자가 동시에 접근하는 리포팅 도구의 시간별 매출 보고서를 생각해 볼 수 있습니다. 일반적으로 매출 데이터는
변화 속도가 충분히 느리므로 데이터베이스는 보고서를 한 번만 컴퓨트하면 됩니다(첫 번째
SELECT쿼리로 표현됨). 이후의 쿼리는 쿼리 캐시에서 직접 제공할 수 있습니다. 이 예시에서 합리적인 유효 기간은 30분일 수 있습니다.
구성 설정 및 사용
clickhouse-local은 한 번에 하나의 쿼리만 실행합니다. 쿼리 결과를 캐싱하는 것은 의미가 없으므로 clickhouse-local에서는 쿼리
결과 캐시가 비활성화되어 있습니다.
use_query_cache = true 매개변수도 함께 사용한 경우),
계산된 결과를 캐시에서 읽어 즉시 반환합니다.
use_query_cache 설정과 그 밖의 모든 쿼리 캐시 관련 설정은 독립 실행형 SELECT SQL 문에만 적용됩니다. 특히,
CREATE VIEW AS SELECT [...] SETTINGS use_query_cache = true로 생성된 뷰에 대해 실행한 SELECT의 결과는 해당 SELECT
SQL 문을 SETTINGS use_query_cache = true로 실행하지 않는 한 캐시되지 않습니다.true)으로 더 세부적으로 구성할 수 있습니다. 앞의 설정은
쿼리 결과를 캐시에 저장할지 제어하고, 뒤의 설정은 데이터베이스가 쿼리 결과를 캐시에서 가져오도록 시도할지 결정합니다. 예를 들어, 다음 쿼리는 캐시를 수동적으로만 사용합니다. 즉, 캐시에서 읽기만 시도하고 그
결과를 캐시에 저장하지는 않습니다:
use_query_cache, enable_writes_to_query_cache 및
enable_reads_from_query_cache는 특정 쿼리에만 지정하는 것이 좋습니다. 사용자 또는 프로필 수준에서 캐싱을 활성화하는 것도 가능하지만(예: SET use_query_cache = true), 이 경우 모든 SELECT 쿼리가 캐시된 결과를 반환할 수 있다는 점을 염두에 두어야 합니다.
쿼리 캐시는 SYSTEM CLEAR QUERY CACHE 문을 사용해 비울 수 있습니다. 쿼리 캐시의 내용은 시스템 테이블
system.query_cache에 표시됩니다. 데이터베이스 시작 이후 발생한 쿼리 캐시 적중 및 미적중 횟수는 시스템 테이블
system.events의 이벤트 “QueryCacheHits” 및 “QueryCacheMisses”로 표시됩니다. 두 카운터는 설정
use_query_cache = true로 실행된 SELECT 쿼리에 대해서만 갱신되며, 다른 쿼리는 “QueryCacheMisses”에 영향을 주지 않습니다. 시스템 테이블
system.query_log의 필드 query_cache_usage는 실행된 각 쿼리에 대해 쿼리 결과가 쿼리 캐시에 기록되었는지, 또는
쿼리 캐시에서 읽혔는지를 보여줍니다. 시스템 테이블
system.metrics의 메트릭 QueryCacheEntries 및 QueryCacheBytes는 현재 쿼리 캐시에 포함된 엔트리 수 / 바이트 수를 보여줍니다.
쿼리 캐시는 각 ClickHouse 서버 프로세스마다 하나씩 존재합니다. 그러나 기본적으로 캐시된 결과는 사용자 간에 공유되지 않습니다. 이는
변경할 수 있지만(아래 참고), 보안상의 이유로 권장되지 않습니다.
쿼리 결과는 쿼리 캐시에서 해당 쿼리의 Abstract Syntax Tree (AST)를 기준으로
참조됩니다. 즉, 캐싱은 대문자/소문자를 구분하지 않으므로 예를 들어 SELECT 1과 select 1은 동일한 쿼리로 처리됩니다. 더
자연스럽게 일치시키기 위해 쿼리 캐시 및 출력 형식)과 관련된 모든 쿼리 수준 설정은 AST에서 제거됩니다.
예외가 발생하거나 사용자가 취소하여 쿼리가 중단된 경우에는 쿼리 캐시에 엔트리가 기록되지 않습니다.
바이트 단위의 쿼리 캐시 크기, 최대 캐시 엔트리 수, 개별 캐시 엔트리의 최대 크기(바이트 및
레코드 기준)는 서로 다른 서버 구성 옵션을 사용해 구성할 수 있습니다.
users.xml의 사용자 프로필에 query_cache_max_size_in_bytes 및
query_cache_max_entries 설정을 지정한 다음, 두 설정을 모두
읽기 전용으로 설정합니다:
Age 및 Expires header를 설정합니다.
엔트리는 기본적으로 압축됩니다. 이렇게 하면 쿼리 캐시에 대한 쓰기/읽기 속도는 느려지지만 전체 메모리 사용량은 줄어듭니다.
압축을 비활성화하려면 query_cache_compress_entries 설정을 사용하십시오.
때로는 동일한 쿼리에 대해 여러 결과를 캐시된 상태로 유지하는 것이 유용합니다. 이는 엔트리의 레이블(또는 네임스페이스) 역할을 하는
query_cache_tag 설정을 사용해 구현할 수 있습니다. 쿼리 캐시는 서로 다른 tag가 있는 동일한 쿼리의 결과를 서로 다른 것으로
간주합니다.
동일한 쿼리에 대해 서로 다른 3개의 쿼리 캐시 엔트리를 생성하는 예시:
tag가 있는 항목만 제거하려면 SYSTEM CLEAR QUERY CACHE TAG 'tag' 문을 사용할 수 있습니다.
ClickHouse는 테이블 데이터를 max_block_size행 단위의 블록으로 읽습니다. 필터링, 집계
등으로 인해 결과 블록은 일반적으로 ‘max_block_size’보다 훨씬 작지만, 그보다 훨씬 커지는 경우도 있습니다. 설정
query_cache_squash_partial_results (기본적으로 활성화됨)는 결과 블록을
쿼리 결과 캐시에 삽입하기 전에 ‘max_block_size’ 크기의 블록으로 합치거나(너무 작은 경우) 분할할지(큰 경우) 제어합니다.
이 설정을 사용하면 쿼리 캐시에 쓰는 성능은 다소 떨어지지만, 캐시 엔트리의 압축률이 향상되고 이후 쿼리 결과를 쿼리 캐시에서 제공할 때
더 자연스러운 블록 세분화 수준을 얻을 수 있습니다.
그 결과, 쿼리 캐시는 각 쿼리마다 여러 개의 (partial)
결과 블록을 저장합니다. 이 동작은 기본값으로 적절하지만, 설정
query_cache_squash_partial_results를 사용해 비활성화할 수 있습니다.
또한 비결정적 함수를 포함하는 쿼리 결과는 기본적으로 캐시되지 않습니다. 이러한 함수에는 다음이 포함됩니다.
- 딕셔너리에 접근하는 함수:
dictGet()등 - XML
정의에
<deterministic>true</deterministic>태그가 없는 사용자 정의 함수, - 현재 날짜 또는 시간을 반환하는 함수:
now(),today(),yesterday()등, - 임의 값을 반환하는 함수:
randomString(),fuzzBits()등, - 결과가 쿼리 처리에 사용되는 내부 청크의 크기와 순서에 따라 달라지는 함수:
nowInBlock()등,rowNumberInBlock(),runningDifference(),blockSize()등, - 환경에 따라 결과가 달라지는 함수:
currentUser(),queryID(),getMacro()등.