Redis应用场景

来源:互联网 发布:windows时间服务未运行 编辑:程序博客网 时间:2024/06/06 05:23

1.取最新N个数据的操作

比如典型的取你网站的最新文章,通过下面方式,我们可以将最新的5000条评论的ID放在Redis的List集合中,并将超出集合部分从数据库获取
使用LPUSH latest.comments<ID>命令,向list集合中插入数据
插入完成后再用LTRIM latest.comments 0 5000命令使其永远只保存最近5000个ID
然后我们在客户端获取某一页评论时可以用下面的逻辑(伪代码)
  1. <code>FUNCTION get_latest_comments(start,num_items):
  2.     id_list = redis.lrange("latest.comments",start,start+num_items-1)
  3.     IF id_list.length < num_items
  4.         id_list = SQL_DB("SELECT ... ORDER BY time LIMIT ...")
  5.     END
  6.     RETURN id_list
  7. END</code>

如果你还有不同的筛选维度,比如某个分类的最新N条,那么你可以再建一个按此分类的List,只存ID的话,Redis是非常高效的。

2.排行榜应用,取TOP N操作

这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。

3.需要精准设定过期时间的应用

比如你可以把上面说到的sorted set的score值设置成过期时间的时间戳,那么就可以简单地通过过期时间排序,定时清除过期数据了,不仅是清除Redis中的过期数据,你完全可以把 Redis里这个过期时间当成是对数据库中数据的索引,用Redis来找出哪些数据需要过期删除,然后再精准地从数据库中删除相应的记录。

4.计数器应用

Redis的命令都是原子性的,你可以轻松地利用INCR,DECR命令来构建计数器系统。

5.Uniq操作,获取某段时间所有数据排重值

这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就行了,set意为集合,所以会自动排重。

6.实时系统,反垃圾系统

通过上面说到的set功能,你可以知道一个终端用户是否进行了某个操作,可以找到其操作的集合并进行分析统计对比等。没有做不到,只有想不到。

7.Pub/Sub构建实时消息系统

Redis的Pub/Sub系统可以构建实时的消息系统,比如很多用Pub/Sub构建的实时聊天系统的例子。

8.构建队列系统

使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

9.缓存

Redis能够替代memcached,让你的缓存从只能存储数据变得能够更新数据,因此你不再需要每次都重新生成数据了。性能优于Memcached,数据结构更多样化。


MySql+Memcached架构的问题

实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的不断增加,和访问量的持续增长,

我们遇到了很多问题:

» 1.MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间。
» 2.Memcached与MySQL数据库数据一致性问题。
» 3.Memcached数据命中率低或down机,大量访问直接穿透到DB,MySQL无法支撑。
» 4.跨机房cache同步问题。

总体上各种各样的NoSQL产品主要用于解决以下几种问题

» 1.少量数据存储,高速读写访问。此类产品通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的功能,实际这正是Redis最主要的适用场景。
» 2.海量数据存储,分布式系统支持,数据一致性保证,方便的集群节点添加/删除。
» 3.这方面最具代表性的是dynamo和bigtable 2篇论文所阐述的思路: 前者是一个完全无中心的设计,节点之间通过gossip方式传递集群信息,数据保证最终一致性,后者是一个中心化的方案设计,通过类似一个分布式锁服务来 保证强一致性,数据写入先写内存和redo log,然后定期compat归并到磁盘上,将随机写优化为顺序写,提高写入性能。
» 4.Schema free,auto-sharding等。比如目前常见的一些文档数据库都是支持schema-free的,直接存储json格式数据,并且支持auto-sharding等功能,比如mongodb。

面对这些不同类型的NoSQL产品,我们需要根据我们的业务场景选择最合适的产品。

Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的 持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用 Redis呢?

如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:

» 1. Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
» 2. Redis支持数据的备份,即master-slave模式的数据备份。
» 3. Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。


Redis常用数据类型

