캐 시 쓰기(1):캐 시 눈사태 와 관통

기본 표기 법
프 리 젠 테 이 션 을 편리 하 게 하기 위해 서 는 Runtime.cache 를 캐 시 용기 로 사용 하고 간단 한 조작 류 를 정의 합 니 다.다음 과 같다.
 
public class CacheHelper
    {
        public static object Get(string cacheKey)
        {
    return HttpRuntime.Cache[cacheKey];
}
public static void Add(string cacheKey, object obj, int cacheMinute)
        {
    HttpRuntime.Cache.Insert(cacheKey, obj, null, DateTime.Now.AddMinutes(cacheMinute),
                        Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}
}

 간단 한 읽 기:
    
public object GetMemberSigninDays1() {
    const int cacheTime = 5;
    const string cacheKey = "mushroomsir";
    var cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null)
                    return cacheValue;
    cacheValue = "395";
    //      sql    。  :395     
    CacheHelper.Add(cacheKey,cacheValue,cacheTime);
    return cacheValue;
}

프로젝트 에 서 는 이렇게 쓰 는 방법 이 적지 않다.이렇게 쓰 는 것 은 틀 리 지 않 지만 병발 량 이 올 라 오 면 문제 가 생기 기 쉽다.
 캐 시 눈사태
캐 시 눈사태 는 캐 시 실효(만 료)로 새 캐 시가 만 료 되 지 않 았 습 니 다.
이 중간 시간 동안 모든 요청 은 데이터 베 이 스 를 조회 하 러 가 는데 데이터베이스 CPU 와 메모리 에 큰 압력 을 주 고 전단 연결 수가 부족 하 며 조회 가 막 힙 니 다.
이 중간 시간 은 그렇게 짧 지 않 습 니 다.예 를 들 어 sql 조회 1 초 에 전송 분석 0.5 초 를 더 합 니 다. 1.5 초 안에 모든 사용자 가 데이터 베 이 스 를 직접 조회 한 다 는 것 이다.
이런 상황 에서 가장 많이 사용 되 는 해결 방안 은 자 물 쇠 를 채 워 줄 을 서 는 것 이다.
전역 잠 금,인 스 턴 스 잠 금
  
public static object obj1 = new object();
    public object GetMemberSigninDays2() {
    const int cacheTime = 5;
    const string cacheKey = "mushroomsir";
    var cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) return cacheValue;
    //lock (obj1) //    // {
    // cacheValue = CacheHelper.Get(cacheKey);
    // if (cacheValue != null) // return cacheValue;
    // cacheValue = "395";
    //      sql    。  :395      // CacheHelper.Add(cacheKey,cacheValue,cacheTime);
    //
}
lock (this) {
    cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) return cacheValue;
    cacheValue = "395";
    //      sql    。  :395     
        CacheHelper.Add(cacheKey,cacheValue,cacheTime);
}
return cacheValue;}

첫 번 째:lock(obj 1)  전역 잠 금 은 만족 할 수 있 지만 모든 함수 에 하나의 obj 를 설명 해 야 합 니 다.그렇지 않 으 면 A,B 함수 가 모두 obj 1 을 잠 글 때 반드시 그 중의 하 나 를 막 을 것 입 니 다.
두 번 째:lock(this) 이 자 물 쇠 는 현재 인 스 턴 스 입 니 다.다른 인 스 턴 스 에 효과 가 없습니다.이 자 물 쇠 는 효과 가 없습니다.물론 단일 모드 의 대상 을 사용 하여 잠 글 수 있 습 니 다.
 현재 인 스 턴 스 에서:A 함수 가 현재 인 스 턴 스 를 잠 그 고 다른 것 도 현재 인 스 턴 스 함수 의 읽 기와 쓰기 가 차단 되 어 있 습 니 다.이런 방법 도 취 할 수 없습니다.
문자열 잠 금
잠 금 대상 이 안 되 는 이상 문자열 의 특성 을 이용 하여 캐 시 키 를 직접 잠 글 수 있 습 니 다.
    
