Redis(二 数据类型及使用场景详解)

了解redis

官网

从官网中我们可以看到:“Redis is an open source (BSD licensed),in-memory data structure store, used as a database,cache and message broker.”

这句话的意思是:

  • Redis是一个开源的,基于内存的数据结构存储,可用作于数据库、缓存、消息中间件。
  • 从官方的解释上,我们可以知道:Redis是基于内存,支持多种数据结构。
  • 从经验的角度上,我们可以知道:Redis常用作于缓存。

为什么使用redis

我们知道redis存储方式是以key-value,这和Java中的Map容器不是类似的吗?那为什么有了java map容器还是要使用redis呢?

  • Java实现的Map是本地缓存,如果有多台实例(机器)的话,每个实例都需要各自保存一份缓存,缓存不具有一致性
  • Redis实现的是分布式缓存,如果有多台实例(机器)的话,每个实例都共享一份缓存,缓存具有一致性
  • Java实现的Map不是专业做缓存的,JVM内存太大容易挂掉的。一般用做于容器来存储临时数据,缓存的数据随着JVM销毁而结束。Map所存储的数据结构,缓存过期机制等等是需要程序员自己手写的。
  • Redis是专业做缓存的,可以用几十个G内存来做缓存。Redis一般用作于缓存,可以将缓存数据保存在硬盘中,Redis重启了后可以将其恢复。原生提供丰富的数据结构、缓存过期机制等等简单好用的功能。

总得来说就是两点:

  1. 性能
    在没有使用缓存我们是这样操作数据库的:
    Redis(二 数据类型及使用场景详解)
    这样频繁的操作数据库带来的问题很明确,数据量大的查询不仅效率低,还可能让数据库挂掉,这种查询就特别适合放在缓存中。

使用了缓存我们对数据库的操作就变成了这样:
Redis(二 数据类型及使用场景详解)
使用缓存的好处:提高了查询效率和并发能力

使用redis引发 的问题:

  1. 缓存和数据库双写一致性问题
  2. 缓存雪崩问题
  3. 缓存击穿问题
  4. 缓存的并发竞争问题

后续会对这些问题做出详细说明

redis的数据结构:

redis常用的数据结构有:String、hash、set、list

String

常用命令:

命令 描述
SET key value 设置指定 key 的值
GET key 获取指定 key 的值
MSET key value [key value …] 批量设置字符串键值
GET key 获取指定 key 的值
MGET key1 [key2…] 获取所有(一个或多个)给定 key 的值
DEL key [key …] 删除指定key(一个或多个)
EXPIRE key seconds 设置一个key的过期时间(秒)
PEXPIRE key milliseconds 设置一个key的过期时间(毫秒)
INCRBY key increment 对数字key进行{increment}的增加
DECRBY key decrement 对数字key进行{decrement}的减少
INCR key 对数字key自增1
DECR key 对数字key自减1

reids更多详细命令

字符串key的设计原则

有如下表:
表明 student

id name age
1 zou 20
2 wh 20
3 hx 20

设值:
MSET student::{id}::name zou student::{id}:: age 19
取值:
MGET student::{id}::name student::{id}::age
Redis(二 数据类型及使用场景详解)
在线redis试用

Redis分布式锁核心思路
//设值锁字符串键,若存在则设值失败
SETNX("couponcode", 1== 1//成功获取锁
SETNX("couponcode", 1== 0//有人占用资源获取锁失败
//业务处理完毕释放分布式索
DEL("couponcode");
//设值锁字符串键的时效时间,防止宕机,系统运行意外,导致无法释放锁,防止产生死锁
应用场景
  1. 计数器:比如阅读量、浏览量
    Redis(二 数据类型及使用场景详解)
  • 实现:
INCR key // INCR readcount::{文章id} 每阅读一次
 GET key // GET readcount::{文章id}  获得阅读量
  1. 字符串键-分布式全局序列
    一般:

获取用户ID: INCR(“userID”)

一次拿一个,这样每次都要去读写io,太浪费性能了,所以一般是取一段

INCRBY(“userID”,1000)

Hash

类似java中的:
HashMap<String, HashMap<String, String>> h = new HashMap<String, HashMap<String, String>> ()
常用命令:

命令 描述
HSET key field value 存储一个散列键
HSETNX key field value 只有在字段 field 不存在时,存储这个散列键
HMSET key field1 value1 [field2 value2 ] 一个key中存储多个filed
HGET key field 获取key filed的散列键值
HMGET key field1 [field2] 批量获取key中多个filed的值
HDEL key field1 [field2] 删除一个或多个哈希表字段
HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment

设值:
HMSET student 1::name zou 1::age 20
取值:
HMGET student 1::name 1::age

字符串键 VS Hash键
散列命令 字符串命令
HSET SET
HGET GET
HSETNX SETNX

这里可以看到Hash键和String键差不多

Hash键的意义
  1. Hash键可以将信息凝聚在一起,而不是直接分手的存储在整个redis中,这不仅方便了数据管理,还可以尽量避免一定的误操作;
  2. 避免键名冲突
  3. 减少内存占用(主要意义)
不适用Hash键的情况
  1. 过期功能的使用,过期功能只能使用在key上;
  2. 二进制操作命令如:SETBIT、GETBIT、BITOP
  3. 需要考虑数据量分布的问题

List(列表键)

结构:是有头有尾的链表结构
Redis(二 数据类型及使用场景详解)

常用命令:

散列命令 字符串命令
LPUSH key value1 [value2] 向key的列表键左边放入一个元素,key不存在则新建
RPUSH key value1 [value2] 向key的列表键中右边放入一个元素,key不存在则新建
LPOP key 向key的列表将最左端弹出一个元素
RPOP key 向key的列表键最右端弹出一个元素
LRANGE key start stop 获取列表建从start下标到stop下标的元素
BLPOP key1 [key2 ] timeout 阻塞的从key的列表键最左端弹出一个元素,若列表建中不存在元素,阻塞等待{timeout}秒,若{timeout} = 0 一直阻塞
BRPOP key1 [key2 ] timeout 阻塞的从key的列表键最右端弹出一个元素,若列表键中不存在元素,阻塞等待{timeout}秒,若{timeout} = 0,则一直阻塞
应用场景
  1. 实现阻塞消息队列
  2. 实现新浪/Twitter 用户消息列表功能

Set(应用场景最多)

set特点:无序,不重复
常用命令:

命令 描述
SADD key member1 [member2] 向集合键key中存放元素,若key不存在则新建
SREM key member1 [member2] 移除集合中一个或多个成员
SMEMBERS key 返回集合中的所有元素
SCARD key 获取集合键的元素个数
SISMEMBER key member 判断{member}元素是否存在于集合键key中
SRANDMEMBER key [count] 从集合键key中选出{count}元素,不从集合key中删除
SPOP key [count] 从集合key中选出{count}元素,并且从集合键中删除
应用场景
  1. 实现直播刷礼物、转发、抽奖活动
  2. 实现点赞、签到、like等功能
  3. 可能认识的人(通过集合键运算API)
  4. 电商商品筛选
  5. 基于集合键运算API,实现与支付系统时间对账