Redis 缓存数据库入门教程

Redis是C语言开发的一个高性能键值对(key -value) 内存数据库,能够用做数据库,缓存和消息中间件等。html

Redis

Redis是什么

简介

Redis是C语言开发的一个高性能键值对(key -value) 内存数据库,能够用做数据库,缓存和消息中间件等。

特色

  1. 做为内存数据库,它的性能很是优秀,数据存储在内存当中,读写速度很是快,支持并发10W QPS(每秒查询次数),单进程单线程,是线程安全的,采用IO多路复用机制。java

  2. 丰富的数据类型,支持字符串,散列,列表,集合,有序集合等,支持数据持久化。能够将内存中数据保存在磁盘中,重启时加载。node

  3. 主从复制,哨兵,高可用,可用做分布式锁。能够做为消息中间件使用,支持发布订阅。web


Redis 数据类型

和Java的数据结构类比:redis

Redis Java
string String
hash HashMap
list LinkedList
set HashSet
sorted set TreeSet

1. string (字符串)

介绍:

string是最基本的数据类型,跟 mencached 同样的类型,也是二进制安全的,能够包含任何数据算法

如:jpg图片或者序列化的对象。最大能存储512MB。数据库

特性json

最大能存储容量:
    512MB

数值计算最大范围:
    Java中long的最大值 2^32-1

数据未获取到:
    为nil至关于null

表示运行结果是否成功:
    integer 0 -> false 失败
    integer 1 -> true  成功
    
表示运行结果值:
    integer 3 -> 3条 
    integer 1 -> 1条

关系型数据库数据在Redis中 key 热点数据命名惯例:数组

大部分数据都是从数据库来的:因此

通常的命名规范都会是:key[表名:主键名:主键值:字段名] value[xxxx]

表名  :主键名  :主键值  :字段名
order :id      : 11111   : name
equip :id      : 11111   : type
news  :id      : 11111   : title

用法:缓存

单个操做set get 多个操做加前缀mset mget (Multiple多个)

根据发送时长和执行时长来判断用单指令仍是多指令

// 添加/修改
set key value 
// 获取
get key  
// 删除 1成功,0失败
del key 
// 添加/修改多个
mset key1 value1 key2 value2 ....  
// 获取多个
mget key1 key2 ....
// 获取value的数据字符长度
strlen key 
// 追加信息到值后面(存在就追加,不然新增)
append key value

扩展操做1:Redis解决MySQL分库分表操做的惟一ID问题

场景:

大型企业级应用中,分表操做是基本操做,使用多张表存储同类型数据,可是对应的主键ID必须保证统一性,不能重复。

oracle数据库具备sequence设定,能够解决,可是MySQL没有相似机制。

解决办法:

利用Redis 生成ID,主要利用Redis是单线程,因此也能够用来生成惟一ID,当使用的是Redis集群的时候,好比集群中有5台Redis,初始化每台Redis的值为1,2,3,4,5,设置步长为5,而且肯定一个不随机的负载均衡策略,可以保证有序惟一。

优势:不依赖数据库,灵活,且性能相对于数据库有必定提升,使用Redis集群策略还能排除单点故障问题,ID自然有序。

缺点:须要引入Redis这个新组件,开发配置工做量大

//设置数值数据增长指定范围的值,若是incr传的负数,会变成减,若是decr传整数,变成加

//每次+1,key+=1
incr key  

//每次+指定的值,key+=increment
incrby ket increment  

//每次增长指定小数的值
incrbufloat ket increment  

//设置数值数据减小指定范围的值

//每次减一,key-=1
decr key

//每次减指定的值,key-=increment
decrby key increment

注意事项:

  • string在Redis内部为字符串存储,当它进行操做的时候会转类型为数字,若是含有其余非数字字符,则会报错.

  • 超出数值上限也会报错,好比用Java语言的long类型

基本类型:long 二进制位数:64
    包装类:java.lang.Long
    最小值:Long.MIN_VALUE=-9223372036854775808 (-2的63次方)
    最大值:Long.MAX_VALUE=9223372036854775807 (2的63次方-1)
  • Redis全部操做都是原子性的,都是单线程处理全部业务,因此无需担忧并发带来的数据问题。

