相关文章推荐
独立的柚子  ·  mysql ...·  4 周前    · 
大鼻子的香烟  ·  handler - How to use ...·  1 年前    · 
刚分手的茶叶  ·  在 Python 3 中使用 ...·  1 年前    · 

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

6 人关注

我需要在一个使用Python 3的flask应用程序中保存一次并多次加载一些大数组。我最初用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)

Result:

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

EDIT谈到这个问题,我想说的是,从Redis获取数据的速度其实很快,但JSON解析的速度却相当慢。有没有一种方法可以直接从Redis中获取一个数组,而不需要JSON序列化部分?这就是我们用pyMySQL做的,而且速度很快。

5 个评论
我想说的是,由于磁盘缓存,磁盘版本的速度是人为的。请看 here ,比如说。编写好的基准是很难的。
我在196Gb内存的Linux上加载了近10GB的数据,你认为操作系统会缓存这些数据吗?
"通常情况下,所有没有直接分配给应用程序的物理内存都被操作系统用于 缓存页 ."
谢谢,我更新了我的问题,使之更加具体,Redis在访问数据方面实际上快得多,但由于我把数据存储为JSON字符串,解析部分真的很慢。我正在寻找一种方法来直接获取python对象中的数据,就像我们在pyMySQL中做的那样。
在字节流和内存中的Python对象之间总有一个转换步骤。也就是说,JSON被认为是很慢的,所以你总是可以尝试msgpack甚至pickle。
python
database
performance
redis
Robin
Robin
发布于 2018-09-12
3 个回答
Roopak A Nelliat
Roopak A Nelliat
发布于 2019-05-17
已采纳
0 人赞同

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

Results:

Dump Time : JSON > 味精包装 > pickle > marshal
装载时间 : JSON > pickle > 味精包装 > marshal
空间 : marshal > JSON > pickle > 味精包装

+---------+-----------+-----------+-------+
| 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 味精包装 vs marshal。

Pickle比JSON快得多。 而且味精包装至少比JSON快4倍。 MsgPack看起来是你的最佳选择。

Edit: Tried marshal also. Marshal is faster than JSON, but slower than 味精包装.

所需时间: Pickle > JSON > Marshal > MsgPack
空间 taken: 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

Result:

    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
从你的打印评论来看,你使用了Python 2,那里的pickle很慢,建议你使用C版本的'import cPickle as pickle'。在Python 3.7上,我得到的保存和加载时间如下。 - 使用json: 0.739 + 0.584 ms, 30049 bytes.- 使用ujson: 0.265 + 0.136 ms, 20050 bytes.- 使用pickle。0.188 + 0.132 ms, 20059 bytes.- 使用msgpack。0.317 + 0.059 ms, 10036 bytes.- 使用marshal:0.154 + 0.081 ms, 50038 bytes。当然,如果你要存储大型同质数组,可以使用numpy和pickle: - 使用pickle的Numpy数组。0.016 + 0.000 ms, 40192 bytes.
Andrii Muzalevskyi
Andrii Muzalevskyi
发布于 2019-05-17
0 人赞同

一些解释。

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

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

  • 来自磁盘的JSON解析很可能是与数据加载(来自文件流)同时进行的。

  • 没有选项可以用Redis从流中解析(至少我不知道这种API)。

  • 你可以用最小的改动来加速应用程序,只需将你的缓存文件存储在 tmpfs .它与同一服务器上的Redis设置相当接近。

    同意@RoopakANelliat的观点msgpack比JSON快4倍左右。格式的改变将提高解析性能(如果可能的话)。

    russellthehippo
    russellthehippo
    发布于 2019-05-17
    0 人赞同

    我做了 脑-血浆 这是一个专门用于Flask应用中大对象的快速加载和重载的命名空间。这是一个共享内存对象命名空间,用于Apache Arrow可序列化的对象,包括由 pickle 生成的字节符。

    $ pip install brain-plasma
    $ plasma_store -m 10000000 -s /tmp/plasma # 10MB memory
    from brain_plasma import Brain
    brain = Brain()