在 Python 中使用 Redis 将数据保存在内存中的最快方法



我需要在使用 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所做的,它很快。

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

结果:

转储时间:JSON> msgpack> pickle> marshal

load time: JSON> pickle> msgpack> marshal
Space: 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 |
+---------+-----------+-----------+-------+

我尝试了泡菜 vs json vs msgpack vs 元帅。

Pickle 比 JSON 快得多。msgpack至少比JSON快4倍。 MsgPack看起来像你最好的选择。

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

所用时间:泡菜> JSON>元帅>MsgPack
占用空间:元帅>泡菜> 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

一些解释:

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

  2. 主要的性能杀手是 JSON 解析(显而易见(

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

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


您只需将缓存文件存储在 tmpfs 上,只需进行最少的更改即可加快应用程序速度。它非常接近同一服务器上的 Redis 设置。

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

我专门为这个原因制作了脑血浆 - 在 Flask 应用程序中快速加载和重新加载大型物体。它是 Apache Arrow 可序列化对象的共享内存对象命名空间,包括由pickle.dumps(...)生成的pickle字节串。

$ 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,...]

最新更新