Redis最为常用的数据类型主要有以下:
  1. <code>1.String
  2. 2.Hash
  3. 3.List
  4. 4.Set5.Sorted set6.pub/sub
  5. 7.Transactions</code>

在具体描述这几种数据类型之前,我们先通过一张图了解下Redis内部内存管理中是如何描述这些不同数据类型的:

首先Redis内部使用一个redisObject对象来表示所有的key和value,redisObject最主要的信息如上图所示:

» type代表一个value对象具体是何种数据类型,
» encoding是不同数据类型在redis内部的存储方式,

比 如:type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际 redis内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:"123" "456"这样的字符串。

这 里需要特殊说明一下vm字段,只有打开了Redis的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的,该功能会在后面具体描述。通过上 图我们可以发现Redis使用redisObject来表示所有的key/value数据是比较浪费内存的,当然这些内存管理成本的付出主要也是为了给 Redis不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用,我们随后会具体讨论。


各种数据类型应用和实现方式

下面我们先来逐一的分析下这7种数据类型的使用和内部实现方式:

String:

String 数据结构是简单的 key-value 类型,value 其实不仅是String,也可以是数字.

常用命令: set ,get, decr, incr, mget 等。

应用场景: String 是最常用的一种数据类型,普通的 key/ value 存储都可以归为此类.即可以完全实现目前Memcached 的功能,并且效率更高。还可以享受 Redis 的定时持久化,操作日志及 Replication 等功能。除了提供与Memcached 一样的 getsetincrdecr 等操作外,Redis 还提供了下面一些操作:

  • 获取字符串长度
  • 往字符串append内容
  • 设置和获取字符串的某一段内容
  • 设置及获取字符串的某一位(bit)
  • 批量设置一系列字符串的内容

实现方式: Stringredis 内部存储默认就是一个字符串,被redisObject 所引用,当遇到 incr ,decr 等操作时会转成数值型进行计算,此时redisObjectencoding 字段为 int

Hash

常用命令: hget, hset, hgetall 等。

应用场景:Memcached 中,我们经常将一些结构化的信息打包成 HashMap, 在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一 项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。而RedisHash结构可以使你像在数据库中 Update 一个属性一样只修改某一项属性值。

我们简单举个实例来描述下 Hash 的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:

第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。

第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。

那么Redis提供的Hash很好的解决了这个问题,RedisHash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口,如下图:

也就是说,Key仍然是用户ID, value是一个Map,这个Mapkey是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。

这里同时需要注意,Redis 提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。

实现方式:

上面已经说到 Redis Hash对应 Value 内部实际就是一个 HashMap, 实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结 构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

List

常用命令: lpush, rpush, lpop,rpop, lrange 等。

应用场景:

Redis list 的应用场景非常多,也是 Redis 最重要的数据结构之一,比如 twitter的关注列表,粉丝列表等都可以用Redis的list结构来实现。

Lists 就是链表,相信略有数据结构知识的人都应该能理解其结构。使用 Lists 结构,我们可以轻松地实现最新消息排行等功能。Lists 的另一个应用就是消息队列, 可以利用ListsPUSH 操作,将任务存在 Lists 中,然后工作线程再用 POP 操作将任务取出进行执行。Redis 还提供了操作 Lists 中某一段的api,你可以直接查询,删除Lists中某一段的元素。

实现方式:

Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。

Set

常用命令: sadd, spop, smembers,sunion 等。

应用场景:

Redis set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set 提供了判断某个成员是否在一个set集合内的重要接口,这个也是 list 所不能提供的。

Sets 集合的概念就是一堆不重复值的组合。利用 Redis 提供的 Sets 数据结构,可以存储一些集合性的数据,比如在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供 了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给 客户端还是存集到一个新的集合中。

实现方式:

set 的内部实现是一个 value 永远为 nullHashMap,实际就是通过计算hash 的方式来快速排重的,这也是 set 能提供判断一个成员是否在集合内的原因。

Sorted Set

常用命令: zadd, zrange, zrem,zcard

使用场景:

