【问题标题】:Fastest way to keep data in memory with Redis in Python在 Python 中使用 Redis 将数据保存在内存中的最快方法
【发布时间】:2019-02-17 06:26:50
【问题描述】:

我需要使用 Python 3 在烧瓶应用程序中保存一次并多次加载一些大数组。我最初使用 json 库将这些数组存储在磁盘上。为了加快速度,我在同一台机器上使用 Redis 通过将数组序列化为 JSON 字符串来存储数组。我想知道为什么我没有得到任何改进(实际上在我使用的服务器上需要更多时间),而 Redis 将数据保存在 RAM 中。我猜 JSON 序列化没有优化,但我不知道如何加快速度:

import json
import redis
import os 
import time

current_folder = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(current_folder, "my_file")

my_array = [1]*10000000

with open(file_path, 'w') as outfile:
    json.dump(my_array, outfile)

start_time = time.time()
with open(file_path, 'r') as infile:
    my_array = json.load(infile)
print("JSON from disk  : ", time.time() - start_time)

r = redis.Redis()
my_array_as_string = json.dumps(my_array)
r.set("my_array_as_string", my_array_as_string)

start_time = time.time()
my_array_as_string = r.get("my_array_as_string")
print("Fetch from Redis:", time.time() - start_time)

start_time = time.time()
my_array = json.loads(my_array_as_string)
print("Parse JSON      :", time.time() - start_time)

结果:

JSON from disk  : 1.075700044631958
Fetch from Redis: 0.078125
Parse JSON      : 1.0247752666473389

编辑:貌似从redis中获取其实很快,但是解析JSON却很慢。有没有办法在没有 JSON 序列化部分的情况下直接从 Redis 获取数组?这就是我们使用 pyMySQL 所做的,而且速度很快。

【问题讨论】:

  • 在我的脑海中,我会说磁盘版本由于磁盘缓存而人为地快速。例如,请参阅here。编写好的基准很难。
  • 我在 196 Gb RAM linux 上加载了近 10 GB 的数据,你认为操作系统缓存了大部分数据吗?
  • “通常,所有未直接分配给应用程序的物理内存都被操作系统用于page cache。”
  • Thx,我更新了我的问题更具体,Redis 访问数据实际上要快得多,但是因为我将数据存储为 JSON 字符串,所以解析部分真的很慢。我正在寻找一种直接在 python 对象中获取数据的方法,就像我们对 pyMySQL 所做的那样。
  • 在字节流和内存中的 Python 对象之间总是有一个转换步骤。也就是说,众所周知,JSON 很慢,因此您可以随时尝试 msgpack 甚至 pickle。

标签: python database performance redis


【解决方案1】:

更新:2019 年 11 月 8 日 - 在 Python3.6 上运行相同的测试

结果:

转储时间:JSON > msgpack > pickle > marshal
加载时间:JSON > pickle > msgpack > marshal
空格: marshal > JSON > pickle > msgpack

+---------+-----------+-----------+-------+
| package | dump time | load time | size  |
+---------+-----------+-----------+-------+
| json    | 0.00134   | 0.00079   | 30049 |
| pickle  | 0.00023   | 0.00019   | 20059 |
| msgpack | 0.00031   | 0.00012   | 10036 |
| marshal | 0.00022   | 0.00010   | 50038 |
+---------+-----------+-----------+-------+

我试过pickle vs json vs msgpack vs marshal。

Pickle 比 JSON 快得多。 msgpack 至少比 JSON 快 4 倍。 MsgPack 看起来是您拥有的最佳选择。

编辑: 也试过元帅。 Marshal 比 JSON 快,但比 msgpack 慢。

耗时:Pickle > JSON > Marshal > MsgPack
占用空间:Marshal > Pickle > Json > MsgPack

import time
import json
import pickle
import msgpack
import marshal
import sys

array = [1]*10000

start_time = time.time()
json_array = json.dumps(array)
print "JSON dumps: ", time.time() - start_time
print "JSON size: ", sys.getsizeof(json_array)
start_time = time.time()
_ = json.loads(json_array)
print "JSON loads: ", time.time() - start_time

