目录
一、Redis是什么?
二、redis常用命令
三、redis的五种常用数据结构
字符串(strings)
列表(lists)
哈希表
集合(sets,无序)
有序集合(sorted sets)
四种高级类型:
四、Geospatial
五、Bitmap:位图
六、Stream:
七、redis的持久化:(将内存中的数据异步写入硬盘中)
八、Redis的事务
九、 LUA脚本
十、哨兵模式(sentinel)
十一、 Redis集群
十二、 Redis应用问题
一、Redis是什么?
- Redis是一个高性能键值对存储系统,可用于数据缓存
特点:
- redis的key:value存储系统中的value支持五种基本数据类型:string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
为了保证效率,数据都是缓存在内存中
- Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。
- 并且在此基础上实现了master-slave(主从)同步。
memcached是多线程+锁技术,Redis是单线程+多路IO复用技术
redis的应用场景:
- 主要用于处理大量数据的高访问负载,配合关系型数据库做高速缓存。
- 分布式架构,做session共享
Redis在项目中的使用场景:
缓存(核心)、分布式锁(set + lua 脚本)、排行榜(zset)、计数(incrby)、消息队列(stream)、地理位置(geo)、访客统计(hyperloglog)等。
二、redis常用命令
- keys *:查看当前库所有key (匹配:keys *1)
- exists key:判断某个key是否存在
- type key:查看你的key是什么类型
- del key:删除指定的key数据
- unlink key:根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。
- expire key 10:10秒钟:为给定的key设置过期时间
- ttl key:查看还有多少秒过期,-1表示永不过期,-2表示已过期
- select:命令切换数据库
- dbsize:查看当前数据库的key的数量
- flushdb:清空当前库
- flushall:通杀全部库
key命名的要点:
- key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
- key也不要太短,太短的话,key的可读性会降低;
- 在一个项目中,key最好使用统一的命名模式,例如user:10000:passwd。
redis文件夹下的文件解释:
- redis-server.exe文件用于启动Redis服务
- redis-cli.exe文件为Redis在Windows开发环境下的Dos客户端,提供给开发者以命令行的方式跟Redis服务进行交互
- redis-check-aof.exe是Redis内置的用于数据持久化备份的工具;
- redis-benchmark.exe文件是Redis内置的用于性能测试的工具;
- redis.windows.conf则是Redis在Windows下的核心配置文件,主要用于Redis的IP绑定、数据持久化备份、连接数及相关操作超时等配置
特点:
- String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。
一个Redis中字符串value最多可以是512M
常用命令:
- set
:添加键值对 get
:查询对应键值 append
:将给定的 追加到原值的末尾 strlen
获得值的长度 setnx
只有在 key 不存在时 设置 key 的值 incr
将 key 中储存的数字值增1
只能对数字值操作,如果为空,新增值为1
decr
将 key 中储存的数字值减1
只能对数字值操作,如果为空,新增值为-1
incrby / decrby
<步长>将 key 中储存的数字值增减。自定义步长。 mset
..... :同时设置一个或多个 key-value对 mget
.....:同时获取一个或多个 value msetnx
..... :同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 原子性,有一个失败则都失败
getrange
<起始位置><结束位置>:获得值的范围,类似java中的substring,前包,后包 setrange
<起始位置> :用 覆写 所储存的字符串值,从<起始位置>开始(索引从0开始)。 setex
<过期时间 >: 设置键值的同时,设置过期时间,单位秒。getset
:以新换旧,设置了新值同时获得旧值。
LPUSH, LPOP, RPUSH, RPOP,LRANGE
特点:
- redis中的lists在底层实现上并不是数组,而是双向链表(对于大量数据,插入和删除都方便,但定位较慢)
常用命令:
lpush/rpush
.... :从左边/右边插入一个或多个值。 lpop/rpop
:从左边/右边吐出一个值。值在键在,值光键亡。 rpoplpush
从 :列表右边吐出一个值,插到 列表左边。 lrange
:按照索引下标获得元素(从左到右) lrange mylist 0 -1: 0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex
:按照索引下标获得元素(从左到右) llen
:获得列表长度 linsert
before :在 的后面插入 插入值 lrem
:从左边删除n个value(从左到右) lset
:将列表key下标为index的值替换成value
HSET, HGET
特点:
Redis hash 是一个键值对集合。
- hash是一个string类型的field和value的映射表,特别适合用于存储对象。
常用命令:
- hset
:给 集合中的 键赋值 hget
:从 集合 取出 value hmset
... :批量设置hash的值 hexists
:查看哈希表 key 中,给定域 field 是否存在。 hkeys
:列出该hash集合的所有field hvals
:列出该hash集合的所有value hincrby
:为哈希表 key 中的域 field 的值加上增量 1 -1 hsetnx
:将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在 .
SADD, SPOP
特点:
Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值。
set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口
常用命令:
- sadd
.....:将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略 smembers
:取出该集合的所有值。 sismember
:判断集合 是否为含有该 值,有1,没有0 scard
:返回该集合的元素个数。 srem
....: 删除集合中的某个元素。 spop
:随机从该集合中吐出一个值。 srandmember
:随机从该集合中取出n个值。不会从集合中删除 。 smove value:把集合中一个值从一个集合移动到另一个集合
sinter
:返回两个集合的交集元素。 sunion
:返回两个集合的并集元素。 sdiff
:返回两个集合的差集元素(key1中的,不包含key2中的)
ZADD, ZSCORE, ZRANGE
特点:
是一个没有重复元素的字符串集合。
有序集合中的每个元素都关联一个序号(score),这便是排序的依据
集合的成员是唯一的,但是评分可以是重复的
常用命令:
- zadd
…:将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 zrange
:返回有序集 key 中,下标在 [WITHSCORES] 之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。 zrangebyscore key minmax [withscores] [limit offset count]:返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
zrevrangebyscore key maxmin [withscores] [limit offset count] :同上,改为从大到小排列。
zincrby
:为元素的score加上增量 zrem
:删除该集合下,指定值的元素 zcount
:统计该集合,分数区间内的元素个数 zrank
:返回该值在集合中的排名,从0开始。
HyperLogLog:
- 通常用于基数统计。
在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
- 基数就是元素集中出现过至少一次的数
- HyperLogLog适用于一些对于统计结果精确度要求不是特别高的场景,例如网站的UV统计。
pfmerge
[sourcekey ...] 将一个或多个HLL合并后的结果存储在另一个HLL中
常用命令:
- pfadd
< element> [element ...]: 添加指定元素到 HyperLogLog 中 pfcount
[key ...] :计算HLL的近似基数,可以计算多个HLL
四、Geospatial
- redis 3.2 版本的新特性。可以将用户给定的地理位置信息储存起来, 并对这些信息进行操作(经纬度设置,查询,范围查询,距离查询,经纬度Hash等)。
常用命令:
- geoadd
< longitude> [longitude latitude member...] 添加地理位置(经度,纬度,名称) geopos
[member...] 获得指定地区的坐标值 geodist
[m|km|ft|mi ] 获取两个位置之间的直线距离(米|千米|英尺|英里) georadius
< longitude> radius m|km|ft|mi 以给定的经纬度为中心,找出某一半径内的元素
五、Bitmap:位图
- Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。
- 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量。
- bitmap适合处理大量数据的高访问负载(节省内存空间),set适合处理小量一点的。
常用命令:
- setbit
:设置Bitmaps中某个偏移量的值(0或1) getbit
:获取键的第offset位的值(从0开始算) bitcount
[start end]: 统计字符串从start字节到end字节比特值为1的数量 bitop and(or/not/xor) <destkey> [key…]:做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中。
六、Stream:
- 主要用于消息队列,类似于 kafka,可以认为是 pub/sub 的改进版。提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
方式一:RDB(Redis DataBase)
特点:
- 在指定的时间间隔内将redis存储的数据生成快照并存储到磁盘等介质上
- 可能会导致部分数据丢失
- 触发方式:手动触发;自动触发
- 适合大规模的数据恢复
- 恢复速度快
命令:
- save :save时只管保存,其它不管,全部阻塞。手动保存。不建议。
bgsave:Redis会在后台异步进行快照操作, 快照同时还可以响应客户端请求。
方式二:AOF(Append Only File)
特点:
- 将redis执行过的所有写指令记录下来,重启时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了
- 注重数据的完整性
- 文件体积大,恢复慢
AOF持久化流程:
(1)客户端的请求写命令会被append追加到AOF缓冲区内;
(2)AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
(3)AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
(4)Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;
- 官方推荐两个都启用。
- 如果对数据不敏感,可以选单独用RDB。
- 不建议单独用 AOF,因为可能会出现Bug。
- 如果只是做纯内存缓存,可以都不用。
特点:
是一个单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
- 不保证原子性
- 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
作用:
- 串联多个命令防止别的命令插队
事务的错误处理:
- 组队时出错:组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
执行时出错:如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
事务常用操作:
- 从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。
组队的过程中可以通过discard来放弃组队。
- WATCH key [key ...]
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
unwatch
取消 WATCH 命令对所有 key 的监视。
如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
悲观锁:
- 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等。
- 都是在做操作之前先上锁。
乐观锁:
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
特点:
Lua并没有提供强大的库,一个完整的Lua解释器不过200k,所以Lua不适合作为开发独立应用程序的语言,而是作为嵌入式脚本语言。
将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数。提升性能。
具有原子性,不会被其他命令插队
lua脚本可以解决争抢问题,本质上是redis 利用其单线程的特性,用任务队列的方式解决多任务并发问题。
redis主从复制
是什么?
- 主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
作用?
- 读写分离,性能扩展
- 容灾快速恢复
复制原理
- Slave启动成功连接到master后会发送一个sync命令
- Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
- 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
- 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
- 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
是什么?
- 反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库
是什么?
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
优点:
实现扩容
分摊压力
无中心配置相对简单
缺点:
- 多键操作是不被支持的
多键的Redis事务是不被支持的。
lua脚本不被支持
集群的Jedis开发:
即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据。
缓存击穿
问题:某个key的瞬间访问量过大
解决:
- 预先设置热门数据
- 实时调整
- 使用锁
缓存穿透
问题:找一个不存在数据库里的数据
解决:
- 对空值进行缓存
- 设置可访问的白名单
- 采用布隆过滤器
- 进行实时监控
缓存雪崩
问题:多个key的瞬间访问量过大
解决:
- 构建多级缓存架构
- 使用锁或队列
- 设置过期标志更新缓存
- 将缓存失效时间分散开