복붙노트

[REDIS] 캐싱 애플리케이션에서 디스크 대 레디 스의 성능

REDIS

캐싱 애플리케이션에서 디스크 대 레디 스의 성능

파이썬에서 레디 스 캐시를 만들고 싶었, 어떤 자기 존중 과학자로 나는 성능을 테스트하는 벤치 마크를 만들었다.

흥미롭게도, 레디 스 너무 잘 지내지 않았다. 어느 파이썬 뭔가 마법 (파일 저장) 또는 레디 스의 내 버전을하는 일은 엄청나게 느리다.

이 때문에 내 코드의 구조 방법, 또는 무엇 인 경우는 모르겠지만, 나는 레디 스 그것보다 더 잘 할 것으로 예상했다.

레디 스 캐시하려면 나는 5 분의 만료와 파일 이름에서 파생 된 키 (이 경우, HTML 페이지에) 내 바이너리 데이터를 설정합니다.

모든 경우에, 파일 처리 (이 빨리 f.readlines () 이상 ~ 3 배를하고, 나는 이진 BLOB 필요) () f.read로 이루어집니다.

내 비교에서 누락이 무엇인가, 아니면 레디 스 정말 아무 디스크에 대한 일치하지 않습니다? 파이썬은 파일 곳을 캐싱하고, 매번 reaccessing 있습니까? 왜 이런 일이 레디 스에 대한 접근보다 훨씬 빠르다?

내가 레디 스 2.8, 파이썬 2.7을 사용하고-평을 레디 스있어, 64의 모든 우분투 시스템을 비트.

내가 파이썬 객체의 파일 데이터를 저장하는 기능을 만들어 영원히 열매를 산출 나는 파이썬 특히 마법의 아무것도 생각하지 않습니다.

나는 그룹화하는 것이 네 개의 함수 호출이 :

파일 X 시간을 읽기

레디 스 객체가 메모리에 남아 있는지 확인하기 위해 호출되는 함수는로드, 또는 캐시 새 파일 (단일 및 다중 레디 스 인스턴스).

(레디 스의 단일 및 다중 인스턴스)가 레디 스 데이터베이스로부터의 결과를 얻을 수 생성기를 생성하는 함수.

그리고 마지막으로, 메모리에 파일을 저장하고 영원히 열매를 산출.

import redis
import time

def load_file(fp, fpKey, r, expiry):
    with open(fp, "rb") as f:
        data = f.read()
    p = r.pipeline()
    p.set(fpKey, data)
    p.expire(fpKey, expiry)
    p.execute()
    return data

def cache_or_get_gen(fp, expiry=300, r=redis.Redis(db=5)):
    fpKey = "cached:"+fp

    while True:
        yield load_file(fp, fpKey, r, expiry)
        t = time.time()
        while time.time() - t - expiry < 0:
            yield r.get(fpKey)


def cache_or_get(fp, expiry=300, r=redis.Redis(db=5)):

    fpKey = "cached:"+fp

    if r.exists(fpKey):
        return r.get(fpKey)

    else:
        with open(fp, "rb") as f:
            data = f.read()
        p = r.pipeline()
        p.set(fpKey, data)
        p.expire(fpKey, expiry)
        p.execute()
        return data

def mem_cache(fp):
    with open(fp, "rb") as f:
        data = f.readlines()
    while True:
        yield data