# --------------

start_time = time.time()
pickled_object = pickle.dumps(array)
print "Pickle dumps: ", time.time() - start_time
print "Pickle size: ", sys.getsizeof(pickled_object)
start_time = time.time()
_ = pickle.loads(pickled_object)
print "Pickle loads: ", time.time() - start_time


# --------------

start_time = time.time()
package = msgpack.dumps(array)
print "Msg Pack dumps: ", time.time() - start_time
print "MsgPack size: ", sys.getsizeof(package)
start_time = time.time()
_ = msgpack.loads(package)
print "Msg Pack loads: ", time.time() - start_time

# --------------

start_time = time.time()
m_package = marshal.dumps(array)
print "Marshal dumps: ", time.time() - start_time
print "Marshal size: ", sys.getsizeof(m_package)
start_time = time.time()
_ = marshal.loads(m_package)
print "Marshal loads: ", time.time() - start_time

结果:

    JSON dumps:  0.000760078430176
JSON size:  30037
JSON loads:  0.000488042831421
Pickle dumps:  0.0108790397644
Pickle size:  40043
Pickle loads:  0.0100247859955
Msg Pack dumps:  0.000202894210815
MsgPack size:  10040
Msg Pack loads:  7.58171081543e-05
Marshal dumps:  0.000118017196655
Marshal size:  50042
Marshal loads:  0.000118970870972

【讨论】:

  • 确实,msgpack 快了大约 4 倍。由于我一直在寻找更通用的答案,因此我稍等片刻,但是您的答案很有帮助。从 Redis 获取:0.023797988891601562 解析 msgpack:0.17844223976135254
  • 从您的打印 cmets 来看,您使用的是 Python 2,其中 pickle 很慢,建议您使用带有“import cPickle as pickle”的 C 版本。在 Python 3.7 上,我得到以下保存和加载时间: - 使用 json:0.739 + 0.584 毫秒,30049 字节。 - 使用 ujson:0.265 + 0.136 毫秒,20050 字节。 - 使用泡菜:0.188 + 0.132 毫秒,20059 字节。 - 使用 msgpack:0.317 + 0.059 毫秒,10036 字节。 - 使用元帅:0.154 + 0.081 毫秒,50038 字节。当然,如果要存储大型同构数组,请使用 numpy 和 pickle: - 使用 pickle 的 Numpy 数组:0.016 + 0.000 ms,40192 字节。
【解决方案2】:

一些解释:

  1. 从磁盘加载数据并不总是意味着磁盘访问,通常是从内存中操作系统缓存返回的数据,当这种情况发生时,这甚至比从 Redis 获取数据更快(从总时间中删除网络通信)

  2. 主要的性能杀手是 JSON 解析(cpt. Obvious)

  3. 从磁盘解析 JSON 很可能与数据加载(从文件流)并行完成

  4. 没有使用 Redis 从流中解析的选项(至少我不知道这样的 API)


只需将缓存文件存储在tmpfs 上,您就可以通过最小的更改来加速应用程序。这与在同一台服务器上设置 Redis 非常接近。

同意@RoopakANelliat msgpack 比 JSON 快大约 4 倍。格式更改将提高解析性能(如果可能的话)。

【讨论】:

    【解决方案3】:

    我专门为此创建了brain-plasma - 在 Flask 应用程序中快速加载和重新加载大对象。它是 Apache Arrow 可序列化对象的共享内存对象命名空间,包括由pickle.dumps(...) 生成的pickle'd 字节串。

    $ pip install brain-plasma
    $ plasma_store -m 10000000 -s /tmp/plasma # 10MB memory
    from brain_plasma import Brain
    brain = Brain()
    
    brain['a'] = [1]*10000
    brain['a']
    # >>> [1,1,1,1,...]
    
    

    【讨论】:

      【解决方案4】:

      Redis 的 RedisJson 扩展:https://oss.redislabs.com/redisjson/

      【讨论】:

        猜你喜欢
        • 2019-08-14
        • 2015-10-07
        • 2011-11-03
        • 1970-01-01
        • 2016-01-21
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        相关资源
        最近更新 更多