基于青训营的学习视频和一些资料,本文主要介绍了 Redis 中间件的五种基本类型以及 Redis的事务和持久化。
Num7
Redis介绍
Redis (Remote Dictionary Server),即远程字典服务。
Redis 是一个开源,内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs 等数据类型。内置复制、Lua 脚本、LRU 收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。
与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis基本类型
String(字符串)
String类型是redis的最基础的数据结构,也是最经常使用到的类型。String 类型的值最大能存储 512MB,这里的 String类型 可以是简单字符串、复杂的 xml/json 的字符串、二进制图像或者音频的字符串、以及可以是数字的字符串。
应用场景
- 缓存功能:String 字符串是最常用的数据类型,不仅仅是 redis,各个语言都是最基本类型,因此,利用 redis 作为缓存,配合其它数据库作为存储层,利用 redis 支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
- 计数器:许多系统都会使用 redis 作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
- 统计多单位的数量:eg,
uid:gongming count:0根据不同的 uid 更新 count 数量。 - 共享用户 session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存 cookie,这两种方式有一定弊端,每次都重新登录效率低下,cookie 保存在客户端,有安全隐患。这时可以利用 redis 将用户的 session 集中管理,在这种模式只需要保证 redis 的高可用,每次用户 session 的更新和获取都可以快速完成。大大提高效率。
Sring的使用
//设置键值
set key value
//设置键值及过期时间,以秒为单位
SETEX key seconds value
//设置多个键值
MSET key value [key value ...]
//根据键获取值,如果不存在此键则返回nil
GET key
//根据多个键获取多个值
MGET key [key ...]
//运算
INCR key
//将key对应的value加整数
INCRBY key increment
//将key对应的value减1
DECR key
//将key对应的value减整数
DECRBY key decrement
//追加值
APPEND key value
//获取值长度
STRLEN key
List(列表)
list 类型是用来存储多个有序的字符串的,列表当中的每一个字符看做一个元素,一个列表当中可以存储有一个或者多个元素,redis 的 list 支持存储2^32次方-1个元素。
redis 可以从列表的两端进行插入(pubsh)和弹出(pop)元素,支持读取指定范围的元素集,或者读取指定下标的元素等操作。redis 列表是一种比较灵活的链表数据结构,它可以充当队列或者栈的角色。redis 列表是链表型的数据结构,所以它的元素是有序的,而且列表内的元素是可以重复的。意味着它可以根据链表的下标获取指定的元素和某个范围内的元素集。
应用场景
- 消息队列:reids 的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过
Lpush命令从左边插入数据,多个数据消费者,可以使用BRpop命令阻塞的“抢”列表尾部的数据。 - 文章列表或者数据分页展示的应用。比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用 redis 的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率。
List的使用
//在头部插入数据
LPUSH key value [value ...]
//在尾部插入数据
RPUSH key value [value ...]
//在一个元素的前|后插入新元素
LINSERT key BEFORE|AFTER pivot value
//设置指定索引的元素值
LSET key index value
//移除并且返回 key 对应的 list 的第一个元素
LPOP key
//移除并返回存于 key 的 list 的最后一个元素
RPOP key
//返回存储在 key 的列表里指定范围内的元素
LRANGE key start stop
//裁剪列表,改为原集合的一个子集
LTRIM key start stop
//返回存储在 key 里的list的长度
LLEN key
//返回列表里索引对应的元素
LINDEX key index
Set(集合)
redis 集合(set)类型和 list 列表类型类似,都可以用来存储多个字符串元素的集合。但是和 list 不同的是 set 集合当中不允许重复的元素。而且 set 集合当中元素是没有顺序的,不存在元素下标。
redis 的 set 类型是使用哈希表构造的,因此复杂度是O(1),它支持集合内的增删改查,并且支持多个集合间的交集、并集、差集操作。可以利用这些集合操作,解决程序开发过程当中很多数据集合间的问题。
应用场景
- 标签:比如我们博客网站常常使用到的兴趣标签,把一个个有着相同爱好,关注类似内容的用户利用一个标签把他们进行归并。
- 共同好友功能,共同喜好,或者可以引申到二度好友之类的扩展应用。
- 统计网站的独立 IP。利用set集合当中元素不唯一性,可以快速实时统计访问网站的独立IP。
- 数据结构:set 的底层结构相对复杂些,使用了
intset和hashtable两种数据结构存储,intset 可以理解为数组。
Set的使用
//添加元素
SADD key member [member ...]
//返回key集合所有的元素
SMEMBERS key
//返回集合元素个数
SCARD key
//求多个集合的交集
SINTER key [key ...]
//求某集合与其它集合的差集
SDIFF key [key ...]
//求多个集合的合集
SUNION key [key ...]
//判断元素是否在集合中
SISMEMBER key membe
hash(哈希)
Redis hash 数据结构是一个键值对(key-value)集合,它是一个 string 类型的 field和value的映射表,redis 本身就是一个key-value型数据库,因此 hash 数据结构相当于在value中又套了一层key-value型数据,所以 redis 中 hash 数据结构特别适合存储关系型对象。
应用场景
- 由于 hash 数据类型的 key-value 的特性,用来存储关系型数据库中表记录,是 redis 中哈希类型最常用的场景。一条记录作为一个 key-value,把每列属性值对应成 field-value 存储在哈希表当中,然后通过 key 值来区分表当中的主键。
- 经常被用来存储用户相关信息。优化用户信息的获取,不需要重复从数据库当中读取,提高系统性能。
hash的使用
//设置单个属性
HSET key field value
//设置多个属性
HMSET key field value [field value ...]
//获取一个属性的值
HGET key field
//获取多个属性的值
HMGET key field [field ...]
//获取所有属性和值
HGETALL key
//获取所有的属性
HKEYS key
//返回包含属性的个数
HLEN key
//获取所有值
HVALS key
//判断属性是否存在
HEXISTS key field
//删除属性及值
HDEL key field [field ...]
//返回值的字符串长度
HSTRLEN key field
sorted set(有序集合)
redis 有序集合也是集合类型的一部分,所以它保留了集合中元素不能重复的特性,但是不同的是,有序集合给每个元素多设置了一个分数,利用该分数作为排序的依据。
应用场景
- 排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
- 用 Sorted Sets 来做带权重的队列,比如普通消息的 score 为1,重要消息的 score 为2,然后工作线程可以选择按 score 的倒序来获取工作任务。让重要的任务优先执行。
sorted set的使用
//添加
ZADD key score member [score member ...]
//返回指定范围内的元素
ZRANGE key start stop
//返回元素个数
ZCARD key
//返回有序集key中,score值在min和max之间的成员
ZCOUNT key min max
//返回有序集key中,成员member的score值
ZSCORE key member
Redis事务
Redis 事务提供了一种将多个命令打包, 然后一次性、按顺序地执行的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。
Redis 中一个事务从开始到执行会经历开始事务(muiti)、命令入队和执行事务(exec)三个阶段,事务中的命令在加入时都没有被执行,直到提交时才会开始执行(Exec)一次性完成。
一组命令中存在两种错误不同处理方式:
- 代码语法错误(编译时异常)所有命令都不执行
- 代码逻辑错误(运行时错误),其他命令可以正常执行 (该点不保证事务的原子性)
Redis 不支持回滚来保证原子性的优点:
- Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
- 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。
鉴于没有任何机制能避免程序员自己造成的错误, 并且这类错误通常不会在生产环境中出现, 所以 Redis 选择了更简单、更快速的无回滚方式来处理事务。
Redis持久化
Redis 是一种内存型数据库,一旦服务器进程退出,数据库的数据就会丢失,为了解决这个问题 Redis 供了两种持久化的方案,将内存中的数据保存到磁盘中,避免数据的丢失两种持久化方式:快照(RDB文件)和追加式文件(AOF文件),下面分别为大家介绍两种方式的原理。
- RDB 持久化方式会在一个特定的间隔保存那个时间点的数据快照。
- AOF 持久化方式则会记录每一个服务器收到的写操作。在服务启动时,这些记录的操作会逐条执行从而重建出原来的数据。写操作命令记录的格式跟Redis协议一致,以追加的方式进行保存。
- Redis 的持久化是可以禁用的,就是说你可以让数据的生命周期只存在于服务器的运行时间里。
- 两种方式的持久化是可以同时存在的,但是当 Redis 重启时,AOF 文件会被优先用于重建数据。
发布订阅
- 发布者不是计划发送消息给特定的接收者(订阅者),而是发布的消息分到不同的频道,不需要知道什么样的订阅者订阅。
- 订阅者对一个或多个频道感兴趣,只需接收感兴趣的消息,不需要知道什么样的发布者发布 的。
- 发布者和订阅者的解耦合可以带来更大的扩展性和更加动态的网络拓扑。
- 客户端发到频道的消息,将会被推送到所有订阅此频道的客户端。
- 客户端不需要主动去获取消息,只需要订阅频道,这个频道的内容就会被推送过来。
消息的格式
消息类型(包含三种类型)
- subscribe:表示订阅成功。如果第一部分的值为
subscribe,则第二部分是频道,第三部分是现在订阅的频道的数量。 - unsubscribe:表示取消订阅成功。如果第一部分的值为
unsubscribe,则第二部分是频道,第三部分是现在订阅的频道的数量,如果为 0 则表示当前没有订阅任何频道,当在Pub/Sub以外状态,客户端可以发出任何 redis 命令。 - message:表示其它终端发布消息。如果第一部分的值为
message,则第二部分是来源频道的名称,第三部分是消息的内容。
//订阅
SUBSCRIBE 频道名称 [频道名称 ...]
//取消订阅,如果不写参数,表示取消所有订阅
UNSUBSCRIBE 频道名称 [频道名称 ...]
//发布
PUBLISH 频道 消息
小结
Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件,速度快、持久化、功能丰富、简单稳定等特点,希望大家看完有所收获。