Redis的基本语法和常见问题归纳

前言:在写这篇博客之前我犹豫了很久,一方面是写出来能不能对自己和大家有帮助,另一方面是redis的篇幅确实是太长了。从我之前面试来看,面试官更加注重你是否经历过redis面临的各个场景,而不是夸夸其谈,大述涵义,所以,实践重于理论,尤其在计算机行业,祝大家以梦为马,不负韶华。

Redis通用命令

keys [parttern]

匹配所有符合parttern的key

dbsize

计算数据库大小(key的总数)

exists key

当前key是否存在

del key...

删除一个/n个key

expire key second

设置key过期时间

ttl key

查看key剩余过期时间

persist key

去除key的过期时间

type key

查看key的类型

学习redis总要了解redis的命令,redis的博客我看了不少,写命令的没几个,基本命令都不知道怎么去学他的功能。

Redis-String

value作为redis的五大基础类型之一,用到的是最广泛的,value类型符合redis中key-value的设计理念,其中可以存放String字符串、数字、double、二进制数据,最大能存放512MB的东西。

String自增——计数器实现的基础

incr key

key自增1,如果key不存在,自增后get(key)=1

decr key

key自减1,如果key不存在,自减后get(key)=-1

decrby key k

key自减k,如果key不存在,自增后get(key)=-k

incrby key k

key自增k,如果key不存在,自增后get(key)=k

incrfloat key

key自增k,如果key不存在,自增后get(key)=k,这里是浮点数增加

基础指令

get

获取key对应的value

set

设置key-value

del

删除一个key对应的value

setnx key value

设置分布式锁,当key不存在时才可以设置

set key value [nx|xx]

当key存在时设置

mget key1,key2,key3...

批量获取

mset key1,key2,key3...

批量设置

getset key newvalue

为key设置新的值并返回旧的值

append key value

为key之前的值后追加value

strlen key

返回key对应value的字符数

getrange key start end

获取key中下标从index到end的部分字符串

setrange key index value

设置指定下标的值(同上相似)

使用场景

  • 缓存
  • 计数器
  • 分布式锁

Redis-Hash

redis的hash是一个可以存放对象的数据结构,同我们在java中的ash一样,键值不允许重复。

基础指令

hget key field

获取key对应的属性的值

hset key field1 value1,field2 value2...

设置key对应的对象属性以及值

hdel key field 

删除key中的一个属性

hexists key field 

判断该key中的field是否存在

hlen key

获取key属性个数

hmget key field1 field2 field3...

批量获取属性

hmset key field1 value1 field2 value2...

批量设置属性值

hincrby key field k

为key中的某个属性自增k

hgetall key

获取key中的所有属性和对应的值

hvals key

获取key中所有的value

hkeys key

获取key中所有的field

hsetnx key field value

设置分布式锁

hincrbyfloat key field k

为该属性自增浮点数k

写到这里,大家应该看出来了,这尼玛简直跟String没有区别,就是在String的操作命令前面加个h,所以说String重要是有依据的。

使用场景——存储对象

Redis-list

list作为redis中的一个类似双向队列的数据结构,可以方便的实现栈、队列等操作。list是有序的,类似数组可以用下标来访问,list是可以添加重复元素的

基础指令

rpush key value1 value2

从右边插入

lpush key value1 value2

从左边插入

insert key before|after value value newvalue

在当前元素前|后插入新元素

lpop key

从左边弹出一个元素

rpop key

从右边弹出一个元素

lrem key count value
  • count>0 从左边删除值为value的count个元素
  • count<0 从右边删除值为value的abs(count)个元素
  • count=0 删除所有值为value的值
lrem key start end

保留索引从start到end的一个列表

lrange key start end

查询列表冲start到end的列表
列表如果要从右开始则start为-1

lindex key index

查询下标为index的元素值

llen key 

计算列表长度

lset key index newvalue

设置改下标新的值

阻塞队列

brpop key timeout

timeout是阻塞时间,如果timeout=0则表示不阻塞,从右边弹出

blpop key timeout

timeout是阻塞时间,如果timeout=0则表示不阻塞,从左边弹出