Redis sorted set 的使用场景与 set 类似,区别是 set 不是自动有序的,而sorted set 可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set 数据结构,比如twitter 的public timeline可以以发表时间作为 score 来存储,这样获取时就是自动按时间排好序的。

另外还可以用 Sorted Sets 来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

实现方式:

Redis sorted set 的内部使用 HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap 里存的 score,使用跳跃表的结构可以获得比较高的查找效率,并且在实现上比较简单。

Pub/Sub

Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis 中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

Transactions

谁说NoSQL都不支持事务,虽然Redis的Transactions提供的并不是严格的ACID的事务(比如一串用EXEC提交执行的命令,在 执行中服务器宕机,那么会有一部分命令执行了,剩下的没执行),但是这个Transactions还是提供了基本的命令打包执行的功能(在服务器不出问题 的情况下,可以保证一连串的命令是顺序在一起执行的,中间有会有其它客户端命令插进来执行)。Redis还提供了一个Watch功能,你可以对一个key 进行Watch,然后再执行Transactions,在这过程中,如果这个Watched的值进行了修改,那么这个Transactions会发现并拒 绝执行。

view rawhosted with ❤ byCloudPaper



php中使用PhpRedis模块操作Redis的实例


Redis官方已经列出了很多的PHP的客户端,这里推荐另外一个非常受欢迎的php客户端–phpredis。

phpredis/phpredis

3205934

A PHP extension for Redis — Read More

 

Latest commit to the develop branch on 12-11-2015
Download as zip

安装配置的过程就不在这里说了,下面列出应用方法:

  1. <code>/**
  2.  * 常用的phpredis方法
  3.  */
  4.  
  5. $objRedis = new Redis();
  6. $objRedis->connect('127.0.0.1', 6379);
  7.  
  8. /* 1. set: 写入key-value对 */
  9.  
  10. $objRedis->set('key1', 'value1');
  11. $objRedis->set('key2', 'value2');
  12.  
  13. /* 2. get: 根据key得到value */
  14.  
  15. echo $objRedis->get('key1');
  16.  
  17. /* 3. mget: 根据key的队列获取多个value值 */
  18.  
  19. $arrKeys = array('key1', 'key2');
  20.  
  21. print_r($objRedis->mget($arrKeys));
  22.  
  23. /* 4. setex: 同样是写入key-value对,添加了过期时间 */
  24.  
  25. $objRedis->setex('key3', 3600, 'value3');
  26.  
  27. /* 5. del: 删除key */
  28.  
  29. $objRedis->del('key1');
  30.  
  31. /* 6. exists: 判断key是否存在,如存在返回true */
  32.  
  33. $isExists = $objRedis->exists('key1');
  34.  
  35. if($isExists) {echo 'yes';}else{echo 'no';}
  36.  
  37. /* 7. close: 关闭连接 */
  38.  
  39. $redisClient -> close();</code>

php使用redis的例子

