我们是一个图片分享网站,所有用户上传的图片做了按用户ID分服务器存储。每张图片都有一个唯一的图片ID,对应上传它的用户ID。目前用户上传的图片总量已接近30亿张,也就意味着这个对应关系有30亿条。现在需要对这些键值对数据的存储做一个优化,要求:

查询速度快 数据能存储在内存中。因为我们使用了Amazon的EC2,所以希望占用内存总量不超过他们要求的17G 数据一致性和持久化有保障

根据网上的一些资料和推荐,我们打算用redis来存储,key为图片ID,value为用户ID。当我们实施的时候却发现,存储100W个key时就已经使用了70M内存,如果30亿个都存储进去需要21G,超过了规定的上限。

请问大家有什么好的优化方案吗?主要根据图片找到玩家这一种查询优化。

2010-11-03 23:43:55

3 Answers

今天看到mongodb的GridFs的特性,突然想到之前回答的这个问题,有更好的解决方案。这个算是最佳的解决方案了吧。

[nginx + gridfs 实现图片的分布式存储(一)]

http://www.cnblogs.com/zhangmiao-chp/archive/2011/05/05/2038285.html


我觉得在使用redis时候有误区,redis不像memcached,是简单的key, value结构的数据容器。redis是支持hash_table的。根据你的应用应该是用hash_table来存储。

加入图片ID是10位,redis的指令是:
hset 图片ID的前5位 图片ID的后5位 user_id

通过ID切割的方式,一定程度上也可以减少内存的使用。

再有就是这么大的数量级,实现下冷热数据策略。

因为redis是单线程的,另外最好运行多个redis实例(这个根据你的访问频繁程度), 多个进程比能有效的分担负载。

2010-11-04 01:56:09

我们在游戏项目中用redis做实时排行,我们的排行数据跟你的情况有相似之处:全部数据都是数字,因此我现在在做一项工作,就是调整redis在相关功能的数据结构方面的实现,主要目的就是压缩内存(有压缩空间的),顺便加入一些内存管理功能。
你们的项目,这个规模,也适合定制所需的工具的。

2010-11-04 03:17:42

“存储100W个key时就已经使用了70M内存”,刚算了下,平均一个桶占用73个字节。由于key-value都是存的ID,不可能暂用73个字节。怀疑是“图片ID”、“用户ID”数值很大,造成redis初始化hash表时分配了很多空间。因为redis可能认为要存储的数据很多,为了满足能使数据均匀分布在每个桶内且桶内元素个数较少的需求。这也是hash这种存储结构性能必须考虑的地方。

所以为了满足你“十亿数量级的key-value”,我推荐自己实现一个hash表。因为redis支持的功能很多,包括对很多数据类型的支持、多种访问方式、取交集并集和差集、排序、原子操作。所以性能(内存占用率、CPU消耗、IO)上必然照成损失。

自实现的hash表只保存ID(整型)数据,使用拉链的散列法(一个桶内的多条数据使用链表的方式把元素连接起来)。另外,散列函数非常重要,为了能使元素均匀分布,以下给出一个非常经典的通用散列算法,经过研究人员统计分析过散列程度的。

unsigned long hashcode(const unsigned char name) { unsigned long h=0,g; while(name) { h=(h<<4) + *name++; if(g=h & 0xF0000000) h^=g>>24; h &=~g; } return h; }

hash表使用C语言实现,使得存取高效。

除了自实现方法以外(其实这是本质的方法),还可以考虑把“图片ID”、“用户ID”数值缩小测试测试看,仍然使用redis。

2010-11-04 04:58:57
您不能回答该问题或者回答已经关闭!

相关文章推荐

  • C#中using指令的几种用法

    using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到

  • C#实例解析适配器设计模式

    将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作

  • C#开发高性能Log Help类设计开发

    项目中要在操作数据库的异常处理中加入写Log日志,对于商业上有要求,写log时对其它操作尽可能影响小,不能因为加入log导致耗时太多

  • 使用托管C++粘合C#和C++代码(二)

    本文实现一下C++代码调用C#代码的过程。我构造一个简单并且直观的例子:通过C++ UI 触发C# UI.

  • Async和Await使异步编程更简单

    C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作

  • C#开发中的反射机制

    反射的定义:审查元数据并收集关于它的类型信息的能力。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等

  • C#运行时相互关系

    C#运行时相互关系,包括运行时类型、对象、线程栈和托管堆之间的相互关系,静态方法、实例方法和虚方法的区别等等

  • C#协变和逆变

    “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型,“逆变”则是指能够使用派生程度更小的类型

  • C#基础概念之延迟加载

    延迟加载(lazy load)是Hibernate3关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作

  • C#中的索引器的简单理解和用法

    C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了数组和集合成员时,索引器将大大简化对数组或集合成员的存取操作

  • 使用托管C++粘合C#和C++代码(一)

    C#在xml读写,数据库操纵,界面构造等很多方面性能卓越;C++的效率高,是底层开发的必备武器

  • 深入C# 序列化(Serialize)、反序列化(Deserialize)

    C#中的序列化和反序列化,序列化是.NET运行时环境用来支持用户定义类型的流化的机制