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重启了后可以将其恢复。原生提供丰富的数据结构、缓存过期机制等等简单好用的功能。
总得来说就是两点:
- 性能
在没有使用缓存我们是这样操作数据库的:
这样频繁的操作数据库带来的问题很明确,数据量大的查询不仅效率低,还可能让数据库挂掉,这种查询就特别适合放在缓存中。
使用了缓存我们对数据库的操作就变成了这样:
使用缓存的好处:提高了查询效率和并发能力
使用redis引发 的问题:
- 缓存和数据库双写一致性问题
- 缓存雪崩问题
- 缓存击穿问题
- 缓存的并发竞争问题
后续会对这些问题做出详细说明
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 |
字符串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分布式锁核心思路
//设值锁字符串键,若存在则设值失败
SETNX("couponcode", 1)== 1//成功获取锁
SETNX("couponcode", 1)== 0//有人占用资源获取锁失败
//业务处理完毕释放分布式索
DEL("couponcode");
//设值锁字符串键的时效时间,防止宕机,系统运行意外,导致无法释放锁,防止产生死锁
应用场景
- 计数器:比如阅读量、浏览量
- 实现:
INCR key // INCR readcount::{文章id} 每阅读一次
GET key // GET readcount::{文章id} 获得阅读量
- 字符串键-分布式全局序列
一般:
获取用户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键的意义
- Hash键可以将信息凝聚在一起,而不是直接分手的存储在整个redis中,这不仅方便了数据管理,还可以尽量避免一定的误操作;
- 避免键名冲突
- 减少内存占用(主要意义)
不适用Hash键的情况
- 过期功能的使用,过期功能只能使用在key上;
- 二进制操作命令如:SETBIT、GETBIT、BITOP
- 需要考虑数据量分布的问题
List(列表键)
结构:是有头有尾的链表结构
常用命令:
散列命令 | 字符串命令 |
---|---|
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,则一直阻塞 |
应用场景
- 实现阻塞消息队列
- 实现新浪/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}元素,并且从集合键中删除 |
应用场景
- 实现直播刷礼物、转发、抽奖活动
- 实现点赞、签到、like等功能
- 可能认识的人(通过集合键运算API)
- 电商商品筛选
- 基于集合键运算API,实现与支付系统时间对账