Stack与Queue

使用list双端队列的特点可以实现异步队列与栈

Redis-set

set同Java中的set类似,底层使用hash实现,不允许重复,并且无序,set最大的特点就是如它的名字所说是一个集合,集合在数学中支持交集、并集、补集、差集,同样这些操作redis也提供。

基本指令

sadd key member

向集合内添加元素

srem key member

删除集合内的元素

scard key

判断集合大小

sismember key member

查看集合中是否有该元素

smembers key

取出集合中所有的缘故

srandmember key

随机拿出一个元素

spop key

随机弹出一个元素

sdiff key1 key2

差集(key1中的减去key1和key2中共有的)

sinter key1 key2

交集

sunion key1 key2

交集

使用场景

set根据其特点,可以使用交集、差集、并集来操作元素,并且根据其spop以及srandmember的随机弹出特点,可以生成一些随机数之类。

Redis-zset

zset也是集合,并且被称为有序集合,它通过插入时附带一个分数来进行集合间的排序,它也是不允许重复的。

基本指令

zadd key score member

向有序集合中添加带分数的元素

zrem key member

删除一个值

zscore key member

获取该元素的分数

zincrby key score value

为value增加一个指定的分数

zcard key

返回元素的个数

zrank key value

获取当前值的一个排名

zrange key start end

获取排名从start到end的元素

zrangebyscore key min max

获取分数从min到max的元素

zcount key minscore maxscore

获取分数在min到max的元素个数

zremrangebyrank key start end

删除排名从start到end的元素

zremrangebysocre key min max

删除分数从min到max的元素

zet总结

zset适用于做一些排行榜之类的应用,通过其zincrby key score value来为点击量比较高的某个新闻做一些排行。其相对于set来说,多了一些对分数以及排名的操作,但是整体指令与set是相似的。

Redis-持久化

redis与memcache的区别除了数据结构较前者复杂之外,还在于redis可以持久化,将数据保存在硬盘中,我们都知道,redis基于内存,如果断电,内存中的数据是不会保存的,所以redis支持持久化为我们带来了非常好的体验。

RDB持久化

关于RDB持久化,在我的另篇Redis的文章中有提到,这里再补充一些其他的知识。

  • RDB以二进制方式存储在硬盘中,体积小恢复快
  • RDB以特定方式去触发,如时间策略(config文件中设置)和bgsave触发,所以不可避免的会丢失数据
  • RDB优先级要低于AOF(两者同时开启)
  • RDB是以子进程的方式去执行的,并且执行时会与父进程共享数据区域,所以在进行备份时要选择父进程工作量小的时候执行。

RDB是使用快照的形式来记录当前所有数据信息。

AOF持久化

关于AOF持久化,AOF是将数据以命令的形式写在AOF文件中,并且以增量形式,所以会一直增大,在我另一篇博客也提到过并且谈到了解决方法。

  • AOF文件体积大,恢复慢
  • AOF相对于RDB来说少丢失数据
  • AOF会一直增大,必须使用AOF重写来保持文件的大小

RDB与AOF的选择

  • RDB优先级低
  • RDB体积小
  • RDB回复速度快
  • RDB容易丢失数据
  • RDB是重量级的快照
  • RDB最好关闭或者使用RDB与AOF混合使用

Redis-单线程架构

老生常谈的一个问题,redis为什么这么快?

  • 完全基于内存
  • 数据结构简单,数据操作简单
  • IO多路复用,非阻塞性IO
  • 单线程执行,避免锁的竞争
  • 持久化时会生成子线程,而不是阻塞执行

说了这么多,我觉得重点在于完全基于内存,在内存中读写和在硬盘中读写根本不是一个数量级的,避免了从外存调取分页或者分块到内存执行,并且内存仅次于寄存器的速度也是它快的重要因素。

Redis-其他特性

慢查询

生命周期

redis命令的生命周期包括,发送请求,排队,执行命令,返回响应,其中慢查询属于执行命令阶段。

三个命令

  • slowlog get[n] 获取慢查询队列中的值,n为可选条数
  • slowlog len 查看慢查新队列中有多少个慢查询语句
  • slowlog reset 清空慢查询队列