def stressTest(fp, trials = 10000):

    # Read the file x number of times
    a = time.time()
    for x in range(trials):
        with open(fp, "rb") as f:
            data = f.read()
    b = time.time()
    readAvg = trials/(b-a)


    # Generator version

    # Read the file, cache it, read it with a new instance each time
    a = time.time()
    gen = cache_or_get_gen(fp)
    for x in range(trials):
        data = next(gen)
    b = time.time()
    cachedAvgGen = trials/(b-a)

    # Read file, cache it, pass in redis instance each time
    a = time.time()
    r = redis.Redis(db=6)
    gen = cache_or_get_gen(fp, r=r)
    for x in range(trials):
        data = next(gen)
    b = time.time()
    inCachedAvgGen = trials/(b-a)


    # Non generator version    

    # Read the file, cache it, read it with a new instance each time
    a = time.time()
    for x in range(trials):
        data = cache_or_get(fp)
    b = time.time()
    cachedAvg = trials/(b-a)

    # Read file, cache it, pass in redis instance each time
    a = time.time()
    r = redis.Redis(db=6)
    for x in range(trials):
        data = cache_or_get(fp, r=r)
    b = time.time()
    inCachedAvg = trials/(b-a)

    # Read file, cache it in python object
    a = time.time()
    for x in range(trials):
        data = mem_cache(fp)
    b = time.time()
    memCachedAvg = trials/(b-a)


    print "\n%s file reads: %.2f reads/second\n" %(trials, readAvg)
    print "Yielding from generators for data:"
    print "multi redis instance: %.2f reads/second (%.2f percent)" %(cachedAvgGen, (100*(cachedAvgGen-readAvg)/(readAvg)))
    print "single redis instance: %.2f reads/second (%.2f percent)" %(inCachedAvgGen, (100*(inCachedAvgGen-readAvg)/(readAvg)))
    print "Function calls to get data:"
    print "multi redis instance: %.2f reads/second (%.2f percent)" %(cachedAvg, (100*(cachedAvg-readAvg)/(readAvg)))
    print "single redis instance: %.2f reads/second (%.2f percent)" %(inCachedAvg, (100*(inCachedAvg-readAvg)/(readAvg)))
    print "python cached object: %.2f reads/second (%.2f percent)" %(memCachedAvg, (100*(memCachedAvg-readAvg)/(readAvg)))

if __name__ == "__main__":
    fileToRead = "templates/index.html"

    stressTest(fileToRead)

그리고 지금 결과 :

10000 file reads: 30971.94 reads/second

Yielding from generators for data:
multi redis instance: 8489.28 reads/second (-72.59 percent)
single redis instance: 8801.73 reads/second (-71.58 percent)
Function calls to get data:
multi redis instance: 5396.81 reads/second (-82.58 percent)
single redis instance: 5419.19 reads/second (-82.50 percent)
python cached object: 1522765.03 reads/second (4816.60 percent)

결과 파이썬 읽은 엄청나게 빠른 개체) 느린 디스크로부터 판독보다 레디 스, 및 c) 빠른 기능마다 호출보다 B임을 a) 발전기 흥미.

왜 디스크에서 읽기는 레디 스에서 메모리 파일 읽기보다 훨씬 빠른 것?

편집하다: 일부 자세한 내용과 테스트합니다.

내가 할 수있는 기능을 대체

data = r.get(fpKey)
if data:
    return r.get(fpKey)

결과에서 많은 차이가 없습니다

if r.exists(fpKey):
    data = r.get(fpKey)


Function calls to get data using r.exists as test
multi redis instance: 5320.51 reads/second (-82.34 percent)
single redis instance: 5308.33 reads/second (-82.38 percent)
python cached object: 1494123.68 reads/second (5348.17 percent)


Function calls to get data using if data as test
multi redis instance: 8540.91 reads/second (-71.25 percent)
single redis instance: 7888.24 reads/second (-73.45 percent)
python cached object: 1520226.17 reads/second (5132.01 percent)

각 함수 호출에 새로운 레디 스 인스턴스를 만들기 실제로 읽기 속도에 눈에 띄는 효과가없는, 시험에 대한 시험의 변화는 이득보다 크다.

Sripathi Krishnan 임의의 파일 읽기 구현 제안했다. 정말 도움말을 시작 캐싱 여기서 우리는 이러한 결과에서 볼 수 있듯이이있다.

Total number of files: 700

10000 file reads: 274.28 reads/second

