您的位置:首页 > 数据库 > Redis

redis专题

2020-03-05 20:23 330 查看

Redis是什么

Redis是C语言开发,一个单线程、高性能(key-value)内存数据库,基于内存运行并支持持久化的数据库。

做什么

Redis不单单能做缓存,还可以做分布式锁 ,队列,会话缓存等。

安装

windows下解压就可以用单机的。
linux解压 检查下有没有gcc ,先安装下yum install gcc -c++,gcc安装好后执行make,

端口

#Accept connections on the specified port, default is 6379 (IANA #815344).
#If port 0 is specified Redis will not listen on a TCP socket.
#指定Redis的端口
port 6379

# 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password>命令提供密码,默认关闭
# requirepass foobared

redis设置外网访问

1.注释bind并且把protected-mode no
2.使用bind
3.设置密码
protected-mode它启用的条件有两个,第一是没有使用bind,第二是没有设置访问密码。

Redis数据类型

key

keys * 获取所有key
exists key 判断某个key是否存在
move key db 当前库就没有了,到指定的库中去了
expire key 为给定的key设置过期时间
ttl key 查看还有多少时间过期 -1表示永不过期 -2表示已过期
type key 查看key是什么类型

String

String是redis的最基本的类型,一个key对应一个value
String是二进制安全的,redis的String可以包含很多数据.
String一个redis中文字符串value最多可以是512M
set key value 设置key value
get key 查看当前key的值
del key 删除key
append key value 如果key存在,则在指定的key末尾添加,如果key存在则类似set
** getrange key 0(开始位置) -1(结束位置** 获取指定区间范围内的值,类似between…and的关系 (0 -1)表示全部 类似字符串截取

以下几个命令只有在key值为数字的时候才能正常操作
incr key 为执定key的值加一
decr key 为指定key的值减一
incrby key 数值 为指定key的值增加数值
decrby key 数值 为指定key的值减数值
strlen key 返回此key的长度
setex 键 秒值 真实值 设置带过期时间的key,动态设置。
setnx key value 只有在 key 不存在时设置 key 的值。(其实分布式锁可以用这个)
mset key1 value key2 value 同时设置一个或多个 key-value 对。
mget key1 key2 获取所有(一个或多个)给定 key 的值。
msetnx key1 value key2 value 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
getset key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。

list

它是一个字符串链表,left、right都可以插入添加;
如果键不存在,创建新的链表;
如果键已存在,新增内容;
如果值全移除,对应的键也就消失了。
链表的操作无论是头和尾效率都极高,但假如是对中间元素进行操作,效率就很惨淡了。
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。
它的底层实际是个链表
lpush key value1 value2 将一个或多个值加入到列表头部
rpush key value1 value2 将一个或多个值加入到列表底部
lrange key start end 获取列表指定范围的元素 (0 -1)表示全部
lpop key 移出并获取列表第一个元素
rpop key 移出并获取列表最后一个元素
lindex key index 通过索引获取列表中的元素
llen 获取列表长度
lrem key 0(数量) 值,表示删除全部给定的值。零个就是全部值 从left往right删除指定数量个值等于指定值的元素,返回的值为实际删除的数量
ltrim key start(从哪里开始截) end(结束位置) 截取指定索引区间的元素,格式是ltrim list的key 起始索引 结束索引

set

Redis的Set是string类型的无序,不能重复的集合。
sadd key value1 value 2 向集合中添加一个或多个成员
smembers key 返回集合中所有成员
sismembers key member 判断member元素是否是集合key的成员
scard key 获取集合里面的元素个数
srem key value 删除集合中指定元素
srandmember key 数值 从set集合里面随机取出指定数值个元素 如果超过最大数量就全部取出,
spop key 随机移出并返回集合中某个元素
smove key1 key2 value(key1中某个值) 作用是将key1中执定的值移除 加入到key2集合中
sdiff key1 key2 在第一个set里面而不在后面任何一个set里面的项(差集)
sinter key1 key2 在第一个set和第二个set中都有的 (交集)
sunion key1 key2 两个集合所有元素(并集)

hash

Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
kv模式不变,但v是一个键值对
类似Java里面的Map<String,Object>
hset key (key value) 向hash表中添加一个元素
hget key key 向hash表中获取一个元素
hmset key key1 value1 key2 value2 key3 value3 向集合中添加一个或多个元素
hmget key key1 key2 key3 向集合中获取一个或多个元素
hgetall key 获取在hash列表中指定key的所有字段和值
hdel key key1 key2 删除一个或多个hash字段
hlen key 获取hash表中字段数量
hexits key key 查看hash表中,指定key(字段)是否存在
hkeys key 获取指定hash表中所有key(字段)
hvals key 获取指定hash表中所有value(值)
hincrdy key key1 数量(整数) 执定hash表中某个字段加 数量 ,和incr一个意思
hincrdyfloat key key1 数量(浮点数,小数) 执定hash表中某个字段加 数量 ,和incr一个意思
hsetnx key key1 value1 与hset作用一样,区别是不存在赋值,存在了无效。

zset

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。
redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
zadd key score 值 score 值 向集合中添加一个或多个成员
**zrange key 0 -1 ** 表示所有 返回指定集合中所有value
zrange key 0 -1 withscores 返回指定集合中所有value和score
zrangebyscore key 开始score 结束score 返回指定score间的值
zrem key score某个对应值(value),可以是多个值 删除元素
zcard key 获取集合中元素个数
zcount key 开始score 结束score 获取分数区间内元素个数
zrank key vlaue 获取value在zset中的下标位置(根据score排序)
zscore key value 按照值获得对应的分数

持久化

Reids持久化,reids为了保证效率,数据缓存在内存中,但是会周期性的把更新的数据写入擦盘或者把修改的操作追加到记录文件中,保证数据持久化。
Redis持久化有2种 redis默认是RDB持久化方式。
RDB:以快照的形式直接把内存中的数据保存在dump文件中,定时保存。redis会创建一个与当前进程一模一样的子进程进行持久,这个子进程所有的数据与原进程一模一样,会将数据写在一个临时文件中,待持久化结束了,在用临时文件替换上次持久化文件,整个过程主进程不进行io操作,这就确保了极高的性能。(如果在fock子进程的时候,有客户端发送过来命令,得不到处理,必须等持久化结束了才能接受客户端命令。)
持久化文件# dbfilename文件存放目录。必须是一个目录,aof文件也会保存到该目录下。
dir ./
触发持久化
1.shutdown的时候如果没有开启AOF会触发
2.配置文件中默认的配置
#指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
#这里表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改
#如果想禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以
save 900 1
save 300 10
save 60 1
3.执行命令save或者bgsave
bgsave:会fork子进程,原理与rdb上面原理一样,系统默认触发rdb持久化都是调用的此命令
save:是不会fork子进程的,它使用主进程进行持久化,所以会导致客户端命令发送到我们服务端得不到及时处理,所以他是阻塞的

AOF:把所有的对Redis服务器进行修改的命令存储在一个文件夹中,一个命令的集合。
触发配置文件
开启**# 是否启用aof持久化方式 。否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。
因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no->改为yes

#指定更新日志条件,共有3个可选值:
#no:表示等操作系统进行数据缓存同步到磁盘(快,持久化没保证)
#always:同步持久化,每次发生数据变更时,立即记录到磁盘(慢,安全)
#everysec:表示每秒同步一次(默认值,很快,但可能会丢失一秒以内的数据)
#appendfsync always
appendfsync everysec
#appendfsync no
AOF重写机制:aof是以日志追加的方式将命令字符串协议保存在aof 文件中,随着redis使用时间越来越长,aof文件会越来越大。如果不做处理太占磁盘,所有会出现重写,重写就是给aof文件瘦身。
当AOF文件增长到一定大小的时候Redis能够调用 bgrewriteaof对日志文件进行重写 。当AOF文件大小的增长率大于该配置项时自动开启重写(这里指超过原大小的100%)。
auto-aof-rewrite-percentage 100
当AOF文件增长到一定大小的时候Redis能够调用 bgrewriteaof对日志文件进行重写 。当AOF文件大小大于该配置项时自动开启重写
auto-aof-rewrite-min-size 64mb
注意:重写操作是通过fork子进程来完成的,所以正常的aof不会fork子进程,触发了重写才会
Redis4.0后支持混合开启:4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,5.0之后默认开启。
混合持久化是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,
优点:混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差

redis有RDF为什么还要用AOF:RDB会丢失最后一次快照数据,AOF最多不丢失2秒数据。
同时开启redis会听谁的:AOF
RDB和AOF的各自优势:
rdb适合做大规模数据恢复,对于数据完整性一致性不高,一段时间会持久化一次,如果redis down机了会丢失最后一次数据快照。
aof根据配置而定,如果是默认配置最多丢失不超过2秒数据。

集群

redis cluster集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。
redis cluster集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单
主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,mester已写为主,slaver已读为主。
原则:配从不配主。
第一步:
(1)准备好配置文件
(2)mkdir 7000、mkdir 7001、 mkdir 7002、 mkdir 7003、 mkdir 7004、 mkdir 7005

第二步:修改redis.conf配置文件copy到8001下,修改如下内容:
(1)daemonize yes(是否以守护进程启动)
(2)port 7000(分别对每个机器的端口号进行设置)
(3)bind 0.0.0.0(使用bind)
(4)dir /usr/local/redis-cluster/7000/(指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据)
(5)cluster-enabled yes(启动集群模式)
(6)cluster-config-file nodes-7000.conf(集群信息存在路径)
(7)cluster-node-timeout 5000(是集群中的节点能够失联的最大时间,超过这个时间,该节点就会被认为故障。如果主节点超过这个时间还是不可达,则用它的从节点将启动故障迁移,升级成主节点。)
(8)appendonly yes(AOF持久化开启)
第三步:
其他几个配置文件一样的修改,记得修改端口和信息存在名称方便查看
第四步:
(1)/usr/local/redis/bin/redis-server /usr/local/redis-cluster/700*/redis.conf
(2)ps -ef | grep redis 查看是否启动成功
第五步:创建整个集群
/usr/local/bin/redis-cli --cluster create 10.0.0.1:7000 10.0.0.1:7001 10.0.0.1:7002 10.0.0.1:7003 10.0.0.1:7004 10.0.0.1:7005 --cluster-replicas 1
cluster-replicas 1表示主从关系是1:1,配置出来是3主3从,槽位是平均分配。
第六步:验证
(1)连接任意一个客户端即可:./redis-cli -c -h -p (-c表示集群模式,指定ip地址和端口号)
(2)进行验证: cluster info(查看集群信息)、cluster nodes(查看节点列表)
(3)进行数据操作验证
(4)关闭集群则需要逐个进行关闭,使用命令:
/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 800* shutdown
集群的伸缩都可以迁移槽和数据。
客户端
1.moved重定向:指我们发送命令时,会对发送的key进行crc16算法,得到一个数字,然而我们连接的客户端并不是管理这个数字的范围,所以会返回错误并告诉你此key应该对应的槽位,然后客户端需要捕获此异常,重新发起请求到对应的槽位
2.asx重定向:指在我们送发命令时,对应的客户端正在迁移槽位中,所以此时我们不能确定这个key是还在旧的节点中还是新的节点中
3.smart客户端
1.从集群中选取一个可运行节点,使用cluster slots初始化槽和节点映射。
2.将cluster slots的结果映射到本地,为每个节点创建jedispool
3.准备执行命令
故障转移
1.故障发现: 通过ping/pong消息实现故障发现(不依赖sentinel)
2.故障恢复
1.检查资格
1.每个从节点检查与主节点的断开时间
超过cluster-node-timeout * cluster-replica-validity-factor 时间取消资格
2.选择偏移量最大的
替换主节点
1.当前从节点取消复制变为主节点(slaveof no one)
2.撤销以前主节点的槽位,给新的主节点
3.向集群广播消息,表明已经替换了故障节点

reids的哨兵

哨兵模式的出现是就是为了解决我们主从复制模式中需要我们人为操作的东西变为自动版,并且它比人为要更及时。
监控:哨兵会不断地检查主节点和从节点是否运作正常。
自动故障转移:当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
配置提供者:客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
通知:哨兵可以将故障转移的结果发送给客户端。
其中,监控和自动故障转移功能,使得哨兵可以及时发现主节点故障并完成转移;而配置提供者和通知功能,则需要在与客户端的交互中才能体现。
哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的Redis节点,不存储数据。
数据节点:主节点和从节点都是数据节点。
哨兵节点
哨兵节点本质上是特殊的Redis节点。
3个哨兵节点的配置几乎是完全一样的,主要区别在于端口号的不同(26379 / 26380 / 26381)下面以26379节点为例介绍节点的配置和启动方式;配置部分尽量简化:
#####sentinel-26379.conf
port 26379
daemonize yes
logfile “26379.log”
sentinel monitor mymaster 10.0.0.1 6379 2
其中,sentinel monitor mymaster 10.0.0.1 6379 2配置的含义是:该哨兵节点监10.0.0.1 6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移。
哨兵节点的启动有两种方式,二者作用是完全相同的:
redis-sentinel sentinel-26379.conf
redis-server sentinel-26379.conf --sentinel
基本原理
关于哨兵的原理,关键是了解以下几个概念:
主观下线:在心跳检测的定时任务中,如果其他节点超过一定时间没有回复,哨兵节点就会将其进行主观下线。顾名思义,主观下线的意思是一个哨兵节点“主观地”判断下线;与主观下线相对应的是客观下线。
客观下线:哨兵节点在对主节点进行主观下线后,会通过sentinel is-master-down-by-addr命令询问其他哨兵节点该主节点的状态;如果判断主节点下线的哨兵数量达到一定数值,则对该主节点进行客观下线。
需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。
定时任务:每个哨兵节点维护了3个定时任务。定时任务的功能分别如下:
1.每10秒通过向主从节点发送info命令获取最新的主从结构;
发现slave节点
确定主从关系
2.每2秒通过发布订阅功能获取其他哨兵节点的信息;SUBSCRIBE c2 PUBLISH c2 hello-redis
交互对节点的“看法”和自身情况
3.每1秒通过向其他节点发送ping命令进行心跳检测,判断是否下线(monitor)。
心跳检测,失败判断依据
选举领导者哨兵节点:当主节点被判断客观下线以后,各个哨兵节点会进行协商,选举出一个领导者哨兵节点,并由该领导者节点对其进行故障转移操作。
监视该主节点的所有哨兵都有可能被选为领导者,选举使用的算法是Raft算法;Raft算法的基本思路是先到先得:即在一轮选举中,哨兵A向B发送成为领导者的申请,如果B没有同意过其他哨兵,则会同意A成为领导者。选举的具体过程这里不做详细描述,一般来说,哨兵选择的过程很快,谁先完成客观下线,一般就能成为领导者。
故障转移:选举出的领导者哨兵,开始进行故障转移操作,该操作大体可以分为3个步骤:
在从节点中选择新的主节点:选择的原则是,
1.首先过滤掉不健康的从节点;
2.过滤响应慢的节点
3.过滤与master断开时间最久的
4.优先原则
先选择优先级最高的从节点(由replica-priority指定);如果优先级无法区分,
则选择复制偏移量最大的从节点;如果仍无法区分,
则选择runid最小的从节点。
更新主从状态:通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点。
将已经下线的主节点(即6379)保持关注,当6379从新上线后设置为新的主节点的从节点
实践建议
哨兵节点的数量应不止一个。一方面增加哨兵节点的冗余,避免哨兵本身成为高可用的瓶颈;另一方面减少对下线的误判。此外,这些不同的哨兵节点应部署在不同的物理机上。
哨兵节点的数量应该是奇数,便于哨兵通过投票做出“决策”:领导者选举的决策、客观下线的决策等。
各个哨兵节点的配置应一致,包括硬件、参数等;此外应保证时间准确、一致。

在主从复制的基础上,哨兵引入了主节点的自动故障转移,进一步提高了Redis的高可用性;但是哨兵的缺陷同样很明显:哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,需要我们对从节点做额外的监控、切换操作。此外,哨兵仍然没有解决写操作无法负载均衡、及存储能力受到单机限制的问

缓存问题

缓存雪崩
是指机器宕机或在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:如果缓存数据库是分布式部署,将热点数据均匀分布在不同得缓存数据库中。
缓存穿透
是指查询一个一定不存在的数据,由于缓存不命中,并且出于容错考虑, 如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
1.缓存空对象
2.布隆过滤器
这个也能很好的预防缓存穿透的发生,他的原理也很简单,就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好了,存在你就去查DB刷新KV再return。

缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
互斥锁
第一次获取缓存的时候,加一个锁,然后查询数据库,接着是重建缓存。这个时候,另外一个请求又过来获取缓存,发现有个锁,这个时候就去等待,之后都是一次等待的过程,直到重建完成以后,锁解除后再次获取缓存命中。

解决
public String getKey(String key){
String value = redis.get(key);
if(value == null){
String mutexKey = "mutex:key:"+key; //设置互斥锁的key
if(redis.set(mutexKey,"1","ex 180","nx")){ //给这个key上一把锁,ex表示只有一个线程能执行,过期时间为180秒
value = db.get(key);
redis.set(key,value);
redis.delete(mutexKety);
}else{
// 其他的线程休息100毫秒后重试
Thread.sleep(100);
getKey(key);
}
}
return value;
}

。。。待续
以上均为鲁班学院学习资料,欢迎大家报班学习,真心推荐!

  • 点赞
  • 收藏
  • 分享
  • 文章举报
qigemingzhendenan 发布了5 篇原创文章 · 获赞 1 · 访问量 203 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: