Redis基础原理

2018-05-04

Redis基础原理

[TOC]

1.概念

NoSQL

非关系型数据库,Redis是非关系型数据库中的键值存储数据库

应用

处理高并发/海量数据的访问,内容缓存/内容持久化

同样具有分布式的内存对象缓存的系统还有MemCache (不能数据持久化)

简介传送门:https://blog.csdn.net/sinat_33994744/article/details/56514901

优点

  1. 快速查询,支持横向扩充(集群)和纵向扩充(加强设备)
  2. 丰富的数据类型String,list,set,sorted set,hash
  3. 一主多从,读写分离
  4. 哨兵机制,检测选举
  5. 集群机制,多主多从,数据高可用,分布式存储
  6. 保存在内存中,速度很快 读11W/s | 写8W/s

缺点

​ 存储缺少结构化(难以构建关系型理数据库模型)

2.数据类型

String

最简单的KV存储,value可以是String也可以是数字等

场景

kv字符串结构等,非常普遍

指令

SET key value 设置key=value

GET key 或者键key对应的值

​ GETRANGE key start end 得到字符串的子字符串存放在一个键

​ GETSET key value 设置键的字符串值,并返回旧值

​ GETBIT key offset 返回存储在键位值的字符串值的偏移

​ MGET key1 [key2..] 得到所有的给定键的值

​ SETBIT key offset value 设置或清除该位在存储在键的字符串值偏移

​ SETEX key seconds value 键到期时设置值

​ SETNX key value 设置键的值,只有当该键不存在

​ SETRANGE key offset value 覆盖字符串的一部分从指定键的偏移

​ STRLEN key 得到存储在键的值的长度

​ MSET key value [key value…] 设置多个键和多个值

​ MSETNX key value [key value…] 设置多个键多个值,只有在当没有按键的存在时

​ PSETEX key milliseconds value 设置键的毫秒值和到期时间

INCR key 增加键的整数值一次

incr key 让Redis统计网站访问量,速度快,减少并发问题,生成主键

​ INCRBY key increment 由给定的数量递增键的整数值

​ INCRBYFLOAT key increment 由给定的数量递增键的浮点值

​ DECR key 递减键一次的整数值

​ DECRBY key decrement 由给定数目递减键的整数值

​ APPEND key value 追加值到一个键

​ ——–操作管理———-

  • KEYS * 查看所有的Key
  • DEL key 如果存在删除键
  • DUMP key 返回存储在指定键的值的序列化版本
  • EXISTS key 此命令检查该键是否存在
  • EXPIRE key seconds 指定键的过期时间 常用
  • EXPIREAT key timestamp 指定的键过期时间。在这里,时间是在Unix时间戳格式
  • PEXPIRE key milliseconds 设置键以毫秒为单位到期
  • PEXPIREAT key milliseconds-timestamp 设置键在Unix时间戳指定为毫秒到期
  • KEYS pattern 查找与指定模式匹配的所有键
  • MOVE key db 移动键到另一个数据库
  • PERSIST key 移除过期的键 常用,让key持久化
  • PTTL key 以毫秒为单位获取剩余时间的到期键。
  • TTL key 获取键到期的剩余时间。 正数是有过期时间,-1是持久化,-2是不存在
  • RANDOMKEY 从Redis返回随机键
  • RENAME key newkey 更改键的名称
  • RENAMENX key newkey 重命名键,如果新的键不存在
  • TYPE key 返回存储在键的数据类型的值。
List(列表)

字符串列表,非常重要的Redis类型,本质是双向链表,支持反向查询和遍历,更加方便,但是会额外增加内存开销(存储双向链表),redis内部的发送缓冲队列使用的就是List结构

一般用来做排队处理的工作(队列)一个一个左边加,一个一个右边取

场景

如twitter的关注列表和粉丝列表,实现轻量级的消息队列等

指令

  • BLPOP
    BLPOP key1 [key2 ] timeout 取出并获取列表中的第一个元素,或阻塞,直到有可用
  • BRPOP
    BRPOP key1 [key2 ] timeout 取出并获取列表中的最后一个元素,或阻塞,直到有可用
  • BRPOPLPUSH
    BRPOPLPUSH source destination timeout 从列表中弹出一个值,它推到另一个列表并返回它;或阻塞,直到有可用
  • LINDEX
    LINDEX key index 从一个列表其索引获取对应的元素
  • LINSERT
    LINSERT key BEFORE|AFTER pivot value 在列表中的其他元素之后或之前插入一个元素
  • LLEN
    LLEN key 获取列表的长度
  • LPOP
    LPOP key 获取并取出列表中的第一个元素,并移除
  • LPUSH
    LPUSH key value1 value2 [value3] 在value3左边加上一个或多个值的列表
  • LPUSHX
    LPUSHX key value 在前面加上一个值列表,仅当列表中存在
  • LRANGE
    LRANGE key 0 -1 (其中0和-1的集合代表查看全部)从一个列表获取各种元素
  • LREM
    LREM key count value 从列表中删除元素
  • LSET
    LSET key index value 在列表中的索引设置一个元素的值
  • LTRIM
    LTRIM key start stop 修剪列表到指定的范围内
  • RPOP
    RPOP key 取出并获取列表中的最后一个元素,并移除
  • RPOPLPUSH
    RPOPLPUSH source destination 删除最后一个元素的列表,将其附加到另一个列表并返回它
  • RPUSH
    RPUSH key value1 value2 [value3] 在value3右边添加一个或多个值到列表
  • RPUSHX
    RPUSHX key value 添加一个值列表,仅当列表中存在
Hash

Redis Hash对应Value内部实际就是一个HashMap,实际这里会有2种不同实现,这个Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht。

场景

可以方便的存储用户信息,用户ID为Key,用户信息序列化为value存储

注意

不能设定Hash的key中指定field的过期时间(只有key的过期时间)

指令

  • HDEL
    HDEL key field[field…] 删除对象的一个或几个属性域,不存在的属性将被忽略
  • HEXISTS
    HEXISTS key field 查看对象是否存在该属性域
  • HGET
    HGET key field 获取对象中该field属性域的值
  • HGETALL
    HGETALL key 获取对象的所有属性域和值
  • HSET
    HSET key field value 设置对象指定字段的值
  • HMSET
    HMSET key field value [field value …] 同时设置对象中一个或多个字段的值
  • HSETNX
    HSETNX key field value 只在对象不存在指定的字段时才设置字段的值
  • HINCRBY
    HINCRBY key field value 将该对象中指定域的值增加给定的value,原子自增操作,只能是integer的属性值可以使用
  • HINCRBYFLOAT
    HINCRBYFLOAT key field increment 将该对象中指定域的值增加给定的浮点数
  • HKEYS
    HKEYS key 获取对象的所有属性字段
  • HVALS
    HVALS key 获取对象的所有属性值
  • HLEN
    HLEN key 获取对象的所有属性字段的总数
  • HMGET
    HMGET key field[field…] 获取对象的一个或多个指定字段的值
  • HSTRLEN
    HSTRLEN key field 返回对象指定field的value的字符串长度,如果该对象或者field不存在,返回0.
  • HSCAN
    HSCAN key cursor [MATCH pattern][COUNT count] 类似SCAN命令
  • HKEYS
  • HKeys key 查看key中所有的field
  • HVALS
  • Hvals key 查看key中所有field对应的值
  • HGETALL
  • HgetAll key 查看key中所有的fieldvalue
Set

存储数据不重复,set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。

用来去重

场景

set和list的功能类似,但是set加载的列表自动排重,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。