通常redis作为数据库的cache而存在,这里我们可以看到redis是如何跟php+mysql整合的。

  1. <code><?php 
  2.   /* Functions for redis operations starts here   */ 
  3.  
  4.   $redisObj = new Redis(); 
  5.  
  6.   function openRedisConnection( $hostName, $port){ 
  7.         global $redisObj; 
  8.         // Opening a redis connection
  9.         $redisObj->connect( $hostName, $port );
  10.         return $redisObj; 
  11.   } 
  12.  
  13.   function setValueWithTtl( $key, $value, $ttl ){ 
  14.  
  15.         try{ 
  16.                 global $redisObj; 
  17.                 // setting the value in redis
  18.                 $redisObj->setex( $key, $ttl, $value );
  19.         }catch( Exception $e ){ 
  20.                 echo $e->getMessage(); 
  21.         } 
  22.   } 
  23.  
  24.   function getValueFromKey( $key ){ 
  25.         try{ 
  26.                 global $redisObj; 
  27.                 // getting the value from redis
  28.                 return $redisObj->get( $key);
  29.         }catch( Exception $e ){ 
  30.                 echo $e->getMessage(); 
  31.         } 
  32.   } 
  33.  
  34.   function deleteValueFromKey( $key ){ 
  35.         try{ 
  36.                 global $redisObj; 
  37.                 // deleting the value from redis
  38.                 $redisObj->del( $key);
  39.         }catch( Exception $e ){ 
  40.                 echo $e->getMessage(); 
  41.         } 
  42.   } 
  43.  
  44.    /* Functions for converting sql result  object to array goes below  */ 
  45.  
  46.   function convertToArray( $result ){ 
  47.         $resultArray = array(); 
  48.  
  49.         for( $count=0; $row = $result->fetch_assoc(); $count++ ) { 
  50.                 $resultArray[$count] = $row; 
  51.         } 
  52.  
  53.         return $resultArray; 
  54.   } 
  55.  
  56.    /* Functions for executing the mySql query goes below   */ 
  57.  
  58.   function executeQuery( $query ){ 
  59.      $mysqli = new mysqli( 'localhost',  'username',  'password',  'someDatabase' ); 
  60.  
  61.      if( $mysqli->connect_errno ){ 
  62.        echo "Failed to connect to MySql:"."(".mysqli_connect_error().")".mysqli_connect_errno(); 
  63.      } 
  64.  
  65.      $result =  $mysqli->query( $query ); 
  66.          // Calling function to convert result  to array
  67.      $arrResult = convertToArray( $result );
  68.  
  69.      return $arrResult;     
  70.   }  
  71.  
  72.   $query = 'select * from sometable limit 1'; 
  73.   // Calling function to execute sql query
  74.   $arrValues = executeQuery( $query );
  75.  
  76.   // Making json string
  77.   $jsonValue = json_encode($arrValues);
  78.  
  79.   // Opening a redis connection
  80.   openRedisConnection( 'localhost', 6379 );
  81.  
  82.   // Inserting the value with ttl =  1 hours
  83.   setValueWithTtl( 'somekey1', $jsonValue, 3600);
  84.  
  85.   // Fetching value from redis using the key. 
  86.   $val = getValueFromKey( 'somekey1' ); 
  87.  
  88.   //  Output:  the json encoded array from redis 
  89.   echo $val;
  90.  
  91.   // Unsetting value from redis
  92.   deleteValueFromKey( $key );
  93.  
  94. ?></code>



php Redis应用中的一些开源程序


ErikDubbelboer/phpRedisAdmin

969274

Simple web interface to manage Redis databases. — Read More

http://dubbelboer.com/phpRedisAdmin/

Latest commit to the master branch on 11-20-2015
Download as zip

Shumkov/Rediska

25961

Full-featured PHP client for key-value database Redis — Read More

http://rediska.geometria-lab.net

Latest commit to the master branch on 8-26-2015
Download as zip

jdp/redisent

129121

a no-nonsense Redis client for PHP — Read More

http://jdp.github.com/redisent/

Latest commit to the master branch on 5-25-2015
Download as zip

nrk/predis

2375436

Flexible and feature-complete PHP client library for Redis — Read More

https://github.com/nrk/predis/wiki

Latest commit to the v1.0 branch on 11-9-2015
Download as zip

sash/php-redis

3612

Redis connector for php — Read More

 

Latest commit to the master branch on 11-23-2012
Download as zip



Reference

• Redis应用场景 http://blog.csdn.net/hguisu/article/details/8836819
• How to take advantage of Redis just adding it to your stack http://oldblog.antirez.com/post/take-advantage-of-redis-adding-it-to-your-stack.html
• 国内外三个不同领域巨头分享的Redis实战经验及使用场景 http://www.csdn.net/article/1970-01-01/2817107
• php中使用PhpRedis模块操作Redis的实例 http://www.sudops.com/example-of-use-redis-with-phpredis.html
• Redis作者谈Redis应用场景 http://blog.nosqlfan.com/html/2235.html
0 0
原创粉丝点击