public object GetMemberSigninDays3() {
    const int cacheTime = 5;
    const string cacheKey = "mushroomsir";
    var cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) return cacheValue;
    const string lockKey = cacheKey + "n(*≧▽≦*)n";
    //lock (cacheKey) // {
    // cacheValue = CacheHelper.Get(cacheKey);
    // if (cacheValue != null) // return cacheValue;
    // cacheValue = "395";
    //      sql    。  :395      // CacheHelper.Add(cacheKey,cacheValue,cacheTime);
    //
}
lock (lockKey) {
    cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) return cacheValue;
    cacheValue = "395";
    //      sql    。  :395     
        CacheHelper.Add(cacheKey,cacheValue,cacheTime);
}
return cacheValue;}

첫 번 째:lock(cacheName)  문제 가 있 습 니 다.문자열 도 공유 되 기 때문에 이 문자열 을 사용 하 는 다른 작업 행 위 를 막 을 수 있 습 니 다. 
문자열 이 공용 언어 실행 라 이브 러 리(CLR)에 잠시 남아 있 기 때문에 전체 프로그램 에서 주어진 문자열 이 하나의 인 스 턴 스 만 있 기 때문에 다음 두 번 째 방법 을 사용 할 수 있 습 니 다.
두 번 째:lock(lockKey) 만족 할 수 있다.그 목적 은 자물쇠 의 입도 가 가장 작고 전역 유일 성 을 확보 하기 위해 현재 캐 시 만 잠 그 는 조회 행 위 를 하 는 것 이다.
캐 시 관통
먼저 간단 한 예 를 들 어 일반 사 이 트 는 사용자 가 검색 한 결 과 를 캐 시 합 니 다.데이터 베 이 스 를 조회 하지 못 하면 캐 시 를 하지 않 습 니 다.그러나 이 빈 키 워드 를 자주 찾 으 면 요청 할 때마다 데이터 베 이 스 를 직접 조회 하 게 된다.
예 를 들 어 캐 시 관통 입 니 다.캐 시 를 돌아 서 데이터 베 이 스 를 직접 찾 아 달라 고 요청 하 는 것 도 캐 시 명중률 문제 입 니 다.
  
public object GetMemberSigninDays4() {
    const int cacheTime = 5;
    const string cacheKey = "mushroomsir";
    var cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) return cacheValue;
    const string lockKey = cacheKey + "n(*≧▽≦*)n";
    lock (lockKey) {
    cacheValue = CacheHelper.Get(cacheKey);
    if (cacheValue != null) return cacheValue;
    cacheValue = null;
    //       ,  。 //if (cacheValue2 == null) // {
    // return null;
    //    ,     //
}
if (cacheValue == null) {
    cacheValue = string.Empty;
    //      ,       ,     。
}
CacheHelper.Add(cacheKey,cacheValue,cacheTime);}return cacheValue;}

검색 할 수 없 는 빈 결 과 를 캐 시 에 저장 하면 다음 에 같은 요청 을 하면 null 로 바로 돌아 갈 수 있 습 니 다.즉,검색 값 이 비어 있 을 때 발생 하 는 캐 시 관통 을 피 할 수 있 습 니 다.
캐 시 영역 에 빈 값 을 저장 하고 검색 할 key 를 미리 검사 한 다음 에 뒤의 정상 캐 시 처리 논 리 를 실행 할 수 있 습 니 다.
캐 시 눈사태
앞 에 자물쇠 넣 고 줄 서 는 방식 으로 해결 되 지 않 았 습 니까?사실 잠 금 줄 을 서 는 것 은 데이터 뱅 크 의 압력 을 줄 이기 위해 서 일 뿐 본질 적 으로 시스템 스루풋 을 높이 지 않 았 다.
만약 에 높 고 보 내 면 캐 시 재 구축 기간 에 key 는 잠 겨 있 습 니 다.이것 은 1000 개의 요청 999 개가 모두 막 혀 있 습 니 다.결 과 는 사용자 가 시간 을 초과 하 기 를 기다 리 는 것 으로 매우 최적화 되 지 않 은 체험 이다.
이러한 행 위 는 본질 적 으로 다 중 스 레 드 웹 서버 를 이때 단일 스 레 드 로 처리 하면 대량의 차단 을 초래 할 수 있다.시스템 자원 도 낭비 입 니 다.캐 시 재 구축 으로 막 힌 스 레 드 는 더 많은 요청 을 처리 할 수 있 습 니 다.
여기 서 해결 방안 을 제시 하 는 것 은:
  