两个配置

  • slowlog-max-len 配置在redis.conf中,表示慢查询队列的长度,只要是慢查询语句就会被记录在其中。默认128
  • slowlog-log-lsower-then 配置在redis.conf中,表示执行超过该值的语句就会被放入慢查询队列,当其等于0时,所有命令都会放入慢查询队列,<0时,所有命令都不是慢查询语句。单位:微秒 默认10000

运维

  • slowlog-max-len:一般设置为1000,不要太小
  • slowlog-log-lsower-then:一般设置为1ms

pipeline-批处理操作

相对于mget、mset等,pipeline不是原子操作,它需要所执行的命令没有前后因果关系。

  • 流水线,一次传输+n次操作
  • 相对于原生批处理,不是原子性
  • 只能使用在一个节点上

订阅

redis的订阅是运行在redis的客户端之间的,角色有发布者、订阅值、频道。其模型是一个共享的消息队列。

publish key

发布订阅,频道名为key,发送消息为publish key msg

subscrib key 订阅之后如果是redis客户端(shell),则只能显示消息

订阅频道

unscribe key

取消订阅
在这里插入图片描述

BitMap和Hyperloglog

BitMap是位图,而Hyperloglog是一种数据结构,使用非常小的空间来存储一些信息,这两个正好符合布隆过滤器的实现原理,后面会讲布隆过滤器如何去应对缓存穿透问题。

GEO

计算地理位置,保存地理信息(经纬度)之后,可以计算距离。

Redis开发运维常见问题

Fork操作

fork操作在redis生成子线程时使用,通常为进行复制操作,比如主从同步复制,持久化、主从切换。fork操作因为copy-on-write会影响主进程执行,所以需要改善fork的执行速度。
info:lateset_fork_usec:查看fork执行的时间
改善fork操作

  • 优先使用物理机或者高效支持fork操作的虚拟化技术
  • 控制redis实例最大可用内存:maxmemory
  • 合理配置linux内存的分配策略 vm:overcommit_memory=1
  • 降低fork频率:例如放宽AOF重写自动触发时机,不必要的全量复制

进程外开销

CPU开销

  • RDB和AOF生成属于CPU密集型
  • 不做CPU绑定,不和CPU密集型的应用一起部署

内存开销

  • fork内存开销,copy-on-write在父进程写入时会加大内存消耗
  • 写入量小时进行重写或者是全量同步

硬盘开销

  • AOF和RDB文件写入
  • 不要和高硬盘服务部署在一起:例如存储和消息队列
  • no-appendfsync-on-rewrite=yes:设置不要在AOF同步时进行增量写入(这里指写入AOF文件中,而不是写入缓存中)
  • 使用SSD
  • 单机多实例持久化文件目录可以考虑分盘

Redis主从复制

主从复制是什么

  • 提供高可用、读写分离、提高负载功能
  • 一个master可以有多个slave,提高读写负载能力
  • 数据流向是单向的,master到slave

复制配置

  • slaveof IP 端口 :配置当前节点,要成为谁的从节点
  • 配置 slave-read-only yes 当前节点只允许读
  • slaveof no one :表示当前节点不想成为任何一个节点的从节点
  • RunID和偏移量 :每一个服务器都有一个RunId,而偏移量可以理解为数据的大小,如果主从偏移量不一致可能导致主从不同步。

全量复制和部分复制

  • 全量复制:和AOF重写差不多,先将快照发送,之后将增量放入缓存之后发送
  • 部分复制:当从服务器发送一个psync命令,想要复制主从节点数据时,此时如果不是第一次同步,则会发送一个偏移量到主服务器,如果该偏移量在主节点缓存数据中,则说明只是错过了一部分数据会进行一个部分复制,如果不在缓存中的话,则说明已经错过了很多数据,需要进行一个全量同步。

故障处理

  • slave故障:从属服务器迁移到其他的服务器
  • master故障:redis sentinel检测到之后,会进行投票之后选举出一个合适的slave将其升级成为master