另外一篇介绍常见的惟一ID生成方案,数据库自增/UUID/snowflake雪花算法/zookeeper/MongoDB的objectId


扩展操做2:数据时效性问题

Redis 控制数据的生命周期,经过数据是否失效控制业务行为,适用于所具备时效性限定控制的操做。

场景:
  • 微信投票:每四小时只能投一票。

  • 电商热门商品推荐:热门商品不能一直处于热门期,每种商品热门期维持3天,3天后自动取消热门。

  • 热点新闻,新闻网站会出现热点新闻,最大特征是时效性,因此如何控制热点新闻的时效性。

解决办法:

设置数据具备指定的生命周期:

// 设置N秒时间,N秒后失效删除掉数据
setex key seconds values

// 设置N毫秒时间,N毫秒后失效删除掉数据
psetex key milliseconds value

扩展操做3:主页高频访问信息显示控制

Redis 应用于各类结构型和非结构型高热度数据访问加速

场景:

例如微博大V主页显示的粉丝数与微博数量,每一个用户访问到其主页都会有这几个数据

解决办法:

Redis 存储格式

  • 以用户主键和属性做为 key ,数量为 value ,后台设定定时刷新策略
// set 用户:id:id号:粉丝 粉丝数
set user:id:11111:fans   20

// set 用户:id:id号:博客 博客数
set user:id:11111:blogs   20

// set 用户:id:id号:关注 关注数
set user:id:11111:focus 20
  • 以用户主键做为 key ,JSON做为 value,定时刷新(也能够用hash类型)
// set 用户:id:id号 JSON
set user:id:11111 {ID:11111,NAME:大V,fans:20,blogs:20,focus:30}

对于第一种,能够随时利用incr key进行数据更新,
而第二种须要全取出来才能够更新(取数据方便,更新数据麻烦),不过对于这种而言,上面举例的粉丝数多几个少几个用户也不会特别去关注,只需设定刷新策略,来选择用哪一种方式去实现。


2. hash(哈希):

介绍:

hash 是一个string类型的key-field-value 映射表,特别适合用来存储对象,和单个string类型不一样的是,hash能够对信息的每一个属性字段进行单独存储,而string类型则须要对用户对象进行序列化保存,而且以字符串存储整个序列化数据(整存整取)对象类的数据存储若是有比较频繁的更新需求操做会显得笨重,而hash能够进行整存零取,从而节约网络流量,不过内存占用也会相对比string大。

新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息

须要的存储结构:一个存储空间保存多个键值对数据

hash类型:底层使用哈希表结构实现数据存储

KEY
VALUE
FIELD1
FIELD2
FIELD3
VALUE1
VALUE2
VALUE3

特性

  • hash存储结构优化:

    • 若是field数量较少,存储结构优化为类数组结构
    • 若是field数量较多,存储结构优化为HashMap结构
  • hash 类型下的value 只能存储字符串,不容许存储其余数据类型,不存在嵌套现象,若是数据未获取到,则返回nil(NULL)

  • 每一个hash能够存储2^32-1个键值对

  • hash类型十分贴近对象的数据存储形式,而且能够灵活添加删除对象属性。可是hash设计初衷不是为了存储大量对象而设计的,因此不能滥用,更不能够将hash做为对象列表使用。

  • hgettall 操做能够获取所有属性,若是内部field过多,遍历总体数据效率就会很低,有可能成为数据访问瓶颈。

命名惯例

用法

  • 添加/修改数据
// 单个
hset key field value

// 多个
hmset key field1 value1 field2 value2..
  • 获取数据
// 单个
hget key field

// 多个
hmget key field1 field2..
  • 获取所有属性
hgettall key
  • 删除数据
hdel key field1
  • 获取哈希表中字段的数量
hlen key
  • 获取哈希表中是否存在指定的字段
hexists key field    1存在 0不存在
  • 获取哈希表中全部的字段名称或字段值
// 获取全部的field名
hkeys key

// 获去全部的field-value值
hvals key
  • 设置指定字段的数值数据增长指定范围的值
