web-dev-qa-db-ja.com

jsonを圧縮してredisやmemcacheなどのメモリベースのストアに保存するための最良の方法はどれですか?

要件:Python整数、文字列、リスト、辞書などの基本的なdatypeを含む2〜3レベルのネストを持つオブジェクト(日付などなし)は、キーに対して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 /ピクルスのようなオプションも検討しますか?

13
DhruvPathak

コンプレッサーとして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()オブジェクトを使用することもできます。

14
Alfe

@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
AlaskaJoslin

高速にしたい場合は、 lz4を試してください 。より良く圧縮したい場合は、 lzmaに行く

Jsonを圧縮してredisのメモリを節約する(後で軽量のデコードを保証する)他のより良い方法はありますか?

候補者はmsgpack [ http://msgpack.org/] でどれほど良いですか?

Msgpackは比較的高速で、メモリフットプリントが小さくなっています。しかし json は一般的に私にとっては速いです。データでそれらを比較し、圧縮率と解凍率、および圧縮率を測定する必要があります。

ピクルスのようなオプションも検討しますか?

ピクルス(特にcPickle)とマーシャルの両方を検討してください。彼らは速いです。ただし、それらは安全でもスケーラブルでもないことを忘れないでください。追加の責任で速度を支払う必要があります。

3
gukoff

簡単な「後処理」方法の1つは、「短いキー名」マップを作成し、生成された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でさらに優れたものにすることもできます(ただし、その後は文字列にはなりません)。

2
Jonatan Hedborg

さまざまなバイナリ形式(MessagePack、BSON、Ion、Smile CBOR)と圧縮アルゴリズム(Brotli、Gzip、XZ、Zstandard、bzip2)の間でいくつかの広範な比較を行いました。

テストに使用したJSONデータの場合、データをJSONとして保持し、Brotli圧縮を使用することが最善の解決策でした。 Brotliにはさまざまな圧縮レベルがあるため、データを長期間保持している場合は、高レベルの圧縮を使用する価値があります。あまり長く持続しない場合は、低レベルの圧縮またはZstandardの使用が最も効果的である可能性があります。

Gzipは簡単ですが、より高速であるか、より優れた圧縮を提供するか、またはその両方である代替手段がほぼ確実にあります。

あなたはここで私たちの調査の完全な詳細を読むことができます: ブログ投稿

1
Richard Shurtz

もう1つの可能性は、MongoDBのストレージ形式 [〜#〜] bson [〜#〜] を使用することです。

そのサイトの実装ページに2つのpython実装があります。

編集:辞書を保存し、取得時にjsonに変換しないのはなぜですか?

0
Ivo