开发运维常见问题

  • 读写分离
    读流量分摊到从节点
    减少master压力
    可能遇到的问题有:复制数据延迟、读到过期数据、从节点故障
  • 主从配置不一样
    例如maxmemory不一致:丢失数据
    设置数据结构优化参数不一致:内存不一致
  • 规避全量复制
    第一次全量复制不可避免 :设置小主节点(复制的数据少了)、在低峰进行处理
    节点的运行ID不匹配:原因有主节点重启或故障转移(主从切换)
    复制积压缓冲区不足:增大复制缓冲区,以便于偏移量尽可能的落在区间
  • 规避复制风暴
    单主节点复制风暴:主节点重启、多从节点进行复制 :需要更换复制拓扑,让从节点去复制从节点,不要都去复制主节点
    单机器复制风暴:多主节点都放在一个机器上,机器宕机之后大量复制,此时将主节点应该放在多个机器上。

Redis Sentinel

  • 原因
    Redis Sentinel是为了解决主从复制问题,手动转移故障非常麻烦并且不易于实现

  • 自动进行主从切换
    投票、转移策略
    1、多个sentinel发现并且确认master有问题
    2、选举出一个sentinel作为领导
    3、选出一个slave成为新的master的slave
    4、通知其余的slave成为新的master的slave
    5、通知客户端主从变化
    6、等待旧的master复活成为新的master的slave

  • 监控主从节点
    通过监控其余的主从节点来进行主从的切换

  • 特殊的redis
    sentinel是一个特殊的redis,只不过不存储数据,默认端口是26379(redis默认端口是6379)

  • sentinel主要配置
    这里的配置是redis.conf的配置

post xx //填写端口
logfile "xxx.log"//这里填写路径
sentinel monitor mymaster 127.0.0.1 port size //size是当size个节点认为应该切换主从时就切换主从
sentinel down-after-milliseconds mymaster 30000
//这里设置访问时间,超过这个时间没有访问到,说明该节点已经故障了
sentinel parallel-sync mymaster 1
//设置主从复制时,每次复制几个节点,规避复制风暴
sentinel failover-timeout mymaster 180000
//设置复制时间
  • 三个定时任务
    1、每10秒每个sentinel对master进行info查看信息,发现slave节点,确认主从关系
    2、每2秒每个sentinel通过master节点的Chanel进行信息的交换(发布订阅 sub/pub)通过_sentinel_:hello频道进行信息交换;交换对节点的“看法”和自身信息
    3、每1秒每个sentinel对其他的sentinel和redis-server进行ping操作。进行“心跳检测”
  • 主观下线和客观下线
    如果30秒没有访问到redis-server,则sentinel会主观判断其已经下线。所有的sentinel都判定其下线称为客观下线。
  • 领导者选举
    原因:只需要一个sentinel完成故障转移
    选举过程:通过sentinel is-master-down-by-addr命令都希望成为领导者
    1、每个做主观下线的sentinel节点向其他的sentinel节点发送命令,要求将他设置成领导者
    2、收到命令的sentinel如果没有同意其他节点那么就同意,否则拒绝
    3、如果该sentinel节点发现自己的票数已经超过sentinel的半数,则成为领导者
    4、如果有多个领导者就重新选举
  • 节点运维
    上线和下线:机器下线、机器性能不足、节点自身故障

Redis-Cluster

redis作为一个分布式的数据库,区别于关系型数据库的特点就是可以进行分片,通过多个节点的运作进一步的提升访问的效率。

呼唤集群

关于为什么要使用集群服务,这里有三点

  • 超大数据量时,会出现机器无法负载数据的问题
  • 将数据进行打散,分布在不同的机器中(这里跟主从同步的高可用是不同的)
  • 主从同步只是减轻了超大访问量时读的压力

数据分布方式

节点个数分区

在早期的redis中,是使用节点个数进行分区的,用数据总量/节点个数决定每个节点中存放多少数据。
优点:简单、容易操作
缺点:每次增加或者删除节点都需要大量的数据进行迁移,可能出现所有数据都不在原来节点的情况。

一致性哈希分区

