1

所以我有一个字典,它是我从 Redis 获取的哈希对象,类似于以下字典:

source_data = {
   b'key-1': b'{"age":33,"gender":"Male"}', 
   b'key-2': b'{"age":20,"gender":"Female"}'
}

我的目标是从该字典中提取所有值,并将它们作为 Python 字典列表,如下所示:

final_data = [
   {
      'age': 33,
      'gender': 'Male'
   },

   {
      'age': 20,
      'gender': 'Female'
   }
]

我尝试使用 json 解析进行列表理解:

import json
final_data = [json.loads(a) for a in source_data.values()]

它有效,但对于大型数据集,它需要太多时间。

我切换到使用这个 3rd 方 json 模块ujson,根据这个基准它更快,但我没有注意到任何改进。

我尝试使用多线程:

pool = Pool()
final_data = pool.map(ujson.loads, source_data.values(), chunksize=500)

pool.close()
pool.join()

我玩了一点,chunksize但结果是一样的,仍然花费了太多时间。

如果有人可以建议其他解决方案或改进以前的尝试,那将非常有帮助,如果我可以避免使用循环,那将是理想的。

4

2 回答 2

4

假设这些值确实是有效的 JSON,那么构建单个JSON 对象进行解码可能会更快。我认为将值加入单个字符串应该是安全的。

>>> new_json = b'[%s]' % (b','.join(source_data.values(),)
>>> new_json
b'[{"age":33,"gender":"Male"},{"age":20,"gender":"Female"}]'
>>> json.loads(new_json)
[{'age': 33, 'gender': 'Male'}, {'age': 20, 'gender': 'Female'}]

这将调用json.loads2000+ 次的开销替换为单个调用b','.join和单个字符串格式化操作的较小开销。

于 2018-07-03T13:52:52.980 回答
1

作为参考,我尝试复制这种情况:

import json, timeit, random
source_data = { 'key-{}'.format(n).encode('ascii'): 
                '{{"age":{},"gender":"{}"}}'.format(
                    random.randint(18,75), 
                    random.choice(("Male", "Female"))
                 ).encode('ascii') 
               for n in range(45000) }
timeit.timeit("{ k: json.loads(v) for (k,v) in source_data.items() }", 
    number=1, globals={'json': json, 'source_data': source_data})

这在不到一秒钟的时间内完成。那些超过 30 秒的时间一定来自我没有看到的东西。

我最接近的猜测是您将数据放在某种代理容器中,其中每个键获取都变成了远程调用,例如使用hscan而不是hgetall. 使用count提示应该可以在两者之间进行权衡hscan

正确的分析应该揭示延迟的来源。

于 2018-07-03T14:32:16.473 回答