我试图制作基于分数的PHP内存缓存,但我的性能有问题(如何计算分数和删除低分记录)。
目标
我有大约1000万条记录。 我想只在内存中缓存0.1%的最常见记录,而不是光盘。
我想设置10,000个缓存插槽(0.1%),我想只保留这些插槽中最常用的插槽。
尝试/问题
我尝试了基于文件的缓存,但速度很慢。
我尝试过MySQL和PostgreSQL,但是计算分数和删除低分记录的性能成本太高。
我尝试了基于时间的缓存,例如。 xcache,但是因为我的项目中的数据太多,所以写得太多了。此外,删除最低分数记录并列出所有缓存位置也存在问题,因为它" key->值"。
我找到了Redis,但似乎没有任何得分或类似的东西。
我的问题:
我应该使用什么缓存方法进行基于分数的缓存?
请注意,所有这些帖子都相似,但不包含任何可用的答案:
Fastest PHP memory cache/hashtable
In-memory cache with LRU expiration
答案 0 :(得分:1)
听起来LRU缓存应该能满足您的需求。您可以将Redis配置为LRU缓存。可能它会很好地处理你的情况。 以下是redis文档的一些参考:http://redis.io/topics/lru-cache
快速总结一下, 你可以使用“allkeys-lru”驱逐政策,并将“maxmemory”设置为你想要的东西。一旦达到内存限制,redis将释放最近最少使用的项目,并将内存使用量保持在“maxmemory”下。
另一种选择是使用“memcached”,它是一个内存键值存储,默认情况下配置为LRU缓存。
如果您想亲自跟踪分数,并且已经为您的项目设置了某种评分机制,您可以使用Redis,您可以将SortedSet与Hash一起保存以对缓存项目进行排名。
哈希会保留您的缓存数据,而SortedSet会保持您的项目排名。
您需要这些SortedSet命令:
每次插入后,您都必须手动检查SortedSet的计数并限制缓存中的项目数。总的来说,算法是这样的:
缓存插入:
HSET "cacheKey" "itemName" "itemValue"
ZADD "rankingKey" "itemScore" "itemName"
count = ZCARD "rankingKey"
if (count > limit)
lowestRankedItem = ZRANGE "rankingKey" 0 0
ZREM "rankingKey" lowestRankedItem
HDEL "cacheKey" lowestRankedItem
查找将是:
itemValue = HGET "cacheKey" "itemName"
答案 1 :(得分:0)
Pure LRU并不是很好,因为它会缓存所有新密钥,我只需要缓存0.1%的数据(得分很高),因此不会有太多无用的写作。
我确实会按照下一版本的建议实现Redis方法,因为速度要快得多。因此,只有在没有Redis的情况下使用此功能,如果您无法确定得分,只需计算每次点击次数或只是想要保持简单。
但我最好的尝试是使用key =>值memcached,现在只是测试,看起来快速而稳定。在我以错误的方式思考之前...
当缓存新项目时,我们检查缓存中是否存在得分(密钥缓存包含int),如果是,小于我们的限制,我们会增加分数,如果是,高于限制,我们获取内容并保存到缓存。如果缓存包含除数字以外的任何内容,则它是我们的内容(得分高)。如果密钥的缓存根本不存在,我们将int 1作为分数保存到具有非常短的TTL的缓存中,我们只获得没有任何缓存的内容(低分)。
<?php
$minhits = 10; // Min 10 hits for one key
$minhitstime = 60 // Max 60 seconds between hits
$cachettl = 3600; // Cache for 3600 seconds
$key = "article1";
$mem = new Memcached();
$mem->addServer('localhost', 11211);
$content = $mem->get($key);
if(!$content OR (is_int($content) AND $content<$minhits)){
$content = getArticleContent(); // Your own function
}
if(is_int($c)){
if($c>=$minhits){
$mem->set($key, $content, $cachettl);
} else {
$mem->set($key, ($c+1), $minhitstime);
}
} elseif($c) {
$content = $c;
} else {
$mem->set($key, 1, $minhitstime);
}
echo $content;
?>
另外请不要尝试缓存仅限int的值;)如果是,则必须编辑代码。