在这种方式下,通过一个hash环,每个数据对2^32取余,所得到的结果决定其放置在哪一个节点上,而节点也会将自己的一些特定信息进行hash计算来得出自己在hash环上的位置。
一、数据会放置在顺时针遇到的第一个节点中。
二、相对于节点个数分区,添加和删除节点只会影响临近的节点
三、容易出现hash环倾斜的问题,某个节点上放置数据太多而导致该节点被撑爆。
四、hash环倾斜可通过引入虚拟节点的方式来解决。
五、节点个数分片以及一致性哈希扩容的都建议翻倍扩容来使其数据尽可能少迁移。

虚拟槽分区

相对于上面两种方法,前两者都是客户端分片的方式,也就是要在客户端进行hash值的计算,得出具体的节点地址之后再去访问,而虚拟槽分区是一种在服务器分片的方式。
redis一共分为16384个槽,每个节点平分这些槽,并且每个节点都知道其他节点的槽存储情况。
客户端可以访问任意一个节点,该节点通过hash方法计算出该key对应的槽之后,如果正好是自己所拥有的,就返回数据,否则的话就返回存放该数据的节点地址。
对于增加节点来说,每个节点会将自己的一部分槽和数据分配给新来的节点,所以只涉及部分槽和数据的迁移。而对于减少节点来说,就是将当前节点的槽和数据在平分给其他的节点。
对于redisCluster来说,也有主从切换实现故障转移,并且是通过自身来切换的,而不是通过哨兵来切换。

集群架构

基本架构

分布式下,每个节点都知道其他节点的情况,通过流言协议最终达成一致。每个节点都负责读和写,而不是读写分离。

RedisCluster结构

  • 集群配置:需要在redis-server的配置文件中开启cluster-enable:yes,并且配置其特定的redis.conf。
  • 集群交流:节点之间完成交流的基础,每一个节点通过meet操作来向其他节点交流信息最终完成一个同步的结果。
  • 指派槽:即虚拟槽,每个节点会被分配一定数量的指派槽。
  • 复制:每一个主节点都会有一个从节点来准备故障转移,从节点不可读或者写。

特点

  • 复制
  • 高可用
  • 分片

集群伸缩

集群最大的特点就是可以进行伸缩,以此来解决数据量变化的问题,集群伸缩在一定程度上解决了早期不断提升硬件要求的问题。

伸缩原理

数据和槽在节点之间进行转移

扩容集群

准备新节点
加入集群
1、使用cluster meet ip port来将某个节点加入集群中(注意此时该节点是不可用状态)
2、迁移槽和数据

  • 使用redis官方工具(redistrip.rb)将某个节点加入,加入后会自动meet所有节点。
  • 通知迁入数据的节点准备接受数据
  • 通知迁出数据的节点准备发送数据
  • 每次获取一定数量的迁出数据
  • 循环的将数据发送到迁入节点
  • 通知迁出数据的其他节点将槽分配给该迁入节点

集群缩容

  • 下线槽迁移(和扩容迁移数据相似)
  • 忘记节点(对每个节点要执行对下线节点的忘记操作,60秒内要全部执行完毕)
  • 关闭下线节点
  • 先下线从节点,再下线主节点

客户端路由

如何让客户端去访问对应数据的节点,需要由客户端来识别

moved重定向

客户端向随机一个节点发送一条请求,该key会对16383取余来显示当前槽位置,如果命中的话直接返回数据,如果不命中的话会返回一条moved异常并且携带着该请求所放置key的节点IP

ask重定向

如果当前节点正在进行数据迁移的话(缩容/扩容),此时给redis发送一个请求,如果在源节点就会直接返回,不在的话就会返回一个ask异常并且返回一个目标节点。

smart客户端

使用JedisCluster客户端,该客户端会缓存slot和node的对应关系,当有请求访问时会直接向对应节点请求。如果访问出错的话,则会给出一个随机的节点供其访问并且记录此时的对应关系。在此过程中可能会发生moved异常,如果5次没有中,就会返回一个too many rediscluster redirection异常

故障转移

故障发现

主观下线
客观下线