Yielding from generators for data:
multi redis instance: 15393.30 reads/second (5512.32 percent)
single redis instance: 13228.62 reads/second (4723.09 percent)
Function calls to get data:
multi redis instance: 11213.54 reads/second (3988.40 percent)
single redis instance: 14420.15 reads/second (5157.52 percent)
python cached object: 607649.98 reads/second (221446.26 percent)

파일에 변화의 거대한 양의가 퍼센트 차이는 속도 향상의 좋은 지표되지 않도록 읽기입니다.

Total number of files: 700

40000 file reads: 1168.23 reads/second

Yielding from generators for data:
multi redis instance: 14900.80 reads/second (1175.50 percent)
single redis instance: 14318.28 reads/second (1125.64 percent)
Function calls to get data:
multi redis instance: 13563.36 reads/second (1061.02 percent)
single redis instance: 13486.05 reads/second (1054.40 percent)
python cached object: 587785.35 reads/second (50214.25 percent)

내가 사용 random.choice (파일 목록) 무작위로 기능을 통해 각 패스에 새 파일을 선택합니다.

전체 요점은 누군가가 그것을 밖으로 시도하려는 경우 여기에있다 - https://gist.github.com/3885957

편집 편집 : (함수 호출 및 발전기의 성능이 매우 유사하지만) 나는 발전기에 대한 하나 개의 파일을 호출하는 것을 몰랐어요. 여기뿐만 아니라 발전기에서 다른 파일의 결과입니다.

Total number of files: 700
10000 file reads: 284.48 reads/second

Yielding from generators for data:
single redis instance: 11627.56 reads/second (3987.36 percent)

Function calls to get data:
single redis instance: 14615.83 reads/second (5037.81 percent)

python cached object: 580285.56 reads/second (203884.21 percent)

해결법

  1. ==============================

    1.이 오렌지 비교에 사과입니다. http://redis.io/topics/benchmarks 참조

    이 오렌지 비교에 사과입니다. http://redis.io/topics/benchmarks 참조

    레디 스 효율적인 원격 데이터 저장소이다. 명령이 레디 스에서 실행 때마다 메시지가 레디 스 서버로 전송하고, 클라이언트가 동기 인 경우, 그 블록은 응답을 기다리는. 명령 자체의 비용을 넘어 그래서, 당신은 네트워크 왕복 또는 IPC에 대해 지불 할 것입니다.

    현대 하드웨어, 네트워크 왕복 또는 IPC를 다른 작업에 비해 놀라 울 정도로 비싸다. 이것은 여러 가지 요인 때문이다 :

    이제, 결과를 검토 할 수 있습니다.

    발전기 및 함수 호출을 사용하여 하나를 사용하여 구현을 비교, 그들은 레디 스에 왕복 동일한 번호를 생성하지 않습니다. 발전기를 사용하면 간단하게 가지고 :

        while time.time() - t - expiry < 0:
            yield r.get(fpKey)
    

    반복 당 1 왕복 그래서. 기능과 함께, 당신은 :

    if r.exists(fpKey):
        return r.get(fpKey)
    

    반복 당 2 왕복 그래서. 당연히 발전기는 빠른 없다.

    물론 당신은 최적의 성능을 위해 같은 레디 스 연결을 다시 사용하기로되어있다. 체계적 / 단절을 연결하는 벤치 마크를 실행하는 데 아무 문제가 없었다.

    마지막으로, 레디 스 사이의 성능 차이에 대한 것은 호출하고 파일은 단순히 원격 하나에 시내 전화를 비교하고, 읽습니다. 파일은 OS 파일 시스템에 의해 캐시 읽기, 그래서 그들은 커널과 파이썬 사이의 빠른 메모리 전송 작업이다. I / O가 여기에 포함 된 디스크가 없습니다. 레디 스, 당신은 그래서 훨씬 느립니다의 왕복의 비용을 지불해야한다.

  2. from https://stackoverflow.com/questions/12868222/performance-of-redis-vs-disk-in-caching-application by cc-by-sa and MIT license