【问题标题】:Which is the best way to compress json to store in a memory based store like redis or memcache?压缩 json 以存储在 redis 或 memcache 等基于内存的存储中的最佳方法是哪种?
【发布时间】:2023-04-05 05:48:01
【问题描述】:

要求: 具有 2-3 级嵌套的 Python 对象,其中包含整数、字符串、列表和字典等基本数据类型。 (没有日期等),需要在 redis 中针对密钥存储为 json。 将 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/ 我也可以考虑泡菜之类的选择吗?

【问题讨论】:

  • 您的申请有什么要求?你需要性能吗?可靠性、一致性等?你会考虑redis的替代品吗?

标签: python json redis msgpack


【解决方案1】:

另一种可能是使用 MongoDB 的存储格式,BSON

您可以在该站点的实现页面中找到两个 python 实现。

编辑:为什么不直接保存字典,并在检索时转换为 json?

【讨论】:

  • 我不认为 BSON 可以作为 redis 中键的值添加。
  • @DhruvPathak 肯定可以,为什么不呢? Redis 对您在密钥中存储的内容没有意见。
  • @JonatanHedborg 感谢您的更正。我没有注意redis字符串是二进制安全的。
  • 但是,BSON 并不比 JSON 更紧凑(如他们网站上所述),所以它不是一个真正的选择。
【解决方案2】:

我们只是使用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() 对象来替换文件。

【讨论】:

  • 使用with gzip.GzipFile(fileobj=stream, mode='w') as compressor:更好吗?这在通常的open python 函数中将允许在循环停止的情况下更接近文件。
【解决方案3】:

一种简单的“后处理”方法是构建一个“短键名”映射,并在存储之前通过该映射运行生成的 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,在去应用程序的路上替换value->key。

您还可以使用 gzip 来获得额外的好处(但之后不会是字符串)。

【讨论】:

    【解决方案4】:

    如果您希望它更快,try lz4。 如果你想让它压缩得更好,go for lzma

    还有其他更好的方法来压缩 json 以节省内存 redis(也确保之后的轻量级解码)?

    候选人的 msgpack 有多好 [http://msgpack.org/]?

    Msgpack 相对较快且内存占用量较小。但是ujson 对我来说通常更快。 您应该在数据上比较它们,测量压缩和解压缩率以及压缩率。

    我也应该考虑泡菜之类的选择吗?

    同时考虑pickle(特别是cPickle)和marshal。他们很快。但请记住,它们既不安全也不可扩展,您需要为速度付出额外的责任。

    【讨论】:

      【解决方案5】:

      基于上面@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))
      

      【讨论】:

        【解决方案6】:

        我对不同的二进制格式(MessagePack、BSON、Ion、Smile CBOR)和压缩算法(Brotli、Gzip、XZ、Zstandard、bzip2)进行了广泛的比较。

        对于我用于测试的 JSON 数据,将数据保留为 JSON 并使用 Brotli 压缩是最好的解决方案。 Brotli 具有不同的压缩级别,因此如果您要长时间保留数据,那么使用高级别压缩是值得的。如果您不坚持很长时间,那么较低级别的压缩或使用 Zstandard 可能是最有效的。

        Gzip 很简单,但几乎可以肯定会有更快的替代方案,或提供更好的压缩,或两者兼而有之。

        您可以在此处阅读我们调查的全部详细信息:Blog Post

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-04
          • 2014-01-13
          • 1970-01-01
          • 1970-01-01
          • 2019-09-05
          • 1970-01-01
          • 2010-11-30
          相关资源
          最近更新 更多