故障解决

  • 剩余主节点进行该下线节点的从节点投票
  • 从节点上线进行故障节点的槽分配与数据同步
  • 故障节点下线

开发运维常见问题

集群完整性

集群完整性是不是需要整合集群的全部的槽都能工作才认为该集群可用,一般设置为no,默认是yes。常见于一部分槽提供服务,这个设置只是针对于对key的操作

带宽消耗

  • 避免 大 集群
  • 尽量多机器
  • 注意超时时间设置

Pub/Sub广播

缺点:一个节点广播,会散播到所有节点。建议:单独使用Redis Sentinel

倾斜问题

数据倾斜
  • 节点和槽分配不均匀
  • 不同槽对应的键值数量不同(可能存在hash_tag)
  • 包含bigkey
  • 内存相关配置不一致
请求倾斜
  • 热点key
    1、避免使用big key
    2、热点key不要用hash tag
    3、一致性不高时,可以用本地缓存和Mq
  • big key

数据分离

集群模式下,从节点不接受任何读写操作

数据迁移

离线/在线迁移 redistrip.rb

集群VS单机

  • 满足容量和性能的扩展性,很多业务是不需要的
  • 很多场景redis sentinel已经足够

安装方式

官方工具安装

缓存的设计与使用

受益

读写更加快速

成本

  • 缓存层和数据层时间窗口有时不一致
  • 代码维护成本加深
  • 运维成本增加:例如Redis Cluster

使用场景

  • 降低后端负载。对一些查询繁琐的数据/分组统计等数据
  • 加速响应请求
  • 大量合并写为批量写。如计数器

缓存的更新策略

缓存的粒度问题

  • 通用型 全量属性更好 存储整个对象的全部字段
  • 占用空间 部分属性更好 存储一部分字段
  • 代码维护 表面上全量属性更好 根据具体使用场景/业务考虑

缓存的穿透问题

原因

  • 代码业务自身问题
  • 恶意攻击/爬虫等。乱传ID

涵义

  • 大量请求不命中缓存层。缓存层中没有数据,流量就会打到存储层

如何发现

  • 业务的响应时间
  • 业务本身问题
  • 相关指数:总调用数、缓存层命中数、存储层命中数

解决方法

  • 缓存空对象,如果打到存储层之后发现还是不存在,则将空对象直接存放在缓存中。需要更多的键值,因为像什么爬虫什么的,会随机碰一些key,由于网络原因,存储层中有,但是缓存中由于网络波动而没有访问到因而将其置为空。为这个空置设置一个过期时间
  • 布隆过滤器拦截。要求数据固定一些

缓存雪崩优化

涵义

由于缓存服务承载了大量的请求,当cache服务异常/脱机,流量直接打向储存层,造成级联故障

优化思路

  • 提前演练:例如压力测试
  • 依赖隔离组件为后端限流
  • 保证缓存的高可用性,使用Redis Cluster,Redis Sentinel使缓存能进行故障转移。

无底洞问题

  • 原因:mget对多个节点操作不友好
  • 需要增加多个节点
  • 减少网络通信次数
  • 命令本身优化
  • 降低介入成本:使用长连接/连接池、NIO等

热点key重建优化

当某个热点key不断的被访问(此时热点key过期了),需要从数据库中获取,许多请求不断的去查看数据源并且重建key

  • 设置互斥锁 等待降低性能
  • 设置永不过期 设置逻辑过期时间,但是会出现数据不同步的问题

布隆过滤器

  1. 如何在一个大数据集合中过滤一些小数据集,判断他们是否存在等(要求空间小并且错误率较低)
  2. 实现原理:一个很长的二进制向量和若干个哈希函数。将一个key通过这些哈希函数映射到二进制向量上最终得到的二进制数就是该值在布隆过滤器上的映射
  3. 判断元素是否存在:对该key执行hash(多个hash函数),如果得到对应的位置都是1,则它存在
  4. 误差率。m/n比例与hash函数的个数会影响误差。
  5. 本地布隆过滤器。Guava容量受限制。多个应用存在多个布隆过滤器,构建同步复杂。
  6. Redis布隆过滤器。通过redis的bit map(位图)实现。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值