指令

  • SADD
    SADD key member [member …] 添加一个或者多个元素到集合(set)里
  • SACRD
    SCARD key 获取集合里面的元素数量
  • SDIFF
    SDIFF key [key …] 获得队列不存在的元素
  • SDIFFSTORE
    SDIFFSTORE destination key [key …] 获得队列不存在的元素,并存储在一个关键的结果集
  • SINTERSTORE
    SINTERSTORE destination key [key …] 获得两个集合的交集,并存储在一个集合中
  • SISMEMBER
    SISMEMBER key member 确定一个给定的值是一个集合的成员
  • SMEMBERS
    SMEMBERS key 获取集合里面的所有key
  • SMOVE
    SMOVE source destination member 移动集合里面的一个key到另一个集合
  • SPOP
    SPOP key [count] 获取并删除一个集合里面的元素
  • SRANDMEMBER
    SRANDMEMBER key [count] 从集合里面随机获取一个元素
  • SREM
    SREM key member [member …] 从集合里删除一个或多个元素,不存在的元素会被忽略
  • SUNIONSTORE
    SUNIONSTORE destination key [key …] 合并set元素,并将结果存入新的set里面
  • SSCAN
    SSCAN key cursor [MATCH pattern][COUNT count] 迭代set里面的元素

Set中key的各种关系筛选,可以两个也可以多个

设置

Sadd seta a b c d e

Sadd setb c d e f g

  • SDIFF 差集
  • Sdiff seta setb 结果 a b
  • Sdiff setb seta 结果 f g
  • SINTER 交集
    SINTER seta [setb …] 获得两个集合的交集 结果 c d e
  • SUNION 并集
  • Sunion seta setb 结果 a b c d e f g
Sorted Set

set的有序版,由HaspMap和跳跃表组成,兼顾list和set 有序去重,但是处理两方面sorted性能消耗大

场景

用户的积分排行榜需求就可以通过有序集合实现。还有上面介绍的使用List实现轻量级的消息队列,其实也可以通过Sorted Set实现有优先级或按权重的队列。

指令

  • ZADD
    ZADD key score1 member1 [score2 member2] 添加一个或多个成员到有序集合,或者如果它已经存在更新其分数
  • ZCARD
    ZCARD key 得到的有序集合成员的数量
  • ZCOUNT
    ZCOUNT key min max 计算一个有序集合成员与给定值范围内的分数
  • ZINCRBY
    ZINCRBY key increment member 在有序集合增加成员的分数
  • ZINTERSTORE
    ZINTERSTORE destination numkeys key [key …] 多重交叉排序集合,并存储生成一个新的键有序集合。
  • ZLEXCOUNT
    ZLEXCOUNT key min max 计算一个给定的字典范围之间的有序集合成员的数量
  • ZRANGE
    ZRANGE key start stop [WITHSCORES] 由索引返回一个成员范围的有序集合(从低到高)0 -1 取全部
  • ZRANGEBYLEX
    ZRANGEBYLEX key min max [LIMIT offset count]返回一个成员范围的有序集合(由字典范围)
  • ZRANGEBYSCORE
    ZRANGEBYSCORE key min max [WITHSCORES][LIMIT] 返回有序集key中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员,有序集成员按 score 值递增(从小到大)次序排列
  • ZRANK
    ZRANK key member 确定成员的索引中有序集合
  • ZREM
    ZREM key member [member …] 从有序集合中删除一个或多个成员,不存在的成员将被忽略
  • ZREMRANGEBYLEX
    ZREMRANGEBYLEX key min max 删除所有成员在给定的字典范围之间的有序集合
  • ZREMRANGEBYRANK
    ZREMRANGEBYRANK key start stop 在给定的索引之内删除所有成员的有序集合
  • ZREMRANGEBYSCORE
    ZREMRANGEBYSCORE key min max 在给定的分数之内删除所有成员的有序集合
  • ZREVRANGE
    ZREVRANGE key start stop [WITHSCORES] 返回一个成员范围的有序集合,通过索引,以分数排序,从高分到低分
  • ZREVRANGEBYSCORE
    ZREVRANGEBYSCORE key max min [WITHSCORES] 返回一个成员范围的有序集合,以socre排序从高到低
  • ZREVRANK Zrevrank
    ZREVRANK key member 确定一个有序集合成员的索引,以分数排序,从高分到低分
  • ZSCORE
    ZSCORE key member 获取给定成员相关联的分数在一个有序集合
  • ZUNIONSTORE
    ZUNIONSTORE destination numkeys key [key …] 添加多个集排序,所得排序集合存储在一个新的键
  • ZSCAN
    ZSCAN key cursor [MATCH pattern] [COUNT count] 增量迭代排序元素集和相关的分数