// 增长指定的数值
hincrby key field increment

// 增长指定小数的值
ingcrbyfolat field increment
  • 若是key 对应的 field的没有值,则不添加,不然插入新的field
hsetnx key field value

扩展操做1:购物车设计

场景:

电商网站购物车设计与实现

解决办法:

一、初步设计:

  • 以客户ID做为key,每位客户建立一个hash存储结构存储对应的购物车信息。
  • 将商品编号做为field,购买数量做为value进行存储。
  • 添加商品:追加全新的field与value
  • 浏览:遍历hash
  • 更改数量;自增/增减,设置value值
  • 删除商品:删除field
  • 清空:删除key

二、改进设计:

上述的设计仅仅在缓存里设置了商品ID和数量,可是一个完整的购物车是除了商品ID和数量以外,还显示必定量的商品信息(标题、介绍、类型之类),按照初步设计来的话,要显示商品信息还得去数据库查,这就形成了数据库压力增大,并无提升效率,因此商品信息也该缓存起来。

解决:

  • 将每条购物车中的商品记录保存成两条field

  • field1专用于保存购买数量:

    • 命名格式:商品ID:数量
    • 保存数据:商品数量
  • field2专用于保存商品信息:

    • 命名格式:商品ID:信息
    • 保存数据:商品信息JSON
用户:用户ID:11111
购物车
商品ID:数量
商品ID:信息

问题:若是多个用户添加同一件商品到购物车,那么每一个购物车都会保存一份一样的商品信息,因此,须要将商品信息独立出来存储,作一个独立的Hash,减小没必要要的开支。又回到了以前保存的商品ID-数量这一个设计上面。

解决:

用到了 hsetnx key field value 命令, 若是 key 对应的 field 的没有值,则不添加,不然插入新的 field

扩展操做2:用户热点信息

上述 string 操做所存储的用户热点信息为例

场景:

// set 用户:id:id号:粉丝 粉丝数
set user:id:11111:fans   20

// set 用户:id:id号:博客 博客数
set user:id:11111:blogs   20

// set 用户:id:id号:关注 关注数
set user:id:11111:focus 20
用户:ID:ID号:粉丝
粉丝数
用户:ID:ID号:博客
博客数
用户:ID:ID号:关注
关注数

// set 用户:id:id号 JSON
set user:id:11111 {ID:11111,NAME:大V,fans:20,blogs:20,focus:30}
graph LR
user:id:11111-->JSON

改为利用hash存储:

20
30
40
11111
user:id:11111
VALUE
ID
NAME
fans
blogs
focus
大V

扩展操做3:商家抢购活动

场景:

双十一活动日,销售手机充值卡的商家对移动、联通、电信的30元、50元、100元商品退出抢购活动,每种商品抢购上线100张。

解决方案:
  • 以商家ID做为 key
  • 将参与抢购的商品ID做为 field
  • 将参与抢购的商品数量做为对应的 value
  • 抢购时使用降值的方式控制产品数量(实际业务还有超卖等问题,后面再讨论)

tips : redis 应用于抢购,限购类,限量发放优惠卷,激活码等业务的数据存储设计。

对比string 和 hash

  • string适合用于整个操做(如json方式存储的信息)查询起来比较方便
  • hash 适合用于零散的操做(hash内部又是包含多个field-value)比较适合常常进行修改操做的数据

3. list (列表):

介绍

  • 数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
  • 须要的存储结构:一个存储空间保存多个数据,且经过数据能够体现进入顺序
  • list类型:保存多个数据,底层使用双向链表存储结构实现
  • Value对象内部是以LinkedList和ZipList承载。当List的元素个数和单个元素的长度较小时,redis会使用ZipList存储,减小内存的占用,其余状况使用LinkedList
    ZipList(压缩列表)使用条件
    一、列表对象保存的全部字符串元素的长度都小于64字节 二、列表对象保存的元素数量小于512个