public object GetMemberSigninDays5() {
    const int cacheTime = 5;
    const string cacheKey = "mushroomsir";
    //    。
    const string cacheSign = cacheKey + "_Sign";
    var sign = CacheHelper.Get(cacheSign);
    //     
    var cacheValue = CacheHelper.Get(cacheKey);
    if (sign != null) return cacheValue;
    //   ,    。
    lock (cacheSign) {
    sign = CacheHelper.Get(cacheSign);
    if (sign != null) return cacheValue;
    CacheHelper.Add(cacheSign,"1",cacheTime);
    ThreadPool.QueueUserWorkItem((arg) => {
    cacheValue = "395";
    //      sql    。  :395     
        CacheHelper.Add(cacheKey,cacheValue,cacheTime*2);
    //        2 ,    。
}
);}return cacheValue;}

코드 에서 보 듯 이 우 리 는 캐 시 태그 key 를 하나 더 사 용 했 고 더 블 체크 잠 금 검 사 를 통 해 뒤의 논리 가 여러 번 실행 되 지 않도록 했다.
캐 시 태그 key:캐 시 태그 key 는 실제 key 만 료 시간 을 기록 하 는 태그 일 뿐 캐 시 값 은 임의의 값 일 수 있 습 니 다.예 를 들 어 1.이 는 실제 키 가 만 료 된 후에 다른 스 레 드 가 배경 에서 실제 키 의 캐 시 를 업데이트 하 는 데 사 용 됩 니 다.
실제 키: 그것 의 만 료 시간 은 1 배 연장 된다.예:원래 5 분 이 었 는데 지금 은 10 분 으로 설정 된다.이렇게 하 는 목적 은 캐 시 태그 key 가 만 료 된 후에 실제 캐 시 는 더러 운 데이터 로 호출 단 에 되 돌아 갈 수 있 고 다른 스 레 드 가 배경 에서 업 데 이 트 될 때 까지 새 캐 시 를 되 돌려 주 는 것 입 니 다.
실제 키 의 만 료 기간 을 1 배 연장 하 든 2,3 배 연장 하 든 상관없다.정상 캐 시 만 료 시간 보다 크 고 연 장 된 시간 내 에 데 이 터 를 충분히 끌 어 올 릴 수 있 도록 보장 하면 된다.
또 하나의 장점 은 갑자기 db 가 끊 기 면 더러 운 데이터 의 존 재 는 전단 시스템 이 데 이 터 를 얻 지 못 하지 않도록 보장 할 수 있다 는 것 이다.
이렇게 하면 어느 정도 시스템 의 물동량 을 높 일 수 있다.
총결산
글 에서 말 하 는 다른 함 수 를 막 는 것 은 동시 다발 상황 에서 같은 대상 을 잠 그 는 것 을 말한다.예 를 들 어 하나의 함수 가 A 대상 을 잠 그 고 다른 함 수 는 A 대상 의 자물쇠 가 풀 리 기 를 기 다 려 야 다시 잠 글 수 있다.
캐 시 업데이트 에 대해 서 는 캐 시 업 데 이 트 를 위 한 스 레 드 만 열 수 있 습 니 다.그림 이 편리 하면 스 레 드 탱크 에 버 리 면 됩 니 다.
실제 항목 에서 캐 시 층 프레임 워 크 의 패 키 징 은 복잡 해 야 한다.만약 에 병발 량 이 비교적 적 으 면 이렇게 쓰 면 오히려 코드 의 복잡 도 를 증가 시 킬 수 있 고 구체 적 으로 실제 상황 에 따라 취사선택 해 야 한다.

좋은 웹페이지 즐겨찾기