JavaScript의 일괄 처리 및 캐시 상세 정보

장면
최근 운영 환경에서 다음과 같은 상황을 만났습니다.
백그라운드는 사전표에 앞뒤가 공동으로 유지해야 하는 매개 값을 저장하고 type/id에 따라 사전을 가져오는 API를 제공합니다.그래서 목록을 렌더링할 때 많은 목록의 필드가 바로 사전의 id이고 백그라운드를 거치지 않은 데이터의 조합이다.
처음에 우리들이 문제를 해결하는 절차는 다음과 같다
  • 사전 필드 확인, 변환된 객체 유형 인터페이스 추가
  • 객체 목록을 사전 필드의 모든 값으로 변환
  • 사전 id 목록 중복 제거
  • id 목록에 따라 백그라운드에서 모든 사전 데이터 얻기
  • 획득한 사전 데이터를 id=> 사전의Map
  • 으로 변환
  • 최초의 목록을 훑어보고 안에 지정된 사전 필드를 변환한다
  • 위의 절차는 번거롭지 않지만 매우 번거로우므로 추가 유형을 정의해야 하며 말하지 않아도 오류가 발생하기 쉽다는 것을 알 수 있다.
    생각
  • 비동기식 일괄 처리 + LRU 캐시 최적화 성능
  • 비동기식formatter 지원 더 좋은 사용 체험
  • 비동기식 일괄 처리
    참조 구현:
    
    import { wait } from '../async/wait'
    
    /**
     *  
     * @param handle  
     * @param ms  ( , )
     */
    export function batch<P extends any[], R extends any>(
     handle: (list: P[]) => Promise<Map<P, R | Error>>,
     ms: number = 0,
    ): (...args: P) => Promise<R> {
     //  =>    
     const resultCache = new Map<string, R | Error>()
     //  =>  
     const paramCache = new Map<string, number>()
     // 
     let lock = false
     return async function (...args: P) {
      const key = JSON.stringify(args)
      paramCache.set(key, (paramCache.get(key) || 0) + 1)
      await Promise.all([wait(() => resultCache.has(key) || !lock), wait(ms)])
      if (!resultCache.has(key)) {
       try {
        lock = true
        Array.from(
         await handle(Array.from(paramCache.keys()).map((v) => JSON.parse(v))),
        ).forEach(([k, v]) => {
         resultCache.set(JSON.stringify(k), v)
        })
       } finally {
        lock = false
       }
      }
      const value = resultCache.get(key)!
      paramCache.set(key, paramCache.get(key)! - 1)
      if ((paramCache.get(key) || 0) <= 0) {
       paramCache.delete(key)
       resultCache.delete(key)
      }
      if (value instanceof Error) {
       resultCache.delete(key)
       throw value
      }
      return value as R
     }
    }
    일괄 처리를 실현하는 기본적인 사고방식은 다음과 같다.
    1. 맵 paramCache 캐시로 전송된 매개 변수 = > 나머지 호출 횟수 (이 매개 변수는 몇 번 더 결과를 조회해야 함)
    2. Map resultCache 캐시 매개 변수 => 결과 사용
    3. lock을 사용하여 현재 함수가 실행 중인지 여부를 표시합니다
    4. 다음 조건을 충족시키려면 기다려야 한다
    맵에 결과 없음
    현재 다른 호출이 실행 중입니다
    최소 대기 시간 미만 (호출된 최소 시간 세션 수집)
    5. lock 표시를 사용하여 실행 중
    6. 결과 존재 여부 판단
    존재하지 않으면 현재 모든 파라미터를 일괄 처리합니다
    7. 캐시맵에서 결과 얻기
    8. paramCache에 대응하는 매개 변수의 나머지 호출 횟수-1
    9. 캐시를 보존해야 하는지 판단(이 매개 변수에 대응하는 나머지 호출 횟수는 0)
    필요하지 않으면 삭제
    10. 캐시 결과가 Error인지 판단
    그렇다면 throw가 오류를 냈습니다.
    LRU 캐시
    참조: Wiki 캐시 알고리즘, 구현MemoryCache
    질문: 여기는 왜 캐시를 사용합니까?
    답: 이곳의 사전 인터페이스는 대략적인 확률상 멱등이기 때문에 캐시를 사용하여 성능을 향상시킬 수 있다
    Q: 캐시 정책은 왜 LRU를 선택해야 합니까?
    답: FIFO가 불합리하다는 것은 의심의 여지가 없다
    물음: 그럼 왜 LFU 알고리즘을 선택하지 않습니까?그것은 가장 빈번한 자원에 접근할 수 있을 것 같다
    답: 사전표가 완전한 멱등이 아니기 때문에, 우리는 C가 가장 많이 접근할 수 있는 사전이 계속 삭제되지 않고 데이터베이스에 업데이트되는 것을 피하고 싶습니다.
    대체로 사고방식을 다음과 같이 실현하다.
    1. Map 레코드 캐시 키 => 마지막 액세스 시간 사용하기
    2. 캐시를 가져올 때마다 마지막 액세스 시간 업데이트
    3. 새 캐시 추가 시 캐시 수량 확인
    최대 수를 초과하면 마지막 액세스 시간이 현재 가장 긴 캐시를 삭제합니다.
    4. 새 캐시 추가
    Pass: 욕하지 마세요. 성능이 나빠요. 이 장면에서 너무 많은 요소를 캐시하지 않아요. 최대 1000개도 안 돼요.
    결합 고급 함수
    이제 우리는 이 두 가지 방식을 결합할 수 있다. 동시에onceOfSameParam/batch 두 개의 고급 함수를 사용하여 id에 따라 사전 정보를 얻는 API를 최적화할 수 있다.
    
    const getById = onceOfSameParam(
     batch<[number], Dict>(async (idList) => {
      if (idList.length === 0) {
       return new Map()
      }
      //   id
      const list = await this.getByIdList(uniqueBy(idList.flat()))
      return arrayToMap(
       list,
       (dict) => [dict.id],
       (dict) => dict,
      )
     }, 100),
    )
    비동기식 포맷터 지원
    ListTable의 비동기 formatter 함수를 지원하려고 했는데, 나중에 생각해 보니 slot 에도 사전 id가 포함되어 있다면?그렇다면 slot 비동기화도 지원해야 하는가?이것은 비교적 까다로운 문제이기 때문에 그래도 지지하지 않는 것이 좋다.
    마지막으로, 당사는 구성 요소와 API 사이에 *Service 중간층을 추가하여 데이터 변환을 처리합니다.
    다음은 JavaScript의 일괄 처리와 캐시에 대한 상세한 내용입니다. JavaScript의 일괄 처리와 캐시에 대한 더 많은 자료는 저희의 다른 관련 글을 주목해 주십시오!

    좋은 웹페이지 즐겨찾기