特性:

  • list中保存的数据都是string类型的,数据总容量是有限的,最多2^32-1个元素

  • list具备索引的概念,可是操做数据时一般以队列的形式进行入队出队操做,或者以栈的形式进行入栈出栈操做。(左右操做)

  • 获取所有数据结束索引设置为-1

  • list能够对数据进行分页操做,一般第一页的信息来自于list,第二页以及更多的信息再经过数据库查询加载,这样能够避免数据库压力过大,提升查询速度,由于通常首先看到的是第一页,后面的页数信息也不必定去看。

技术方案都不会只用一种去解决问题的,都是混合式方案,经过某种技术去解决其中一个点,可能得到的提高也是很大的。

用list比较合适去解决:

  • 依赖list的数据具备顺序的特征信息进行管理
  • 使用队列模型解决多路信息汇总合并的问题
  • 使用栈模型解决最新消息的问题

举例:使用list结构实现栈和队列。
栈后进先出用lpush+blpop ,
队列先进先出用 lpush+brpop,可应对多客户端消费一个队列。

使用:

r: right右 --> rpush右边插入

l:left左 --> lpush左边插入

  • 添加/修改数据
lpush key value1 [value2].....lpush key 0 -1 0表明首个元素,-1表示倒数第一个,因此用0 -1能够查所有的元素
lpush key value1 [value2].....
  • 获取数据
lrange key start stop   // 第几位开始-第几位结束
lindex key index     // list第几个
llen   key   // list长度
  • 获取并移除数据
lpop key 
rpop key
  • 规定时间内获取并移除数据
blpop key1 [key2] timeout //block 阻塞版本的移除,根据timeout等n秒,再获取数据有数据则取,没有则nil
  • 移除指定数据
// 在某个key列表移除count个 value元素
lrem key count value

扩展操做1:朋友圈点赞列表

场景:

微信朋友圈点赞,要求按照点赞顺序显示点赞好友信息。

解决办法:

将这条朋友圈信息做为Key,点赞列表做为value,每个新人点赞都将用rpush 加到value里面,当用户想取消点赞的时候,该怎么办呢?

由于用户位置在过一段时间后,不必定会是在最后,对于这种状况能够用到上面的移除指定数据 lrem 命令lrem list 1 xxx
根据上面的例子:redis 可应用于具备操做前后顺序的数据控制 ,例如消息队列,数据队列,均可以用list来模拟

扩展操做2:用于最新消息展现

场景:

(用在数据库里的话就是与最新添加的数据按照时间倒序显示差很少 order by time DESC)

我的用户的关注列表须要按照用户的关注顺序进行展现,粉丝列表须要将最近的粉丝列在前面

新闻资讯类的网站将最新新闻资讯按照时间顺序展现

集群服务器日志统一顺序输出,将全部集群服务器日志都打到Redis缓存服务器上,再有其余服务器在缓存上读取日志。

list能存储大量数据,并且它还有存储顺序,能用对应的索引访问,看上去是很是好的存储方式,可是list的底层内部存储结构是一个链表结构。 咱们知道链表结构的存储效率是很低的,对于这么一个能存储大量数据的,可是读取速度慢的结构,就不合适用了,须要引入一种新的存储结构set

4. set (集合):

介绍:

新的存储需求:存储大量的数据,在查询方面提供更高的效率

须要的存储结构:可以保存大量的数据,高校的内部存储机制,便于查询

set类型:与hash存储结构彻底相同,仅存储键(key,field)值,不存储值(value = nil),而且值不容许重复(field自然不容许重复,重复就覆盖了以前的)也就是只利用key-field这部分来存储数据

集合中最大的成员数为 2 32 1 2^{32}-1 每一个集合可存储40多亿个成员)。

value 的空间没法使用,由于开发人员已经决定使用Set这一类型做为存储结构,那么它能调用的API已经规定了,不能跨结构使用

结构对比:
hash类型:

KEY
VALUE
FIELD1
FIELD2
FIELD3
VALUE1
VALUE2
VALUE3

set类型:value 为空

KEY
VALUE
VALUE1
VALUE2
VALUE3
nil1
nil2
nil3

基本操做:

添加数据

// 类型不容许重复,若是添加数据在set中存在,将保留一份,后续数据添加失败
sadd key member1 [member2]

获取所有数据

smembers key

删除数据

srem key member1 [member2]

获取集合数据总量

