本文仅针对对Redis不熟悉的开发人员做入门培训。
官方网站 https://redis.io/
1、Redis是什么?
Redis 是一个基于内存的高性能key-value数据库,全称是 (Remote Dictionary Server,远程字典服务)。
2、Redis有什么特点?
- 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
- 支持丰富的数据类型
- 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
- 缺点 是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写
3、Redis的数据类型以及相关的一些基础语法。
基础数据类型如下图所示
string
作为最简单的类型,基本用法其实很多,下面简单介绍几种常见用法
- 单值缓存
Set key value Get key GetSet key value 进行一些简单的数据缓存
- 对象缓存
Set user:1 jsonvalue Mset user:1:name 张三 user:1:age 18 Mget user:1:name user:1:age 进行一些常用的对象缓存,在一些情况下会比hash更高效
- 简易分布式锁
Setnx loclk:1 true (如果没有这个key会返回1,并且添加这个key,否则返回0) Del lock:1 这种分为两步,1设置锁、2删除锁。存在一定的风险,比如设置了锁,但是删除出现异常,会导致程序死锁。 建议使用下面这种语句实现简单的锁 Set lock:1 true ex 10 nx(这个命令可以有效防止程序异常终止导致的死锁)
- 计数器
Incr article:readCount:1 //Incrby article:readCount:1 100 适用场景:文章的阅读数,网站的被访问次数,订单编号的生成(高并发情况下一次取多个, 放到程序内部慢慢消化)等 Decr article:readCount:1 //Decrby article:readCount:1 100 适用场景:登录异常次数限制(设置账号或者ip为key,设值为登陆次数,每次登陆失败执行此操作, 当返回值小于0时,限制该账号或者ip调用登录接口) Get article:readCount:1
- 设置过期
expire key 10 (默认时间单位为秒)
hash
作为比较常用的类型,基本用法其实很多,下面简单介绍几种常见用法
- 基操
HSet Key fielId value //插入一条hash记录 HSet sc:10086 1 1 (新增返回1,修改返回0) HIncrby Key fielId value //增加数量 Hincrby sc:10086 1 10(返回增加之后的数值) HLen Key fielId //获取总数 Hlen sc:10086(返回的是hash的行数) HDel key fielId //删除hash的一个key HDel sc:10086 HGetall key //获取hash内部的所有值 HGetall sc:10086
优点:
- 同类 数据归类整合存储,方便数据管理
- 相对于string操作更低的消耗内存与cpu
- 相对于string存储更节省空间
缺点:
- 无 法针对fielId设置过期时间,只能针对key进行设置
- Redis集群架构下不适合大规模使用,因为无法进行数据分片存储,会导致数据过于集中,从而导致单节点压力过大
list
可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
内部实现是一个双向链表,两端的查找时间复杂度为0(1),具有两端读取添加速度极快的特点。
- 基操
LPush Key value //将一条记录推入表头(最左侧) LPush ListA 1 (返回列表的长度) RPush Key value //将一条记录推入表尾(最右侧) RPush ListA 1 (返回列表的长度) LPop key //移除并返回列表的头元素 LPop ListA RPop key //移除并返回列表的尾元素 RPop ListA LRange key start stop //返回指定区域中的元素,区间以偏移量制定 LRange ListA 2 5 BLPop key [key] timeout //在表头弹出一个元素,如果没有的话则阻塞等待timeout秒, 若timeout=0,一直阻塞 BLPop ListA 99 10 BRPop key [key] timeout //在表尾弹出一个元素,如果没有的话则阻塞等待timeout秒, 若timeout=0,一直阻塞 BRPop ListA 99 10
作用:构建常用的数据结构 Stack(栈) = LPush + LPop Queue(队列) = LPush + RPop Blocking MQ(阻塞队列) = LPush + BRPop
应用场景:消息流(将用户的通知消息的主键存入list,用户登陆之后推送给用户,而不是去数据库进行查找)
set
集合类型, 数据不重复且无序 ,内部实现是一个 散列表, 所以集合中的元素增删等操作时间复杂度均为0(1),常见的操作为交集、并集、差集的运算。
- 基操
SAdd key member //往集合中存入元素,元素存在就忽略,key不存在就创建 SAdd Students Tom SRem key member //集合中删除元素 SRem Students Tom SMembers key //返回集合的所有元素 SMembers Students SCARD key //获取集合中的元素个数 SCARD Students SisMember key member //判断元素是否存在于集合中 SisMember Students jack 适用场景:用户的好友列表、关注列表、系统黑名单、Tag系统、文章的收藏列表等 SRandmember key count //在集合key中选出count个元素 SRandmember Students 1 Spop key count //在集合key中选出count个元素并删除它 SPop Students 1 适用场景:抽奖机制,随机排班等
- 运算操作
SInter key key key …… //求交集 SInter class1 class2 class3 SInterStore newKey key key key …… //把交集结果存入newKey中 SInterStore class1-2-3 class1 class2 class3 SUnion key key key …… //求并集 SUnion class1 class2 class3 SUnionStore newKey key key key …… //把并集结果存入newKey中 SUnionStore class1-2-3 class1 class2 class3 SDiff key key key …… //求差集 SDiff class1 class2 class3 SDiffStore newKey key key key …… //把差集结果存入newKey中 SDiffStore class1-2-3 class1 class2 class3 适用场景:共同好友模型(Sinter) 可能认识的人模型 (SDiff) 共同关注模型 (SUnion )
Zset
有序集合类型, 数据不重复且有序 ,为集合中的每个元素都关联了一个分数。
- 基操
基本上具有list和set的基操集合
适用场景: 时间轴(使用时间作为score) 积分排行榜(积分作为score) 消息队列(具有优先级以及权重的特点)
Geo
其实也是一种有序集合类型, 数据不重复且有序 ,这里关联的分数是程序通过某种算法对经纬度进行计算 得出的。
- 基操
GeoAdd key log lat member //存入一个地理信息,key不存在就创建 geoadd city 109.89 78.11 上海 Geopos key member //返回指定元素的位置 Geopos city 上海 GeoDist key member1 member2 [unit] //返回两个地点之间的距离 有一个地点不存在就返回空值 //GeoDist city 上海 北京 km util单位: m表示单位为米 km表示单位为千米 mi表示单位为英里 ft表示单位为英尺 GeoRadius key longitude latitude radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc] [count count] //以给定的经纬度为中心,返回键包含的位置元素当中,与中心的距离不超过给定最大距离的所有位置元素。
使用场景: 附近的人(陌陌、微信) 地理信息相关的操作场景,比如 计算两点之间的距离,或者某个地点周围多少距离的所有地点集合等
HyperL ogLog
适用场景应该是和 布隆过滤器有关, 涉及到的是缓存穿透的解决方案相关知识,不做赘述 。
3.1、list和Zset的对比
相同点:
- 都是有序的
- 都可以获取某一个范围内的元素
不同点:
- 列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
- 有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。 列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
- 有序集合要比列表类型耗更多的内存
4、一些常见的概念介绍
- 缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候, 先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
- 缓存雪崩
由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的 缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造 成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
- 缓存穿透
指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次 都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)
- 缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数 据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存 ,前者则是很多key。
5、是否有必要使用redis做缓存?