Redis学习笔记-基本概念知识

性能优越

redis的性能优越主要来自三个方面

  • 基于ANSIC语言编写,接近于汇编语言的机器语言,运行速度快速
  • 基于内存读写
  • 数据库结构只有6种数据类型,数据结构简单,规则少

应用场景

  • 缓存常用的数据
  • 在需要高速读写的场合,比如一些商品抢购和抢红包的场合

业务考量

  • 业务数据常用吗?命中率如何?如果命中率很低就没有必要写入缓存
  • 该业务是读操作多,还是写操作多,如果写操作多,频繁写入数据库,也没有必要使用缓存
  • 业务数据大小如何?如果要存储几百兆字节的文件,会给缓存带来很大的压力,有没有必要?

流程图

缓存

高速读写场合

转换

   由于redis只提供基于字符串型的操作,而在Java中使用的却以类对象为主,所以需要redis存储字符串和Java对象相互转换,Spring对这些进行了封装和支持,它提供了序列化的设计框架和一些序列化的类,使用它后可以通过序列化把Java对象转换,使得redis能把它存储起来,在读取的时候,再把由序列化的字符串转化为java对象,这样在Java环境使用redis就更加简单了,所以更多时候,可以使用Spring提供的RedisTemplate的机制来使用Redis

数据类型

         数据类型     数据类型存储的值                                      说明
STRING(字符串) 可以保存字符串、整数和浮点数 可以对字符串进行操作,比如增加字符或者求字串;如果是整数或者浮点数,可以实现计算,比如自增等
LIST(列表) 它是一个链表,它的每一个节点都包含一个字符串 Redis支持从链表的两端插入或者弹出节点,或者通过偏移对它进行裁剪;还可以读取一个或者多个节点,根据条件删除或者查找结点等
SET(集合) 它是一个收集器,但是是无序的,在它里面的每一个元素都是一个字符串,而且是独一无二的,各不相同的 可以新增、读取、删除单个元素;检测一个元素是否在集合中;计算它和其他集合的交集、并集和差集等;随机从集合中读取元素
HASH(哈希散列表) 它类似于Java语言中的Map,是一个键值对应的无序列表 可以增删改查元素,也可以获取所有的键值对
ZSET(有序集合)

它是一个有序的集合,可以包含字符串、整数、浮点数、分值,

元素的排序是根据分值的大小来决定的

可以增删改查元素,根据分值的范围或者成员来获取对应的元素
HyperLogLog(基数) 它的作用是计算重复的值,以确定存储的数量 只提供基数的运算,不提供返回功能

字符串

除了getsetincrdecrmget等操作外,Redis还提供了下面一些操作,获取字符串的长度,往字符串append内容,设置和获取字符串的一段内容,设置获取字符串的某一位,批量设置一系列字符串的内容

注意事项:

  • 在测试过程中,如果开始把value设置为浮点数,那么incrdecrincrbydecrby的命令都会失败
  • redis并不支持减法、乘法、除法操作
  • 由于redis功能比较弱,所以经常会在java程序中读取它们,然后通过java进行计算并设计它们的值

应用场景

String是最常用的一种数据类型,普通的key/value存储都可以归为此类,value其实不仅是String,也可以是数字;比如想知道什么时候可以封锁一个ip地址,INCRBY命令可以使这些变得容易,通过原子递增保持计数

实现方式:m,decr等操作时会转成数值型进行计算,此时redisObjectencoding字段为int

哈希

常用命令:hget,hset,hgetall等

应用场景

比如我们要存储一个用户信息的对象数据,包含以下信息:

        用户ID,查找的key

        存储的value用户对象包含姓名name,年龄age,生日birthday等信息,

如果用普通的key/value结构来存储,主要有以下两种存储方式:

第一种方式将用户Id作为查找的key,把其他信息封装成一个对象以序列化的方式存储,

如 set u001 “李三,18,20010101”

这种方式的缺点是增加了序列化和反序列化的开销,并且在需要修改其中一项信息时,需要将整个对象取回,并且操作需要对并发进行保护,引入CAS等复杂问题

第二种方法是这个用户信息有多少成员就存储成多少个key-value对,用户ID+对应属性的名称作为唯一标识来取得对应属性的值,

如 mset user:001:name”李三”  user:001:age18 user:001:birthday”20010101”

虽然省去了序列化开销和并发的问题,但是用户ID重复存储,如果存储大量的这样的数据,内存浪费还是非常客观

那么redis提供的hash很好的解决了这个问题,Redis的hash实际上内部存储的value是一个hashMap

如 hmset user:001 name “李三”  age 18 birthday “20010101”

也就是说key任然是用户id,value是一个map,这个map的key是成员属性,value是值。这样对数据的修改和存取都可以直接通过内部的map的key(redis里称内部的key为field),也就是通过key(用户ID)+field(属性标签)操作对应的数据了,既不需要重复村数据,也不会带来序列化和并发修改。很好的解决了问题。

这里同时需要注意,redis提供了接口(hgetall)可以直接获取到全部的属性数据,但是如果内部的map的成员比较多,那么涉及到遍历整个内部的map的操作,由于Redis单线程模型的缘故,这个遍历可能会比较耗时,而其他客户端的请求完全不响应,这点需要格外注意。

实现方式:

 上面已经说的redis hash对应的value内部实际上就是一个hashmap,实际上这里会有两个不同的实现,这个hash的成员比较少的时候,redis会为了节省内存而采用类似一维数组的方式来紧凑存储,而不会采用真正的hashmap结构,对应的value redisobject的encoding为zipmap,而成员数量增大时会自动转换成真正的hashmap,此时的encoding为ht。

 