scard key

判断集合中是否包含指定数据

sismember key member

随机获取集合中指定数量的数据

srandmember key [count]

随机获取集合中的某个数据并将该数据移除集合

spop key [count]

扩展操做1:数据信息推荐

Redis 用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用APP推荐,博主大V推荐

场景:

共同好友,可能认识的人,微博XX也关注了它

解决方案:

经过下列指令来获取A ∪ B, A ∩ B
,A - B

用于同类信息的关联搜索:显示共同关注/显示共同好友

求两个集合的交、并、差集

sinter key1 [key2]

sunion key1 [key2]

sdiff key1 [key2]

求两个集合的交、并、差集并存储到指定集合中

sinterstore destination key1 [key2]  
 
 sunionstore destination key1 [key2]  
 
 sdiffstore destination key1 [key2]

将指定数据从原始集合中移动到目标集合中

smove source destination member

扩展操做2:业务权限校验

场景:

OA系统员工有一个或者多个角色,每一种角色对应多种不一样业务权限,须要快速的进行业务权限校验

解决方案:

依赖set集合数据不重复的特性,依赖sset集合hash存储结构特征完成数据过滤与快速查询

根据用户ID获取用户全部角色

根据用户全部角色获取用户全部操做权限放入set集合

根据用户全部角色获取用户全部数据入set集合

这里有两种方式来验证权限:

  1. 将当前用户:业务的全部权限都查询出来,而后将查询结果在代码里遍历比对当前用户是否拥有此权限
//查询 用户id:01业务的全部权限
smembers userid:01
  1. 直接经过key找到对应的权限返回,有则是1(表明true)
//判断userid:01集合中是否包含insert这个指定数据 
sismember userid:01 insert

对比上面1和2两种方式,咱们使用Redis来提供基础数据仍是提供校验结果?

第一种是将数据和业务逻辑分离开来,redis只提供数据,业务校验在业务层将结果遍历处理

第二种是将业务校验放在数据提供方进行校验,直接将结果返回

如今流行数据-业务分离,数据是纯粹提供基础数据,可是业务逻辑处理仍是在代码里,可是第二种的处理方式是,Redis不只仅提供了基础数据,并且还将这部分业务校验逻辑一并处理,这种方式的业务耦合度会比较高(按照分层思想,咱们不该该将业务逻辑处理放到数据提供层去),比较推荐第一种作法,该分的仍是得分,避免之后扩展业务逻辑的没必要要的麻烦

扩展操做3:网站访问数据统计

利用 Redis 的Set集合的数据去重特性,记录各类访问数据

场景:

网站数据统计,公司网站推广,须要统计网站的PV(访问量),UV(独立访问量),IP(独立IP)。

解决方案:

PV:网站被访问次数,可经过刷新页面提升访问量

这种刷新就加一的数据,能够直接用string类型存储,利用它的incr命令来统计日访问量(PV)

UV:网站被不一样用户访问的次数,可经过cookie统计访问量,相同用户切换IP地址,UV不变

创建set模型,记录不一样cookie数量(UV)利用scard统计

IP:网站被不一样IP地址访问的总次数,可经过IP地址统计访问量,相同IP不一样用户访问,IP不变

创建set模型,记录不一样IP数量(IP)利用scard统计

扩展操做4:网站黑白名单

利用 Redis 的Set集合的数据去重特性,记录各类访问数据

场景:

基于Redis制做网站黑名单,过滤IP地址、设备信息、用户信息等

解决方案:
创建set模型,记录不一样名单信息,提供给业务逻辑层进行访问权限断定

5. sorted_set (有序集合):

介绍:

数据排序有利于数据的有效展现,须要提供一种能够根据自身特征进行排序的方式

Redis 有序集合和集合同样也是string类型元素的集合,且不容许重复的成员。

在set的存储结构基础上作改变,不一样的是每一个元素都会关联一个 double/整数 类型的分数。redis正是经过分数来为集合中的成员进行从小到大的排序。若是为整数类型则为64位。

有序集合的成员是惟一的,但分数(score)却能够重复。

集合是经过哈希表实现的,因此添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1 (4294967295, 每一个集合可存储40多亿个成员)。

