A.Redis介绍

REmote DIctionary Server(Redis)是一个由 Salvatore Sanfilippo 写的 key-value 存储系统 。Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化 的日志型、Key-Value 数据库,并提供多种语言的 API。

Redis到底是单线程还是多线程

这个问题本身就是个坑

IO线程:

  • redis 6之前(2020年5月),单线程
  • redis 6之后,多线程,NIO 模型 ==> 主要的性能提升点

    内存处理线程:

  • 单线程 ==> 高性能的核心

B.缓存常见问题

(1).缓存穿透

问题:大量并发查询不存在的KEY,把压力直接透传到DB。
解决办法:

  1. 缓存一个KEY,其VALUE设置成业务空值。具体空值会无法判断从缓存中到底拿没拿到VALUE。
  2. 用Bloom过滤或RoaringBitmap判断KEY是否存在。数据量不大的时候也可以用Set。
  3. 完全以缓存查询结果为准,使用延迟异步加载的策略,这样就不会触发同步的DB更新。

    (2).缓存击穿

    问题:某个KEY失效的时候,正好有大量并发请求访问该KEY。
    解决办法:

  4. KEY的更新操作添加全局互斥锁,一定要加在具体的KEY上,而不是缓存上。

  5. 完全以缓存查询结果为准,使用延迟异步加载的策略,这样就不会触发同步的DB更新。

    (3).缓存雪崩

    问题:当某一时刻发生大规模的缓存失效,导致数据库压力过大。
    分析:由于更新策略,数据热点,缓存服务宕机等原因,可能会导致缓存数据同一时间点大规模不可用。
    解决办法:

  6. 更新策略在时间上做到比较均匀。

  7. 使用的热数据尽量分散到不同的机器上。
  8. 多台机器做主从复制或者多副本,实现高可用。
  9. 实现熔断限流机制,对系统进行负载能力控制。

C.Redis安装

MAC上可以用brew安装
不限OS可以使用docker安装:
docker pull redis
docker run -itd —name redis-test -p 6379:6379 redis docker image inspect redis:latest|grep -i version
docker exec -it redis-test /bin/bash $ redis-cli
> info

安装有坑:没有redis.conf文件
$ docker run -p 6379:6379 —name redis01 -v /etc/redis/redis.conf:/etc/redis/redis.conf -v /etc/redis/data:/data -d redis redis-server /etc/redis/redis.conf —appendonly yes

性能测试:
# redis-benchmark -n 100000 -c 32 -t SET,GET,INCR,HSET,LPUSH,MSET -q

D.Redis的数据结构

(1).Redis的五种基本数据结构

1.字符串String:int,String,byte[]

字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,这便意味着该 类型可以接受任何格式的数据,如 JPEG 图像数据或 json 对象描述信息等。在 Redis 中字符串类 型的 value 最多可以容纳的数据长度是512M。

keys *
set/get/getset/del/exists/append
incr/decr/incrby/decrby

注意:
1、字符串 append:会使用更多的内存
2、整数共享:如何能使用整数,就尽量使用整数。副作用:限制了 redis 内存+LRU (淘汰策略)
3、整数精度问题:redis 大概能保证16~,,17-18位的大整数就会丢失精确

2.散列Hash:Map,Pojo Class

Redis 中的 Hash 类型可以看成具有 String key 和 String value 的 map 容器。所以该类型非常 适合于存储对象的信息。如 Username、password 和 age。如果 Hash 中包含少量的字段,那 么该类型的数据也将仅占用很少的磁盘空间。

hset/hget/hmset/hmget/hgetall/hdel/hincrby
hexists/hlen/hkeys/hvals

3.链表List:java的LinkedList

在 Redis 中,List 类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表 一样,我们 可以在其头部(Left)和尾部(Right)添加新的元素。在插入时,如果该键并不存在,Redis 将 为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据 库中删除。

lpush/rpush/lrange/lpop/rpop

4.集合Set:java的Set,不重复的List

在 redis 中,可以将 Set 类型看作是没有排序的字符集合,和 List 类型一样,我们也可以在该类型 的数值上执行添加、删除和判断某一元素是否存在等操作。这些操作的时间复杂度为O(1),即常量 时间内完成依次操作。
和List类型不同的是,Set 集合中不允许出现重复的元素。

sadd/srem/smembers/sismember 相当于 set.add, remove, contains,
sdiff/sinter/sunion 相当于 集合求差集,求交集,求并集

5.有序集合Sorted Set

sortedset 和 set 极为相似,他们都是字符串的集合,都不允许重复的成员出现在一个 set 中。他 们之间的主要差别是 sortedset 中每一个成员都会有一个分数与之关联。redis 正是通过分数来为 集合的成员进行从小到大的排序。sortedset 中分数是可以重复的。