**Redis过期策略:https://www.jb51.net/article/103236.htm

3.Redis运用

Redis使用策略设计模式来开发(接口+实现类,方便切换单机和集群版)

文件夹格式

key中如果存在 : 那么该符号将被额外解析为文件夹层级

如 ITEM_INFO:TEST:BASE

被解析为ITEM_INFO文件夹下–>TEST子文件夹下的key ITEM_INFO:TEST:BASE

实际开发中,经常会用到集合(list读取较快)来存储缓存数据,但是Redis只支持List,不能直接支持List,因此需要制作工具类来转换Json类型读写

  1. 导入POM
  2. 导入JsonUtils工具类(将Object/List <Object >转化为字符串)
  3. 导入Redis工具类接口/实现接口
  4. 导入配置文件spring-redis/redis.properties
  5. service中注入使用即可

注意spring的加载机制:

1
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'USER_LIST' in string value "${USER_LIST}"

Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个org.springframework.beans.factory.config.PropertyPlaceholderConfigurer的Bean就会停止对剩余PropertyPlaceholderConfigurer的扫描(Spring 3.1已经使用PropertySourcesPlaceholderConfigurer替代PropertyPlaceholderConfigurer了)

所以根据加载的顺序,配置的第二个property-placeholder就被没有被spring加载,我想引入的config-wxapp.properties就没有被引入,所以在使用@Value注入的时候占位符就解析不了…解决方法就是把2个property-placeholder注解配置合并在一起就好了

4.redis事务

5.Redis持久化方案

Redis 的所有数据都是保存在内存中

数据的完整性和速度要有取舍,如果追求完整性,用关系型数据库如MySQL.追求速度用Redis等.

Rdb: 快照形式,定期把内存中当前的数据保存到磁盘中,Redis默认开启的持久化方案

在配置文件redis.config中存在,可以修改

1
2
3
4
5
6
####################SHAPSHOTTING#####################
Save the DB on disk
默认
save 900 1 15分钟内1个key变化了,则15分钟保存一次
save 300 10 5分钟内10个key变化了,则5分钟保存一次
save 60 10000 1分钟10000个key变化了,则1分钟保存一次

aof:

1
2
3
appendonly no  默认不开启,另一套持久化方案(频繁操作磁盘,会每秒钟保存一次,慢)
如果觉得rdb丢数据可能性高,那么可以开启aof模式,将增删改命令保存到指定文件(恢复时运行一下就好)
当前目录会生成一个appendonly.aof,运行它就可以恢复

Rdb和aof如果有必要可以同时开启

6.Redis集群

Redis-cluster (最少三个)
  1. 集群所有节点相互通信,ping-pong,只要连接集群任意一个节点即可连接到集群
  2. 主从节点内容相同(同步缓存,高可用),主节点之间内容不同
增强Redis高可用–哨兵机制(投票)

超过半数即生效

slot(槽)

为了保证Redis负载均衡,使用slot来维护

将所有的物理节点映射到[0-16383]个槽上

根据算法,分配数据到不同的槽上(槽和数据数量没关系,只和服务器分配有关系)

7.缓存穿透/缓存雪崩/热点key方案

8.假设redis只能缓存20W数据,如何保证2000W条数据的热点被使用

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:

  1. voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-enviction(驱逐):禁止驱逐数据