Redis实操

Posted by wantu on June 13, 2019

为什么要用Redis以及会带来哪些问题?

用的问题

  1. 性能。我们在实际业务的编码中肯定会同数据库进行交互,碰到一些复杂的业务时会执行一些时间过久且执行的结果不频繁变动的SQL。这个时候我们可以考虑 将结果放到缓存中,这样后面的查询请求就能够迅速响应。
  2. 并发。在大并发的场景下,所有请求直接访问数据库,数据库会出现连接异常。我们可以考虑使用Redis做一个缓冲操作,让请求先访问到Redis,而不是直接访问数据库。

可能带来的问题

  1. Redis数据和数据库数据不一致的问题
  2. 缓存雪崩问题
  3. 缓存击穿问题
  4. 缓存的并发竞争问题

基础

基本数据结构

string Redis都是以唯一的key字符串作为名称,然后通过这个key来获取value。值得注意的是Redis的字符串是动态字符串是可以修改的字符串,内部结构类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存频繁分配。当字符串的长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容1次只会多扩容1M的空间,需要注意的是字符串的最大长度是512M。
list。Redis的list相当于Java的LinkedList,注意它是链表不是数组,删除和插入非常快,但是查询的性能慢。当列表弹出最后一个元素的时候该数据结构自动会被删除,内存被回收。Rediscover的底层存储还不是一个简单的linkedlist,而是被称之为快速列表quicklist的一个结构。首先在列表元素较少的情况下会使用一块连续的内存存储空间,这个结构就是ziplist即是压缩列表,当数量较多的时候才会改成quicklist。此举的原因是普通的链表需要的附加指针空间太大,会比较浪费空间,而且会加重内存的碎片化。

hash Redis的字典相当于Java中的HashMap,它是无序字典,内部实现的结构也是数组加链表的实现方式,不同的是Redis字典的值只能是字符串。另外他们的rehash的方式不一样,Java的HashMap在字典很大的时,rehash是个耗时的操作,需要一次性全部rehash。Redis为了提高性能,采用的是渐进式rehash策略。渐进式rehash会在rehash的同时保留两个hash结构,查询时会同时查询两个hash结构,然后在后续的定时任务中以及hash操作指令中,循序渐进地将旧hash的内容一点点迁移到新的hash结构中,当迁移完成,就会使用新的hash结构取而代之。hash结构不同于字符串一次性需要全部序列化整个对象,hash可以将对用户结构中的每个字段单独存储,获取对象信息时也可以进行部分获取。hash的缺点在于hash结构的存储消耗要高于单个字符串。
set Redis的set相当于Java中的HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典所有value都是NULL。当集合最后一个元素移除之后,数据结构自动删除,内存被回收。常用的业务场景:储存中将用户ID。因为去重功能,可以保证一个用户不会中奖2次。
zset 类似于Java中的SortedSet和HashMap的结合体。一方面它是一个set,保证了内部value的唯一性,另一方面它可以为每个value赋予一个score,这个代表value的排序权重,它内部使用一种叫做跳跃列表的数据结构.

到底选字符串还是哈希

取决于怎么访问数据。
选字符串的场景:
1、大多数我们访问了对象上的大多数字段。
2、key的类型存在差异
选hash的场景:
1、大多数只访问单个字段
2、 始终知道哪些字段可用。

容器对象的通用规则

  1. create if not exists。如果容器不存在,那就创建一个,再进行操作。
  2. drop if no elements。如果容器元素没有了,那么立即删除元素,释放内存。

过期时间

Redis的所有数据结构都可以设置过期时间,时间到了,Redis会自动删除响应的对象。需要注意的是过期是以对象为单位的,比如hash结构的过期是以整个hash对象的过期,而不是其中某个子key。还有一个地方需要注意,如果一个字符串已经设置了过期时间,然后你调用了set方法修改了它,它的过期时间会消失。

HyperLogLog

HyperLogLog数据结构的使用场景:数据量特别巨大,但是对统计的精确度要求不高的情况下,HyperLogLog推荐使用。因为HyperLogLog总共占用12K的内存而set会随着数据增长而线性增长。 12K占用的算法是在Redis的HyperLogLog实现中用到的是2^14个桶,每个桶需要6bits来存储,最大可以表示maxbits=63,于是总共内存占用就是 2^14 * 6 / 8 = 12K
两命令:pfadd, pfcount

布隆过滤器

使用的业务场景:专门用来解决去重问题,在起到去重的同时在空间上还能节省百分之旧事以上,只是稍微有那么点不准确。
几个命令:bf.add bf.madd bf.exists bf.mexists。注意Redis版本需要在4.0以上。
布隆过滤器使用的目的不是为力存储对象,而是标记对象,以在后续能判断此对象是否被标记过。
原理: 每个布隆过滤器对应到Redis中的数据结构就是一个大型的位数组和几个不一样的无偏hash函数。所谓的无偏就是能够把元素的hash值算的比较均匀。 向布隆过滤器中添加key时,会使用多个hash函数对key进行hash算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为1就完成隆add操作。
向布隆过滤器询问key是否存在时,就跟add一样,也会把hash的几个位置都算出来,看看位数组中这几个位置是否都是1,只要有一个位为0那说明布隆过滤器中这个key不存在,如果都是1也布能说明这个key就一定存在,只是极有可能存在。