Redis入门
一、Redis简介
Redis官网(http://www.redis.cn)对Redis的介绍是:Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence),并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
简单来说: Redis是一个高性能的Key-Value存储系统,使用ANSI C语言编写、支持网络、可持久化,并且提供多种语言的API。
二、Redis技术
Redis技术,性能十分优越,可以支持每秒十几万次的读写操作,支持集群、分布式、主从同步等配置。还能支持一定的事务能力,可在高并发访问的场景下保证数据安全和一致性。
1、Redis性能优越原因
(1)基于ANSI C语言编写,接近汇编语言的机器语言,运行速度快。
(2)基于内存的读写,比基于磁盘读写的数据库快很多。
(3)只有6中数据库结构,规则少,处理快。
2、Redis在JavaWeb中的应用
(1)缓存常用数据
(2)在需要高速读写的场合使用Redis来快速读写。
3、在Spring中使用Redis
由于redis是基于字符串操作的,而Java是以对象为操作的,没有办法直接将Java对象存储到redis中,需要进行两边的转换。
Spring封装了序列化的操作,通过序列化来转换两边,使得Java程序能够更简单的使用Redis。Spring还提供了RedisTemplate机制来使用Redis。具体代码参考:
https://mvnrepository.com/artifact/redis.clients/jedis
https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis
4、Redis的6种数据类型
(1)字符串String
字符串以一个键和一个值存储在Redis内部,Redis通过键去找值。
如果字符串是数字(整数或者浮点数),Redis还能提供简单的运算,比如incr(增1),decr(减1)等等。
底层实现:Redis没有用C语言的字符串,而是自己构建了一种SDS(simple dynamic string 简单动态字符串)的抽象类型作为自己的默认字符串表示。
SDS的结构:len(记录保存的字符串长度)+free(未使用字节的数量)+buf(字节数组,用于保存字符串)。
SDS比C字符串的优点:
<1>常数复杂度获取字符串长度。 因为SDS中的len字段直接记录字符串长度,不必对字符串进行遍历。
<2>杜绝缓冲区溢出。 当要对SDS进行修改时会首先检查空间是否组否,如果不足会自动扩容,再进行实际的修改。而不会出现C程序员如果忘记分配空间就修改字段导致内存溢出的问题。
<3>减少修改字符串长度时所需的内存重新分配次数。 SDS通过free字段解除了字符串长度和底层数组长度的关系。采用“空间预分配”和“惰性空间释放”两种优化策略。
<4>二进制安全。 SDS中的buf保存一系列二进制数据,并用len来判断字符串是否结束。不是以 ‘\0’ 作为判断依据,二进制安全。
<5>兼容部分C字符串函数。 SDS遵循C字符串以空字符结尾的惯例,使得SDS可以重用部分<string.h>库定义的函数。
(2)链表List
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活的调整链表的长度。链表被广泛用于实现Redis的各种功能,比如列表键、发布-订阅、慢查询、监视器等等。
底层实现:Redis构建了自己的链表实现(因为C语言没有内置这种数据结构)。
链表结构:head(头节点)+tail(尾节点)+len(节点数量)+一些函数
节点结构:prev(前节点)+next(后节点)+value(节点的值)
特点:双端、无环、多态(节点值是 void* value可以保存不同类型的值)
(3)集合Set
它是一个收集器,但是是无序的。在set里面每个元素都是一个字符串。可以增加、读取和删除单个元素。检测一个元素是否在集合中,计算它和其他集合的交集、并集和差集。可以随机从集合中读取元素。
底层实现:集合的底层可能是intset(整数集合)或者是hashtable(字典)。整数集合的底层是数组实现的,适用于集合的所有元素都是整数值并且不超过512个。
(4)哈希结构Hash
它类似Java语言的Map,是一个键值对应的无序列表。
底层实现:Redis构建了自己的字典实现。使用哈希表作为底层实现,一个哈希表里可以有多个哈希表节点,每个哈希表节点保存字典的一个键值对。
哈希表结构:table(哈希表数组)+size+sizemask(用于计算索引值的掩码)+used(已有节点的数量)。
(5)有序集合zset
有序集合的排序是根据分值的大小来决定的。Redis可以增删查改元素,可以根据分值的范围或者成员来获取对应的元素。
底层实现:有序集合的编码可以是ziplist或者是skiplist。
ziplist的底层使用压缩列表作为底层实现。每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。分值排序是从小到大。
skiplist使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个表。zsl跳跃表按分值从小到大保存了所有集合元素,每个跳跃表节点都保存了一个集合元素。
(6)基数HyperLogLog
基数的作用是计算重复的值,以确定存储的数量,只提供基数的运算,不提供返回值。
5、Redis的事务
(1)Redis的事务是使用MULTI-EXEC的命令组合实现的。
<1>事务是一个被隔离的操作,事务中的方法都会被Redis进行序列化并按顺序执行。事务在执行过程中不会被其他客户端发生的命令打断。
<2>事务是一个原子性操作,要么全部执行,要么全部不执行。
(2)Redis事务机制:在Redis开启事务的命令是multi,执行事务的命令是exec,两个命令之间的redis命令将进入队列,直到exec命令的出现才一次性执行,执行过程中不能再插入任何命令。
(3)事务回滚 discard
如果要回滚事务,用discard命令,这样事务中的方法就不会执行了。
【特别的】Redis在执行事务的命令的时候,会进行检测。如果命令不正确,就会产生错误。无论之前还是之后的命令都会被事务回滚。 如果命令正确而数据结构操作错误,则该命令的执行出现错误,其之前和之后的命令都会被正常执行。(这和数据库很不一样!)
(4)watch命令监控事务
Redis使用watch命令决定事务是执行还是回滚。
Redis在执行事务的过程中,并不会阻塞其他连接的并发,而只是通过比较watch监控的键值对去保证数据的一致性。当键值对发生变化,就要回滚事务。
6、流水线pipeliend
Redis通过流水线技术来提高Redis命令性能。
Redis事务是有系统开销的,Redis的流水线pipelined技术是在没有任何附加条件的场景下去使用队列批量执行一系列命令,以提高系统性能。
Redis流水线其实是一种通信协议。
7、发布-订阅
Redis的发布与订阅功能由 PUBLISH、SUBSCRIBE、PSUBSCRIBE等命令组成。
通过执行SUBSCRIBE命令,客户端可以订阅一个或多个频道,从而成为这些频道的订阅者(subscriber):每当有其他客户端被订阅的频道发送消息(message)时,频道的所有订阅者都会收到这条消息。
8、超时命令与回收机制
Redis是基于内存运行的数据集合,当内存不足时,Redis会触发自动垃圾回收机制。因为Redis有del命令可以删除一些键值对,所以Redis比Java的GC更灵活一点。
Redis可以通过一些命令如expire对键值设置超时。
【重点】Redis的key超时不会被其自动回收,它只会标识哪些键值对超时了。
Redis提供了两种方式来回收超时的键值对:
(1)定时回收:在确定的某个时间触发一段代码回收超时的键值对。
(2)惰性回收:当一个超时的键再次被get命令访问时,将触发redis将其从内存中清空。
三、Redis与数据库的异同
1、Redis的数据主要存储在内存中(部分可以持久化到硬盘); 数据库的内存主要存储在磁盘中。
2、Redis的数据结构比较简单,功能有限; 数据库是范式,完整性和规范性的规则多,处理业务复杂功能更强大。
3、Redis并不安全稳定,基于内存,一旦停电或故障就会失去数据; 数据库基于磁盘不会出现这样的问题。
总而言之:Redis的数据完整性、事务能力、安全性、可靠性和可扩展性目前都不及数据库。Redis目前还不能够取代数据库。但是Redis作为一种提高互联网应用性能的辅助工具是十分有用的,Redis性能高,在高并发的场景下能够保证数据的安全和一致性。