zadd key score member score2 member2… : 将成员以及该成员的分数存放到 sortedset 中
zscore key member : 返回指定成员的分数
zcard key : 获取集合中成员数量
zrem key member [member…] : 移除集合中指定的成员,可以指定多个成员
zrange key start end [withscores] : 获取集合中脚注为 start-end 的成员,[withscores]参数表 明返回的成员包含其分数
zrevrange key start stop [withscores] : 按照分数从大到小的顺序返回索引从 start 到 stop 之间 的所有元素(包含两端的元素)
zremrangebyrank key start stop : 按照排名范围删除元素
zrange key 下标s 下标e

(2).Redis的三种高级数据结构

1.Bitmaps:

setbit/getbit/bitop/bitcount/bitpos
bitmaps 不是一个真实的数据结构。而是 String 类型上的一组面向 bit 操作的集合。由于strings 是二进制安全的 blob,并且它们的最大长度是512m,所以bitmaps能最大设置2^32个不同的 bit。

2.Hyperloglogs:

pfadd/pfcount/pfmerge
在 redis 的实现中,您使用标准错误小于1%的估计度量结束。这个算法的神奇在于不再需要与需 要统计的项相对应的内存,取而代之,使用的内存一直恒定不变。最坏的情况下只需要12k,就可 以计算接近2^64个不同元素的基数。

3.GEO:

geoadd/geohash/geopos/geodist/georadius/georadiusbymember
Redis 的 GEO 特性在 Redis3.2版本中推出,这个功能可以将用户给定的地理位置(经度和纬度) 信息储存起来,并对这些信息进行操作。

E.Redis六大使用场景

(1).业务数据缓存

1、通用数据缓存,string,int,list,map 等。
2、实时热数据,最新500条数据。
3、会话缓存,token 缓存等。

(2).业务数据处理

1、非严格一致性要求的数据:评论,点击等。如网页点赞评论
2、业务数据去重:订单处理的幂等校验等。
3、业务数据排序:排名,排行榜等。

(3).全局一致计数

1、全局流控计数,控制整个集群
2、秒杀的库存计算
3、抢红包
4、全局 ID 生成

(4).高效统计计数

1.id去重,记录访问IP等全局bitmap位图操作。
2.用Hyperloglogs来做UV,PV等访问量的计数==>非严格一致性要求

(5).发布订阅与Stream

1.Pub-Sub模拟队列
docker命令:
subscribe comments
publish comments xxx

2.Redis Stream
是 Redis 5.0 版本新增加的数据结构。可以看作一个相对比较完善的消息队列机制。
具体可以参考 https://www.runoob.com/redis/redis-stream.html

(6).分布式锁

1.获取锁—单个原子性操作

因为redis内存处理是单线程的,所以set命令必定是单线程的。
SET key value NX PX 300000
==>NX:当前key不存在时再执行set,存在则不执行。可以根据此SET命令执行成功失败来判断是否拿到了当前锁。
PX:当前锁的超时时间,单位为毫秒。过期后该KEY被redis删掉。

2.释放锁

一定要保证当前锁是当前线程的锁,才能释放。以免锁超时已经被别的线程拿到,不再属于当前线程。
LUA脚本(保证读删两步操作的原子性,从而具有事务性):

  1. if redis.call("get",KEYS[1]) == ARGV[1] then
  2. return redis.call("del",KEYS[1])
  3. else
  4. return 0
  5. end

F.Redis的Java客户端

(1).官方客户端Jedis

官方客户端,类似于 JDBC,可以看做是对 redis 命令的包装。
基于 BIO,线程不安全,需要配置连接池管理连接。

(2).包心菜Lettuce

Redis官方目前主流推荐的驱动,也是SpringBoot推荐默认使用的工具。
基于 Netty NIO,API 线程安全。

(3).Reddission

基于 Netty NIO,API 线程安全。
亮点:大量丰富的分布式功能特性,比如 JUC 的线程安全集合和工具的分布式版本,分布式的基 本数据类型和锁等。

G.Redis与Spring的整合

(1).Spring Data Redis

核心是 RedisTemplate(可以配置基于 Jedis,Lettuce,Redisson)) 使用方式类似于 MongoDBTemplate,JDBCTemplate 或 JPA
封装了基本 redis 命令操作:
image.png

(2).

H.Redis的高级功能

(1).Redis事务

(2).Redis Lua(存储过程)

open resty = nginx + lua jit

(3).Redis管道pipeline

批处理命令

nc命令:不是redis命令,不可在redis-cli下执行。

(4).Redis数据备份与恢复

RDB~frm

  • 备份

执行 save 即可在 redis 数据目录生成数据文件 dump.rdb 也可以异步执行 bgsave

  • 恢复

将备份文件 (dump.rdb) 移动到 redis 数据目录并启动服务即可
查看文件夹 CONFIG GET dir
127.0.0.1:6379> CONFIG GET dir
1) “dir”
2) “/data”