要求: 具有2-3级嵌套的Python对象,包含整数,字符串,列表和dicts等基本数据类型。 (没有日期等),需要作为json存储在redis中以对照密钥。 什么是可用于将json压缩为低内存占用的字符串的最佳方法。 目标对象不是很大,平均有1000个小元素, 转换为JSON时大约15000个字符。
例如
>>> my_dict
{'details': {'1': {'age': 13, 'name': 'dhruv'}, '2': {'age': 15, 'name': 'Matt'}}, 'members': ['1', '2']}
>>> json.dumps(my_dict)
'{"details": {"1": {"age": 13, "name": "dhruv"}, "2": {"age": 15, "name": "Matt"}}, "members": ["1", "2"]}'
### SOME BASIC COMPACTION ###
>>> json.dumps(my_dict, separators=(',',':'))
'{"details":{"1":{"age":13,"name":"dhruv"},"2":{"age":15,"name":"Matt"}},"members":["1","2"]}'
1 /还有其他更好的方法来压缩json以节省redis内存(同时确保轻量级解码)。
2 / msgpack [http://msgpack.org/]的候选人有多好? 3 /我还要考虑像pickle这样的选项吗?答案 0 :(得分:12)
我们只使用gzip
作为压缩器。
import gzip
import cStringIO
def decompressStringToFile(value, outputFile):
"""
decompress the given string value (which must be valid compressed gzip
data) and write the result in the given open file.
"""
stream = cStringIO.StringIO(value)
decompressor = gzip.GzipFile(fileobj=stream, mode='r')
while True: # until EOF
chunk = decompressor.read(8192)
if not chunk:
decompressor.close()
outputFile.close()
return
outputFile.write(chunk)
def compressFileToString(inputFile):
"""
read the given open file, compress the data and return it as string.
"""
stream = cStringIO.StringIO()
compressor = gzip.GzipFile(fileobj=stream, mode='w')
while True: # until EOF
chunk = inputFile.read(8192)
if not chunk: # EOF?
compressor.close()
return stream.getvalue()
compressor.write(chunk)
在我们的用例中,我们将结果存储为文件,如您所想。要仅使用内存中的字符串,您还可以使用cStringIO.StringIO()
对象替换文件。
答案 1 :(得分:4)
基于上述@Alfe的answer,是一个将内容保留在内存中的版本(用于网络I / O任务)。我还做了一些更改以支持Python 3。
import gzip
from io import StringIO, BytesIO
def decompressBytesToString(inputBytes):
"""
decompress the given byte array (which must be valid
compressed gzip data) and return the decoded text (utf-8).
"""
bio = BytesIO()
stream = BytesIO(inputBytes)
decompressor = gzip.GzipFile(fileobj=stream, mode='r')
while True: # until EOF
chunk = decompressor.read(8192)
if not chunk:
decompressor.close()
bio.seek(0)
return bio.read().decode("utf-8")
bio.write(chunk)
return None
def compressStringToBytes(inputString):
"""
read the given string, encode it in utf-8,
compress the data and return it as a byte array.
"""
bio = BytesIO()
bio.write(inputString.encode("utf-8"))
bio.seek(0)
stream = BytesIO()
compressor = gzip.GzipFile(fileobj=stream, mode='w')
while True: # until EOF
chunk = bio.read(8192)
if not chunk: # EOF?
compressor.close()
return stream.getvalue()
compressor.write(chunk)
要测试压缩率,请尝试:
inputString="asdf" * 1000
len(inputString)
len(compressStringToBytes(inputString))
decompressBytesToString(compressStringToBytes(inputString))
答案 2 :(得分:3)
如果你希望它快,try lz4。 如果您希望它更好地压缩,go for lzma。
还有其他更好的方法来压缩json以节省内存 redis(之后也确保轻量级解码)?
候选人有多好_msgpack [http://msgpack.org/]?
Msgpack速度相对较快,占用内存较小。但ujson对我来说通常更快。 您应该对数据进行比较,测量压缩率和减压率以及压缩率。
我还要考虑像pickle这样的选项吗?
考虑泡菜(pPickle in partucular)和marshal。他们很快。但请记住,它们并不安全或可扩展,您需要为增加的责任付出代价。
答案 3 :(得分:2)
一种简单的“后期处理”方式是构建“短密钥名称”映射并在存储之前运行生成的json,并在反序列化到对象之前再次(反转)。例如:
Before: {"details":{"1":{"age":13,"name":"dhruv"},"2":{"age":15,"name":"Matt"}},"members":["1","2"]}
Map: details:d, age:a, name:n, members:m
Result: {"d":{"1":{"a":13,"n":"dhruv"},"2":{"a":15,"n":"Matt"}},"m":["1","2"]}
只需浏览json并在前往数据库的途中替换key->值,并在前往应用程序的途中替换value->键。
你也可以gzip获得额外的好处(虽然之后不会是字符串)。
答案 4 :(得分:1)
我在不同的二进制格式(MessagePack,BSON,Ion,Smile CBOR)和压缩算法(Brotli,Gzip,XZ,Zstandard,bzip2)之间进行了广泛的比较。
对于我用于测试的JSON数据,将数据保留为JSON并使用Brotli压缩是最好的解决方案。 Brotli具有不同的压缩级别,因此,如果长时间保留数据,那么使用高压缩级别是值得的。如果您坚持不了很长时间,那么较低级别的压缩或使用Zstandard可能是最有效的。
Gzip很简单,但是几乎可以肯定会有替代品,它们要么更快,要么提供更好的压缩率,或者两者兼而有之。
您可以在这里阅读我们调查的全部详细信息:Blog Post
答案 5 :(得分:0)