当前位置: 首页 > news >正文

节约内存:Instagram的Redis实践

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

 

Instagram可以说是网拍App的始祖级应用,也是当前最火热的拍照App之一,Instagram的照片数量已经达到3亿,而在Instagram里,我们需要知道每一张照片的作者是谁,下面就是Instagram团队如何使用Redis来解决这个问题并进行内存优化的。

首先,这个通过图片ID反查用户UID的应用有以下几点需求:

  • 查询速度要足够快

  • 数据要能全部放到内存里,最好是一台EC2的 high-memory 机型就能存储(17GB或者34GB的,68GB的太浪费了)

  • 要合适Instagram现有的架构(Instagram对Redis有一定的使用经验,比如这个应用)

  • 支持持久化,这样在服务器重启后不需要再预热

Instagram的开发者首先否定了数据库存储的方案,他们保持了KISS原则(Keep It Simple and Stupid),因为这个应用根本用不到数据库的update功能,事务功能和关联查询等等牛X功能,所以不必为这些用不到的功能去选择维护一个数据库。

于是他们选择了Redis,Redis是一个支持持久化的内存数据库,所有的数据都被存储在内存中(忘掉VM吧),而最简单的实现就是使用Redis的String结构来做一个key-value存储就行了。像这样:

SET media:1155315 939
GET media:1155315
> 939

其中1155315是图片ID,939是用户ID,我们将每一张图片ID为作key,用户uid作为value来存成key-value对。然后他们进行了测试,将数据按上面的方法存储,1,000,000数据会用掉70MB内存,300,000,000张照片就会用掉21GB的内存。对比预算的 17GB还是超支了。

(NoSQLFan:其实这里我们可以看到一个优化点,我们可以将key值前面相同的media去掉,只存数字,这样key的长度就减少了,减少key值对内存的开销【注:Redis的key值不会做字符串到数字的转换,所以这里节省的,仅仅是media:这6个字节的开销】。经过实验,内存占用会降到50MB,总的内存占用是15GB,是满足需求的,但是Instagram后面的改进任然有必要)

于是Instagram的开发者向Redis的开发者之一Pieter Noordhuis询问优化方案,得到的回复是使用Hash结构。具体的做法就是将数据分段,每一段使用一个Hash结构存储,由于Hash结构会在单个Hash元素在不足一定数量时进行压缩存储,所以可以大量节约内存。这一点在上面的String结构里是不存在的。而这个一定数量是由配置文件中的hash-zipmap-max-entries参数来控制的。经过开发者们的实验,将hash-zipmap-max-entries设置为1000时,性能比较好,超过1000后HSET命令就会导致CPU消耗变得非常大。

于是他们改变了方案,将数据存成如下结构:

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
> "939"

通过取7位的图片ID的前四位为Hash结构的key值,保证了每个Hash内部只包含3位的key,也就是1000个。

再做一次实验,结果是每1,000,000个key只消耗了16MB的内存。总内存使用也降到了5GB,满足了应用需求。

(NoSQLFan:同样的,这里我们还是可以再进行优化,首先是将Hash结构的 key值变成纯数字,这样key长度减少了12个字节,其次是将Hash结构中的subkey值变成三位数,这又减少了4个字节的开销,如下所示。经过实验,内存占用量会降到10MB,总内存占用为3GB)

HSET "1155" "315" "939"
HGET "1155" "315"
> "939"

优化无止境,只要肯琢磨。希望你在使用存储产品时也能如此爱惜内存。

转载于:https://my.oschina.net/wanghhao/blog/502280

相关文章:

  • 智云大咖秀:怎样的稳定器才是摄影师的“灵魂辅助”?
  • Bootstrap vs Foundation如何选择靠谱前端框架
  • 双管齐下,VMware的容器新战略
  • 退役前的记录(2018.10.14-NOIP2018)
  • CENTOS 6.6 nfs 服务器搭建
  • JQuery Mobile难点备忘
  • C++语法小技巧
  • MeiTuanLocateCity仿美团城市列表选择界面
  • React Native安卓模拟器调出Dev Setting菜单
  • Hibernate各保存方法之间的差 (save,persist,update,saveOrUpdte,merge,flush,lock)等一下
  • 少侠请重新来过 - Vue学习笔记(五) - 指令
  • AIX 系统 EBS form 打开报错FRM-92101: FORM server在启动过程中失败
  • JS面向对象编程
  • 解决Eclipse报errors running builder ‘javascript validator’ on project
  • 测试代码覆盖率工具学习(Android Emma)
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • Docker: 容器互访的三种方式
  • ECMAScript6(0):ES6简明参考手册
  • FastReport在线报表设计器工作原理
  • JavaScript DOM 10 - 滚动
  • Linux后台研发超实用命令总结
  • mockjs让前端开发独立于后端
  • Python 基础起步 (十) 什么叫函数?
  • Python打包系统简单入门
  • vue:响应原理
  • vuex 学习笔记 01
  • vue自定义指令实现v-tap插件
  • 回顾2016
  • 时间复杂度与空间复杂度分析
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 微信小程序填坑清单
  • python最赚钱的4个方向,你最心动的是哪个?
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​卜东波研究员:高观点下的少儿计算思维
  • # Apache SeaTunnel 究竟是什么?
  • #1015 : KMP算法
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (3)Dubbo启动时qos-server can not bind localhost22222错误解决
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (转)3D模板阴影原理
  • 、写入Shellcode到注册表上线
  • .NET CF命令行调试器MDbg入门(一)
  • .NET DevOps 接入指南 | 1. GitLab 安装
  • .sdf和.msp文件读取
  • 。Net下Windows服务程序开发疑惑
  • /bin/bash^M: bad interpreter: No such file or directory
  • @font-face 用字体画图标
  • @select 怎么写存储过程_你知道select语句和update语句分别是怎么执行的吗?
  • [ vulhub漏洞复现篇 ] AppWeb认证绕过漏洞(CVE-2018-8715)
  • [20171101]rman to destination.txt
  • [383] 赎金信 js