Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Lua 可能与redis+进行哈希相交;卢阿?_Lua_Redis_Lua Table - Fatal编程技术网

Lua 可能与redis+进行哈希相交;卢阿?

Lua 可能与redis+进行哈希相交;卢阿?,lua,redis,lua-table,Lua,Redis,Lua Table,我有一个用例,我需要计算许多集合之间的相似性来创建一个简单的推荐引擎。我在看Jaccard系数和其他相似系数公式,但它们之间有一个共同点:集合中的项目不能重复(如果我在这里错了,请纠正我) 我用PHP编写了自己的函数来进行自定义哈希交叉,逻辑如下: arr1:一个数组,其键是项的ID,值是其对应的数量。 这表示用户的愿望列表 arr2:与arr1相同,但它表示另一个用户的库存 对于这个自定义交叉点,我需要的逻辑是,愿望列表的所有者不关心卖家是否有100个item1。如果他只想要4个,那么只算4个

我有一个用例,我需要计算许多集合之间的相似性来创建一个简单的推荐引擎。我在看Jaccard系数和其他相似系数公式,但它们之间有一个共同点:集合中的项目不能重复(如果我在这里错了,请纠正我)

我用PHP编写了自己的函数来进行自定义哈希交叉,逻辑如下:

  • arr1
    :一个数组,其键是项的ID,值是其对应的数量。 这表示用户的愿望列表
  • arr2
    :与
    arr1
    相同,但它表示另一个用户的库存
  • 对于这个自定义交叉点,我需要的逻辑是,愿望列表的所有者不关心卖家是否有100个item1。如果他只想要4个,那么只算4个
  • 我需要一种非常快速的方法来相交集合,但是通常的相似系数公式涉及集合的相交和并集,当比较一个集合和其他集合(比如说,20万个集合)时,这可能没有我想要的那么快。以下是我目前的情况:

    function my_similarity_coefficient ($arr1, $arr2) {
    
        $matches = 0;
        $total = 0;
    
        if (count($arr2) == 0)
            return 0;
    
        foreach ($arr1 as $id => $qty) {
    
            $total += $qty;
    
            if (!array_key_exists($id, $arr2))
                continue;
    
            $matches += min($qty, $arr2[$id]); // do not match more than what user wants
    
        }
    
        return $matches / $total;
    
    }
    
    我尝试在PHP中相交两个redish哈希。尺寸分别为
    arr1[67]
    arr2[231]
    。该系数是在未完成的61.98µs(最坏情况下可达266.075µs)下计算的。如果我尝试将数据从Redis获取到PHP,这个数字会膨胀到905.037µsec-3337.86µsec

    我想避开将数据从redis传输到PHP的瓶颈,所以我想知道是否有可能用lua(或者甚至是c++)编程这个自定义交集,如果有可能,它是否也会遇到同样的瓶颈,因为它也在从点A到点B获取数据,还是不会遇到获取瓶颈,因为数据已经是它的本地数据


    我不熟悉lua,但我不想被一匙一匙地灌输确切的代码。由于互联网上几乎没有关于lua的资源与我真正想要实现的目标相关,所以我想在搜索时先在这里挑选一些大脑。

    让我们看看。首先,这里是直接翻译成Lua的PHP代码。我在这里保留了相同的变量名,但在PHP中称为“Array”的变量在Lua中称为“Table”

    请注意,如果
    arr1
    为空,则此代码可以被零除,但您的代码也可以被零除

    让我们试试看:

    local arr1 = {
      a = 3,
      b = 5,
      c = 8,
    }
    
    local arr2 = {
      a = 2,
      c = 10,
      d = 7,
      e = 21,
    }
    
    print(my_similarity_coefficient(arr1, arr2)) -- 0.625
    
    现在让我们使用Redis。首先,让我们创建测试数据

    redis 127.0.0.1:6379> hmset arr1 a 3 b 5 c 8
    OK
    redis 127.0.0.1:6379> hmset arr2 a 2 c 10 d 7 e 21
    OK
    
    此脚本可以满足您的需要,不是以最有效的方式(可以减少对
    redis.call
    )的调用,而是以一种简单的方式,因此您可以理解它并在需要时对其进行优化:

    local k1, k2 = KEYS[1], KEYS[2]
    local matches, total = 0, 0
    
    if not redis.call("exists", k2) then return 0 end
    
    local qty, qty2
    for _, id in ipairs(redis.call("hkeys", k1)) do
      qty = tonumber(redis.call("hget", k1, id))
      total = total + qty
      qty2 = tonumber(redis.call("hget", k2, id) or 0)
      matches = matches + math.min(qty, qty2)
    end
    
    return tostring(matches / total)
    
    我们称之为:

    $ redis-cli eval "$(cat the_script.lua)" 2 arr1 arr2
    "0.625"
    
    成功

    需要注意的重要一点是类型转换:值(数量)通过
    tonumber
    转换为整数(Redis返回字符串),我们将结果转换为字符串,因为如果我们返回浮点数,Redis会将其截断为整数(此处为0)

    编辑-好的,谈论优化而不是说如何不好,所以这里有一个简单的例子:

    local k1, k2 = KEYS[1], KEYS[2]
    local matches, total = 0, 0
    
    if not redis.call("exists", k2) then return 0 end
    
    local t1 = redis.call("hgetall", k1)
    
    local id, qty, qty2
    for i=1,#t1,2 do
      id, qty = t1[i], tonumber(t1[i+1])
      total = total + qty
      qty2 = tonumber(redis.call("hget", k2, id) or 0)
      matches = matches + math.min(qty, qty2)
    end
    
    return tostring(matches / total)
    

    看起来不错。看来你已经为我做了这项工作。有没有可能在一次行程中把一个hgetall放进一张桌子?你是说hgetall k2?只有当两个文档之间有很多交叉点时,这才是一个好主意。此外,hgetall返回
    {key1,val1,key2,val2}
    而不是
    {key1=val1,key2=val2}
    ,因此必须进行转换。当前代码只执行一次网络跳闸,但在Redis内部执行Lua-C跳闸,因此它可能更快,也可能更快。总而言之:这是可能的,但你应该对它进行基准测试。好的,非常感谢!如果我能投两次票,我会的!:)结果真的很奇怪。我做了一次1:40k的运行:第一次从redis一个接一个地执行所有arr2,并用原生PHP进行系数计算。另一个是每次迭代调用$redis->eval()。原生PHP=16s PHP lua eval=21s,这是一致的分数。你认为我可能做错了什么吗?或者这是意料之中的?好吧,我想我终于搞定了。这只需要3秒钟:我认为这很好。除非你能看到我可以优化它的地方。:)
    local k1, k2 = KEYS[1], KEYS[2]
    local matches, total = 0, 0
    
    if not redis.call("exists", k2) then return 0 end
    
    local t1 = redis.call("hgetall", k1)
    
    local id, qty, qty2
    for i=1,#t1,2 do
      id, qty = t1[i], tonumber(t1[i+1])
      total = total + qty
      qty2 = tonumber(redis.call("hget", k2, id) or 0)
      matches = matches + math.min(qty, qty2)
    end
    
    return tostring(matches / total)