加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

Redis存储总用String?你大概错过了更优的使用方法

发布时间:2019-01-18 13:40:45 所属栏目:编程 来源:王清培(沪江)
导读:Redis为我们提供了5种数据类型,基本上我们使用频率最高的就是String,而对其他四种数据类型使用的频次稍弱于String。原因在于: String使用起来比较简单,可以方便存储复杂的对象,使用场景比较多; 由于Redis expire time只能设置在key上,像List、Hash、
副标题[/!--empirenews.page--]

Redis为我们提供了5种数据类型,基本上我们使用频率最高的就是String,而对其他四种数据类型使用的频次稍弱于String。原因在于:

  • String使用起来比较简单,可以方便存储复杂的对象,使用场景比较多;
  • 由于Redis expire time只能设置在key上,像List、Hash、Set、Zset属于集合类型,会管理一组item,我们无法在这些集合的item上设置过期时间,所以使用expiretime来处理集合的cache失效会变得稍微复杂些。但是String使用expire time来管理过期策略会比较简单,因为它包含的项少。这里说的集合是宽泛的类似集合。
  • 从更深层次来看,我们对另外四种数据类型的使用和原理并不是太了解。所以这个时候往往会忽视在特定场景下使用某种数据类型会比String性能高出很多的可能性,比如使用Hash结构来提高某实体某个项的修改等。

这里我们不打算罗列这5种数据类型的使用方法,因为这些资料网上有很多。我们主要讨论这5种数据类型的功能特点,弄清楚它们分别适合用于处理哪些现实的业务场景,我们又该如何组合性使用这5种数据类型,找到解决复杂cache问题的最优方案。

一、Redis的数据类型及特点

我们来简要了解一下String、List、Hash、Set及Zset:

1)String

String是Redis提供的字符串类型。可以针对String类型独立设置expire time,通常用来存储长字符串数据,比如某个对象的json字符串。

在使用上,String类型最巧妙的是可以动态拼接key。通常我们可以将一组id放在Set里,然后动态查找String还是否存在,如果不存在说明已经过期或者由于数据修改主动delete了,需要再做一次cache数据load。

虽然Set无法设置item的过期时间,但是我们可以将Set Item与String Key关联来达到相同的效果。

下图中的左边是一个key为Set:order:ids的Set集合,它可能是一个全量集合,也可能是某个查询条件获取出来的一个集合:

Redis存储总用String?你大概错过了更优的使用方法

有时候复杂点的场景需要多个Set集合来支撑计算,在Redis服务器里可能会有很多类似这样的集合。这些集合我们可以称为功能数据,这些数据是用来辅助cache计算的,当进行各种集合运算之后会得出当前查询需要返回的子集,最后我们才会去获取某个订单真正的数据。

这些String:order:{orderId}字符串key并不一定是为了服务一种场景,而是整个系统最底层的数据,各种场景最后都需要获取这些数据。那些Set集合可以认为是查询条件数据,用来辅助查询条件的计算。

Redis为我们提供了TYPE命令来查看某个key的数据类型,如String类型:

  1. SET string:order:100 order-100  
  2. TYPE string:order:100  
  3. string 

2)List

List在提高throughput的场景中非常适用,因为它特有的LPUSH、RPUSH、LPOP、RPOP功能可以无缝的支持生产者、消费者架构模式。

这非常适合实现类似Java Concurrency Fork/Join框架中的work-stealing算法(工作窃取)。

注:Java Fork/Join框架使用并行来提高性能,但是会带来由于并发take task带来的race condition(竞态条件)问题,所以采用work-stealing算法来解决由于竞争问题带来的性能损耗。

下图中模拟了一个典型的支付callback峰值场景:

Redis存储总用String?你大概错过了更优的使用方法

在峰值出现的地方一般我们都会使用加buffer的方式来加快请求处理速度,这样才能提高并发处理能力,提高through put。

支付gateway收到callback之后不做任何处理直接交给分发器。

分发器是一个无状态的cluster,每个node通过向注册中心pull handler queue list,也就是获取下游处理器注册到注册中心里的消息通道。每一个分发器node会维护一个本地queue list,然后顺序推送消息到这些queue list即可。

这里会有点小问题,就是支付gateway调用分发器的时候,是如何做load balance?如果不是平均负载可能会有某个queue list高出其他queue list。

而分发器不需要做soft load balance,因为哪怕某个queue list比其他queue list多也无所谓,因为下游message handler会根据work-stealing算法来窃取其他消费慢的queue list。

Redis List的LPUSH、RPUSH、LPOP、RPOP特性确实可以在很多场景下提高这种横向扩展计算能力。

3)Hash

Hash数据类型很明显是基于Hash算法的,对于项的查找时间复杂度是O(1)的,在极端情况下可能出现项Hash冲突问题,Redis内部是使用链表加key判断来解决的。具体Redis内部的数据结构我们在后面有介绍,这里就不展开了。

Hash数据类型的特点通常可以用来解决带有映射关系,同时又需要对某些项进行更新或者删除等操作。如果不是某个项需要维护,那么一般可以通过使用String来解决。

如果有需要对某个字段进行修改,使用String很明显会多出很多开销,需要读取出来反序列化成对象然后操作,然后再序列化写回Redis,这中间可能还有并发问题。

那我们可以使用Redis Hash提供的实体属性Hash存储特性,我们可以认为Hash Value是一个Hash Table,实体的每一个属性都是通过Hash得到属性的最终数据索引。

下图使用Hash数据类型来记录页面的a/bmetrics:

Redis存储总用String?你大概错过了更优的使用方法

左边的是首页index的各个区域的统计,右边是营销marketing的各个区域统计。

在程序里我们可以很方便的使用Redis的atomic特性对Hash某个项进行累加操作。

  1. HMSET hash:mall:page:ab:metrics:index topbanner 10 leftbanner 5 rightbanner 8 bottombanner 20 productmore 10 topshopping 8  
  2. OK  
  3. HGETALL hash:mall:page:ab:metrics:index  
  4.  1) "topbanner"  
  5.  2) "10"  
  6.  3) "leftbanner"  
  7.  4) "5"  
  8.  5) "rightbanner"  
  9.  6) "8"  
  10.  7) "bottombanner"  
  11.  8) "20"  
  12.  9) "productmore"  
  13. 10) "10"  
  14. 11) "topshopping"  
  15. 12) "8" 
  16. HINCRBY hash:mall:page:ab:metrics:index topbanner 1
  17. (integer) 11

使用Redis Hash Increment进行原子增加操作。HINCRBY命令可以原子增加任何给定的整数,也可以通过HINCRBYFLOAT来原子增加浮点类型数据。

4)Set

Set集合数据类型可以支持集合运算,不能存储重复数据。

Set最大的特点就是集合的计算能力,inter交集、union并集、diff差集,这些特点可以用来做高性能的交叉计算或者剔除数据。

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读