Redis 에서 scan 명령 에 대한 심도 있 는 설명
레 디 스 를 잘 아 는 사람들 은 모두 그것 이 단일 스 레 드 라 는 것 을 안다.따라서 시간 복잡 도가 O(N)인 명령 을 사용 할 때 는 매우 신중 해 야 한다.자칫 프로 세 스 가 막 혀 레 디 스 가 멈 출 수도 있다.
때때로,우 리 는 조건 에 부합 되 는 일부 명령 에 대해 조작 을 해 야 한다.예 를 들 어 test 를 삭제 하 는 것 이다.시작 키.그럼 이 키 들 을 어떻게 구 할 수 있 을까요?Redis 2.8 버 전에 서 는 keys 명령 을 사용 하여 필요 한 key 를 정규 에 맞 게 얻 을 수 있 습 니 다.그러나 이 명령 은 두 가지 단점 이 있다.
4.567917.limit 이 없 으 면 조건 에 맞 는 키 를 한꺼번에 가 져 올 수 밖 에 없습니다.결과 가 수백 만 개가 있 으 면'무궁무진'문자열 출력 을 기다 리 는 것 입 니 다
keys 명령 에 비해 scan 명령 은 두 가지 뚜렷 한 장점 이 있 습 니 다.
4.567917.scan 명령 의 시간 복잡 도 는 O(N)이지 만 여러 번 진행 되 며 스 레 드 를 막 지 않 습 니 다4.567917.scan 명령 은 limit 인 자 를 제공 하여 매번 결 과 를 되 돌려 주 는 최대 항목 수 를 제어 할 수 있 습 니 다이 두 가지 장점 은 우리 가 위의 어 려 운 문 제 를 해결 하 는 데 도움 을 주 었 지만 scan 명령 도 완벽 하지 않 습 니 다.돌아 온 결과 가 중복 되 기 때문에 클 라 이언 트 가 다시 해 야 합 니 다.왜 중복 되 는 지 에 대해 서 는 본문 을 다 본 후에 답 이 있 을 것 이 라 고 믿는다.
scan 명령 의 기본 용법 에 대해 서 는 Redis 명령 의 상세 한 설명 을 참조 할 수 있 습 니 다Keys 라 는 글 에서 SCAN 명령 에 대한 소개
SCAN 명령
SCAN 명령 은 SCAN,SSCAN,HSCAN,ZSCAN 이 있 습 니 다.
SCAN 은 모든 keys 를 다 옮 겨 다 니 는 거 예요.
다른 SCAN 명령 은 SCAN 이 선택 한 집합 입 니 다.
SCAN 명령 은 증분 의 순환 으로 호출 할 때마다 작은 요소 만 되 돌려 줍 니 다.그 러 니까 키 즈 가 명령 한 구덩이 가 없 을 거 야.
SCAN 명령 은 0 부터 옮 겨 다 니 며 0 까지 옮 겨 다 니 는 커서 를 되 돌려 줍 니 다.
오늘 우 리 는 주로 저층 의 구조 와 소스 코드 의 측면 에서 scan 이 어떻게 일 하 는 지 토론 한다.
Redis 의 구조
Redis 는 Hash 표를 밑바닥 으로 사 용 했 는데 그 이 유 는 효율 적 이 고 간단 하기 때문이다.Hash 표 하면 많은 자바 프로그래머 들 의 첫 반응 이 HashMap 이다.맞습니다.Redis 바 텀 key 의 저장 구 조 는 HashMap 과 같은 배열+링크 의 구조 입 니 다.그 중에서 1 차원 의 배열 크기 는 2n(n>=0)이다.매번 확장 수조 의 길 이 를 배로 늘리다.
scan 명령 은 이 1 차원 배열 을 옮 겨 다 니 는 것 입 니 다.돌아 올 때마다 커서 값 도 이 배열 의 색인 입 니 다.limit 매개 변 수 는 몇 개의 배열 의 요 소 를 옮 겨 다 니 는 지 나타 내 고 이 요소 들 을 조건 에 맞 는 결 과 를 되 돌려 줍 니 다.요소 마다 연결 되 어 있 는 링크 의 크기 가 다 르 기 때문에 매번 돌아 오 는 결과 의 수량 도 다르다.
SCAN 의 옮 겨 다 니 는 순서
scan 명령 의 옮 겨 다 니 는 순서 에 대해 서 는 작은 밤 으로 구체 적 으로 볼 수 있 습 니 다.
127.0.0.1:6379> keys *
1) "db_number"
2) "key1"
3) "myKey"
127.0.0.1:6379> scan 0 MATCH * COUNT 1
1) "2"
2) 1) "db_number"
127.0.0.1:6379> scan 2 MATCH * COUNT 1
1) "1"
2) 1) "myKey"
127.0.0.1:6379> scan 1 MATCH * COUNT 1
1) "3"
2) 1) "key1"
127.0.0.1:6379> scan 3 MATCH * COUNT 1
1) "0"
2) (empty list or set)
우리 의 Redis 에는 3 개의 key 가 있 습 니 다.우 리 는 매번 1 차원 배열 의 요소 만 옮 겨 다 닙 니 다.위 와 같이 SCAN 명령 의 반복 순 서 는?0->2->1->3
이 순 서 는 보기에 좀 이상 하 다.우 리 는 그것 을 2 진법 으로 바 꾸 면 좀 이해 하기 쉽다.
00->10->01->11
우 리 는 매번 이 서열 이 높 은 위치 에 1 을 더 한 것 을 발견 했다.보통 2 진법 의 덧셈 은 오른쪽 에서 왼쪽으로 더 하고 진 하 는 것 이다.이 서열 은 왼쪽 에서 오른쪽으로 더 하고 진 위 된 것 이다.이 점 은 redis 의 소스 코드 에서 도 입증 되 었 다.
dict.c 파일 의 dictScan 함수 에서 커서 를 다음 과 같이 처리 하 였 습 니 다.
v = rev(v);
v++;
v = rev(v);
커서 를 거꾸로 놓 고 하 나 를 더 한 후에 거꾸로 놓 는 것,즉 우리 가 말 하 는'높 은 자리 에 1 을 더 하 는 것'의 조작 이라는 뜻 이다.정상 적 인 0,1,2...........................................................................
SCAN 이 옮 겨 다 니 는 과정 에서 확장 이 발생 했 을 때 옮 겨 다 니 는 것 이 어떻게 진행 되 는 지 살 펴 보 자.원본 배열 에 4 개의 요 소 를 추가 합 니 다.즉,색인 은 두 자리 가 있 습 니 다.이 때 는 3 자리 로 확장 하고 rehash 를 진행 해 야 합 니 다.
원래 xx 에 연 결 된 모든 요 소 는 0xx 와 1xx 에 분배 되 었 다.위의 그림 에서 10 을 옮 겨 다 닐 때 dict 는 rehash 를 진행 합 니 다.이때 scan 명령 은 010 부터 옮 겨 다 니 며 000 과 100(원래 00 에 걸 려 있 는 요소)은 다시 옮 겨 다 니 지 않 습 니 다.
다시 한 번 축소 상황 을 보 자.dict 가 3 비트 에서 2 비트 로 축소 된다 고 가정 하면 110 을 옮 겨 다 닐 때 dict 가 축소 되 었 습 니 다.이때 scan 은 10 을 옮 겨 다 닙 니 다.이 때 010 에 걸 려 있 는 요 소 는 반복 되 지만 010 이전의 요 소 는 반복 되 지 않 습 니 다.그래서 축 용 할 때 중복 요소 가 나타 날 수 있 습 니 다.
Redis 의 rehash
rehash 는 비교적 복잡 한 과정 으로 Redis 의 프로 세 스 를 막 지 않 기 위해 점진 적 인 rehash 체 제 를 사용 했다.
/* */
typedef struct dict {
//
dictType *type;
//
void *privdata;
//
dictht ht[2];
// rehash
// rehash , -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
//
int iterators; /* number of iterators currently running */
} dict;
Redis 의 사전 구조 에는 두 개의 hash 표,새 표,오래된 표 가 있 습 니 다.rehash 과정 에서 redis 는 오래된 표 의 요 소 를 새로운 표 로 옮 겼 습 니 다.다음은 dict 의 rehash 작업 의 소스 코드 를 보 겠 습 니 다.
/* Performs N steps of incremental rehashing. Returns 1 if there are still
* keys to move from the old to the new hash table, otherwise 0 is returned.
*
* Note that a rehashing step consists in moving a bucket (that may have more
* than one key as we use chaining) from the old to the new hash table, however
* since part of the hash table may be composed of empty spaces, it is not
* guaranteed that this function will rehash even a single bucket, since it
* will visit at max N*10 empty buckets in total, otherwise the amount of
* work it does would be unbound and the function may block for a long time. */
int dictRehash(dict *d, int n) {
int empty_visits = n*10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0;
while(n-- && d->ht[0].used != 0) {
dictEntry *de, *nextde;
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned long)d->rehashidx);
while(d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
}
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
while(de) {
uint64_t h;
nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
/* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
}
/* More to rehash... */
return 1;
}
주석 을 통 해 rehash 의 과정 은 bucket 을 기본 단위 로 이전 한 다 는 것 을 알 수 있 습 니 다.버 킷 이란 우리 가 앞에서 언급 한 1 차원 배열 의 요소 입 니 다.목록 을 옮 길 때마다다음은 이 코드 를 설명 하 겠 습 니 다.총결산
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Redis 해시에 대한 완벽한 가이드변경 가능하므로 필요에 따라 쉽게 변경하고 업데이트할 수 있습니다. Redis 해시는 구조가 평평하므로 JSON에서와 같이 여러 수준을 가질 수 없습니다. redis 해시의 명명 규칙은 hash:key 로 입력되므로...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.