注意事项:

  • hmset命令,在javaAPI中,是使用map保存多个键值对在先的
  • hgetall命令返回所有的键值对,并保存到一个map对象中,如果hash结构很大,那么要考虑它对jvm的内存影响
  • hincrby和hincrbyFloat命令都采用Increment方法,Spring会识别它具体使用那种方法
  • redisTemplate.opsForHash().value(key)方法相当于hvals命令,它会返回所有的值,并保存到一个List对象中;而redisTemplate.opsForHash().key(key)方法相当于hkeys命令,它会获取所有的key值保存到一个set对象中
  • 在spring中使用redisTemplate.opsForHash().putAll(key,map)方法相当于执行了hmset命令,使用了map,由于配置了默认的序列化器为字符串,所以它也只会用字符串惊醒转化,这样才能执行对应的数值加法,如过使用其他的序列化器,则后面的命令可能会抛出异常
  • 在使用大的hash机构时,需要考虑返回数据的大小,以避免返回太多的数据,引发JVM内存溢出或者Redis的性能问题

链表

常用命令:lpush,rpush,lpop,lrange,BLOP等

应用场景

     Redis list应用场景非常多,也是redis最重要的数据结构之一。

我们可以轻松的实现最新消息排行功能。Lists的另一个应用就是消息队列,可以利用Lists的push操作,将任务存在Lists中,然后工作线程再用pop操作将任务取出进行执行。

实现方式

  Redis list的实现为一个双向链表,既可以支持反向查找和遍历,更方便操作,不过也带来了部分额外的内存开销,redis内部有很多实现,包括发送缓冲队列也是用的这个数据结构。

注意事项

  • 双向链表,其中以“l”开头的代表左操作,以“r”开头的代表右操作
  • 对于大量的数据操作的时候,我们需要考虑插入和删除的内容的大小,因为这将是十分消耗性能的指令,会导致Redis服务器的卡顿,对于一些不允许卡顿的一些服务器,可以进行分批次操作,以避免卡顿
  • 操作链表的命令是进程不安全的,因为多个redis客户端可能同时操作同一个链表,为了避免这种情况,Redis提供了链表阻塞命令,它们在运行的时候会给链表加锁

集合

常用命令:sadd,srem,spop,sdiff,smembers,sunion等

应用场景

Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set的重要接口,这个也是list所不能提供的。

比如微博应用中,每个人的好友在一个集合中,这样求两个人的共同好友操作,只需要求交集命令即可。Redis还提供求交集,并集,差集等操作,非常方便实用,

实现方式:

Set的内部是一个value永远为null的hashmap,实际上就是计算hash的方式快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

注意事项

  • 对于集合而言,它的每一元素都是不能重复的,当插入相同记录的时候都会失败
  • 集合是无序的
  • 集合的每一个元素都是String数据类型

有序集合

应用场景

   以某个条件为权重,比如按从大到小的次序排序

   ZREVRANGE命令可以按照得分来取前100名的用户,ZRANK       可以用来获取用户排名,非常直接而且操作容易。

Redis sorted set的使用场景与set类似,区别是set不是有序的,而sorted set可以通过额外提供一个优先级(score)的参数来为成员排序,并且插入有序的,即自动排序。

比如:twitter的public timeline可以以发表时间作为score来存储,这样获取时就是按时间自动排好的

比如:全班的成绩,sortedset,value可以是同学的学号,而score就可以是其考试得分,这样的数据插入集合,就已经进行了天然的排序。

另外还可以用sorted set来进行带权重的队列,比如普通的消息score为1,重要的消息score为2,然后工作线程可以按照score的倒序来取任务,让重要的任务先执行。

需要精确的设定过期的时间的应用

比如你可以把score的值设置为过期的时间戳,那么就可以通过简单的过期时间排序,定时清除过期数据了,不仅是清除redis的过期数据,你完全可以把这个时间戳当作数据库的索引,用redis来找出那些数据需要过期清除,然后精确的从数据库删除相应的记录。

实现方式:

Redis sorted set的内部使用Hashmap和跳跃表来保证数据的存储和有序,hash map里存储的是成员到score的映射,而跳跃表里存储的是所有成员,排序的依据是hashmap里存储的score,使用跳跃表的结构可以获得较高的查询效率,并且在实现上比较简单。

注意事项

  • 有序集合和无需集合的主要区别是对于每一个元素除了值以外,它还会多一个分数。
  • Redis支持对分数的排序
  • 集合是同故宫哈希实现的,增删改查的时间复杂度都是O(1)
  • 有序集合依赖key表示它是属于那个集ji依赖分数进行排序

基数

  • 基数是一种算法
  • 基数并不是存储元素,而是给某一个有重复元素集合(一般是很大的数据集合)评估需要的空间单元数

持久化

在redis中存在两种方式的备份:一种是快照(snapshotting),它是备份当前瞬间Redis在内存中的数据记录;另一种是只追加文件(Append-Only File,AOF),其作用就是当Redis执行写命令后,在一定的条件下将执行过的写命令依次保存在redis的文件中,将来可以一次执行那些保存的命令恢复redis的数据了。

对于快照备份而言,如果当前Redis的数据量大,备份会造成Redis卡顿,但是恢复重启是比较快速的

对于AOF备份而言,它只是追加写命令,所以备份一样不会造成Redis卡顿,但是回复重启需要执行更多的命令,备份文件可能很大

猜你喜欢

转载自blog.csdn.net/qq_33543634/article/details/84070138