【问题标题】:How to insert Billion of data to Redis efficiently?如何高效地向 Redis 插入 10 亿条数据?
【发布时间】:2019-09-29 17:29:48
【问题描述】:

我有大约 20 亿个键值对,我希望将它们高效地加载到 Redis 中。我目前正在使用 Python 并使用 redis-py 记录的 Pipe。如何加快以下方法的速度?

import redis

def load(pdt_dict):
    """
    Load data into redis.

    Parameters
    ----------
    pdt_dict : Dict[str, str]
        To be stored in Redis
    """
    redIs = redis.Redis()
    pipe = redIs.pipeline()
    for key in pdt_dict.keys():
        pipe.hmset(self.seller + ":" + str(key), pdt_dict[key])
    pipe.execute()

【问题讨论】:

  • 您实际上并没有使用管道。打电话给pipe.hmset 而不是redIs.hmset
  • 哎呀,这只是输入错误.....请原谅我的错误....你能指导我如何实现加速吗??
  • @JohnDeep 请提供实际代码然后
  • 你好@ItamarHaber 我刚刚更新了完整的功能

标签: python redis redis-py


【解决方案1】:

关于问题和示例代码的几点。

  1. 流水线不是灵丹妙药 - 在使用它之前,您需要了解它的作用。流水线的作用是批量发送多个操作,以及它们从服务器的响应。您获得的是每个操作的网络往返时间都被批处理的时间所取代。但是无限大的批次是真正的资源消耗——你需要保持它们的大小足够小才能有效。根据经验,我通常尝试将每个管道的目标设置为 60KB,并且由于每个数据都不同,因此管道中实际操作的数量也是如此。假设您的密钥及其值约为 1KB,您需要每 60 次左右调用一次pipeline.execute()

  2. 除非我严重误解,否则这段代码不应该运行。您使用HMSET 就好像它是SET,所以您基本上缺少哈希的字段-> 值映射。哈希 (HMSET) 和字符串 (SET) 是不同的数据类型,因此应相应地使用。

  3. 看起来好像这个小循环负责整个“十亿数据” - 如果是这种情况,那么运行代码的服务器不仅会疯狂地交换,除非它有很多RAM 来保存字典,它也将非常无效(不管 Python 的速度)。您需要通过运行此进程的多个实例来并行化数据插入。

  4. 您是否正在远程连接到 Redis?如果是这样,网络可能会限制您的性能。

  5. 考虑您的 Redis 设置 - 假设它确实是一个瓶颈,也许可以调整/调整这些设置以获得更好的性能。

【讨论】:

    【解决方案2】:

    你可以在管道模式下使用 redis-cli,首先你准备一个类似的文件(注意这些行应该由 cr/lf 终止或由-d <dilimiter> 选项设置):

    SET Key0 Value0
    SET Key1 Value1
    ...
    SET KeyN ValueN
    

    然后将其通过管道传输到 redis-cli:

    cat data.txt | redis-cli --pipe
    

    https://redis.io/topics/mass-insert

    【讨论】:

    • 重要提示! data.txt不是一个包含redis命令的文件,它是一个包含redis protocol的文件。在此示例之前,文档说“创建一个包含 Redis 协议格式的以下命令的文件”。
    【解决方案3】:

    另一个考虑因素是,如果以下条件适用(来自 Redis Labs),在管道构造中设置 transaction=False 有助于提高性能:

    对于我们想要向 Redis 发送多个命令的情况, 一个命令的结果不会影响另一个命令的输入,我们 不需要它们都以事务方式执行,将 False 传递给 pipeline() 方法可以进一步提高 Redis 的整体性能。

    【讨论】:

      【解决方案4】:

      我希望你已经在 redis python 包旁边安装了hiredis python 包。请参阅https://github.com/andymccurdy/redis-py#parsers 它也应该可以提升您的性能。

      self.seller 做了什么?也许这是一个瓶颈?

      正如@Itamar 所说,尝试定期执行管道

      def load(pdtDict):
          redIs = redis.Redis()
          pipe = redIs.pipeline()
          n = 1
          for key in pdtDict.keys():
              pipe.hmset(self.seller+":"+str(key),pdtDict[key])
              n = n + 1
              if (n % 64) == 0:
                  pipe.execute()
                  pipe = redIs.pipeline()
      

      【讨论】:

      • 最好在 if 块内设置 n=0。由于问题提到了“十亿”数据。我认为在这种情况下保持如此大的计数没有任何用处。
      • 你需要在 for_loop 之后添加一个tailing pipe.execute() 命令,以确保你仍然缓存任何小于 64 的余数
      • 为什么每次执行后都要创建一个新的管道?只是想知道。
      【解决方案5】:

      要向 Redis 提供大量数据,请考虑使用 here 中描述的 redis 批量插入功能。

      为此,您需要访问 redis-cli。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-25
        • 1970-01-01
        • 1970-01-01
        • 2020-02-21
        • 2019-06-21
        • 2011-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多