set类型:value 为空

KEY
VALUE
VALUE1
VALUE2
VALUE3
nil1
nil2
nil3

sorted_set类型:value 为空,多加一个score,用来作排序用

KEY
VALUE
VALUE1
VALUE2
VALUE3
nil1
nil2
nil3
score1
score2
score3

用法

添加数据

//添加KEY 排序为score1 值为member1
zadd key score1 member1 [score2 member2]

获取所有数据

zrange key start stop

// 获取key里面的数据 0 -1 【WITHSCORES为可选,加上能够显示出每一个值对应排序的分数】
zrange key start stop [WITHSCORES]

//翻转获取,倒叙-由大到小,默认由小到大排序
zrevrange key start stop [WITHSCORES]

删除数据

zrem key member [member...]

按条件获取数据

// min与max用于限定搜索查询的条件 
// start与stop用于限定查询范围,做用于索引,表示开始和结束索引 
// offset与count用于限定查询范围,做用于查询结果,表示开始位置和数据总量 

zrangebyscore key min max [WITHSCORES] [LIMIT] 

zrevrangebyscore key max min [WITHSCORES]

条件删除数据

zremrangebyrank key start stop 

zremrangebyscore key min max

获取集合数据总量

zcard key 
 zcount key min max

集合交、并操做

zinterstore destination numkeys key [key ...] 
zunionstore destination numkeys key [key ...]

扩展操做1:网站榜单排序

利用 Redis 的sorted_set集合的数据排序特性实现各种榜单,应用于计数器组合排序功能对应的排名

场景:

各种投票、网站排名、活跃度统计榜单(百度热榜),游戏亲密度(如王者荣耀的)等等。。。

解决方案:

获取数据对应的索引(排名)

zrank key member 
 zrevrank key member

score值获取与修改

zscore key member 
 zincrby key increment member

扩展操做2:网站会员试用体验

利用 Redis 应用于定时任务执行顺序管理或任务过时管理

场景:

各种VIP体验、开启投票、讨论,限时进行,逾期做废等会过时的信息

解决方案:

对于基于时间线限定的任务处理,将处理时间记录为score值,利用排序功能区分处理的前后顺序
记录下一个要处理的时间,当到期后处理对应任务,移除redis中的记录,并记录下一个要处理的时间

当新任务加入时,断定并更新当前下一个要处理的任务时间

为提高sorted_set的性能,一般将任务根据特征存储成若干个sorted_set。例如1小时内,1天内,周内, 月内,季内,年度等,操做时逐级提高,将即将操做的若干个任务归入到1小时内处理的队列中

扩展操做3:任务队列权重管理

应用于即时任务/消息队列执行管理

场景:

任务/消息权重设定应用 当任务或者消息待处理,造成了任务队列或消息队列时,对于高优先级的任务要保障对其优先处理,如 何实现任务权重管理。

解决方案:

对于带有权重的任务,优先处理权重高的任务,采用score记录权重便可

应用场景总结:

  1. redis用于控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键惟一性
  2. redis 控制数据的生命周期,经过数据是否失效控制业务行为,适用于全部具备时效性限定控制的操做
  3. redis应用于各类结构型和非结构型高热度数据访问加速
  4. redis 应用于购物车数据存储设计
  5. redis 应用于抢购,限购类、限量发放优惠卷、激活码等业务的数据存储设计
  6. redis 应用于具备操做前后顺序的数据控制
  7. redis 应用于最新消息展现
  8. redis 应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用APP推荐,大V推荐等
  9. redis 应用于同类信息的关联搜索,二度关联搜索,深度关联搜索
  10. redis 应用于同类型不重复数据的合并、取交集操做
  11. redis 应用于同类型数据的快速去重
  12. redis 应用于基于黑名单与白名单设定的服务控制
  13. redis 应用于计数器组合排序功能对应的排名
  14. redis 应用于定时任务执行顺序管理或任务过时管理
  15. redis 应用于及时任务/消息队列执行管理
  16. redis 应用于按次结算的服务控制
  17. redis 应用于基于时间顺序的数据操做,而不关注具体时间