redis的简单与集群搭建
wptr33 2024-12-31 15:02 25 浏览
Redis是什么?
是开源免费用c语言编写的单线程高性能的(key-value形式)内存数据库,基于内存运行并支持持久化的nosql数据库
作用
主要用来做缓存,单不仅仅是做缓存,比如:redis的计数器生成分布式的唯一主键,redis实现分布式锁、队列、会话缓存等等。
redis数据类型及api操作(http://redisdoc.com/)
key
keys * 查询所有的键
scan 0 match * count 1 查询所有键从下角标0开始,返回1个键数据的结果集
exists key 判断某个key是否存在
move key db 当前库就没有了,到指定的库中去了
expire key 为给定的key设置过期时间
ttl key 查看还有多少时间过期 -1表示永不过期 -2表示已过期
type key 查看key是什么类型
1.string
一般使用场景:单值缓存、对象缓存、分布式锁、计数器、websession共享、分布式全局序列号
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
set key value 设置key value
get key 查看当前key的值
del key 删除key
append key value 如果key存在,则在指定的key末尾添加,如果key存在则类似set
strlen key 返回此key的长度
以下几个命令只有在key值为数字的时候才能正常操作
incr key 为执定key的值加一
decr key 为指定key的值减一
incrby key 数值 为指定key的值增加数值
decrby key 数值 为指定key的值减数值
getrange key 0(开始位置) -1(结束位置) 获取指定区间范围内的值,类似between......and的关系 (0 -1)表示全部
setrange key 1(开始位置,从哪里开始设置) 具体值 设置(替换)指定区间范围内的值
setex 键 秒值 真实值 设置带过期时间的key,动态设置。
setnx key value 只有在 key 不存在时设置 key 的值。
mset key1 value key2 value 同时设置一个或多个 key-value 对。
mget key1 key 2 获取所有(一个或多个)给定 key 的值。
msetnx key1 value key2 value 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
getset key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
2.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 起始索引 结束索引
3.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 两个集合所有元素(并集)
4.hash
一般使用场景:对象缓存、电商购物车、
Redis hash 是一个键值对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
kv模式不变,但v是一个键值对
类似Java里面的Map
优点:同类数据归类整合存储,方便数据管理;相对于string消耗的内存与cpu更小;相比于string更省存储空间
缺点:过期功能不能用于field(里层)只能作用于key(外层)上;redis集群架构下不适合大规模使用
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作用一样,区别是不存在赋值,存在了无效。
5.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 按照值获得对应的分数
conf文件的注意点
1.bind可以使用0.0.0.0或者机器的IP地址
2.protected-mode在使用bind或者设置了密码时是无效的
3.protected-mode默认是开启的,不能被外网访问的
4.当redis以守护进程方式运行时,redis默认会把pid写入redis.pid文件,可以使用pidfile指定(只适用于linux版本,window不适用)
5.logfile指定日志文件的位置
6.databases是数据库的数目,默认是16
redis的单线程和多性能
redis是单线程吗:redis的单线程只要是指redis的网络io和键值对读写是由一个线程来完成的,这也是redis对外提供键值存储服务的主要流程。但是redis的其他功能,如持久化、异步删除、数据同步等都是由额外的线程执行的。
redis单线程为什么还能这么快:因为它所有的数据都在内存中,所有的运算都是内存级别的运算,并且单线程避免了线程切换的损耗问题。正是由于redis是单线程所以要小心使用redis指令,对于那些耗时的指令(如keys),需要谨慎使用,不小心会导致redis卡顿
redis单线程如何处理多个并发客户端的连接:redis的io多路复用,redis利用epoll来实现io多路复用,将连接信息和事件放到队列总,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
持久化RDB(丢失数据较多)
- 原理:redis会单独创建(fork)一个与当前线程一样的子线程来进行持久化,这子线程的所有数据都和原线程一模一样,会先将数据希尔到一个临时的文件中,等到持久化结束了再用这个临时文件替换上次持久化好的文件,整个过程中,主线程不进行任何的io操作,这就确保了极高的性能。
- 持久化文件存放的位置:由dbfilename指定
- 什么时候fork子进程,或者什么时候触发rdb持久化机制:
shutdow时,没有开启aof会触发
配置文件中默认的快照配置
执行命令save或者bgsave,save是主线程操作会阻塞,save只管保存,其余不管,bgsave是新fork线程后台操作
执行flushall命令(清空所有数据),无意义
- 怎么开启rdb持久化
配置文件中配置save命令:save 900 1;表示900秒由一条数据更新会触发rdb的持久化机制
可以写多个save条件
当配置主从复制集群时,是关闭不了rdb的
持久化aof(丢失数据较少)
- 原理:将redis的操作日志以追加的方式写入文件,读操作是不记录的
- 怎么开启aof:配置文件中appendonly 设置为yes;命令行:config set appendonly yes,然后还是需要修改配置文件
- 持久化文件在哪里:配置文件中 appendfilename指定
- 触发机制(配置文件中appendfsync指定):no表示等操作系统进行数据缓存同步到磁盘(快,没保障);always同步持久化,每次发送数据变更时,立即记录到磁盘(慢,安全);everysec表示美妙同步一次(默认值,快,可能会丢失一秒以内的数据)
- aof文件中日志解析:*2代表是2组命令;$6,$3代表是这个命令有6个,3个字符组成(命令长度);
- aof的重写机制:当aof增长到一定大小时候redis可以调用bgreweiteaof命令对日志文件进行重写;当文件大小的增长率大于配置项时自动开启重写,是配置文件中auto-aof-rewrite-percentage指定表示超过源文件大小的百分之多少,案例:50;当文件大小大于配置项的大小时自动开启重写,是配置文件中的auto-aof-rewrite-min-size指定,案例:64mb
rdb与aof是可以同时开启时,默认使用aof持久化,所以切换rdb与aof时,最好通过命令行的方式启动aof,这样不会有数据丢失
混合持久化
- 4.0版本的混合持久化是默认关闭的,可以通过配置文件中的aof-use-rdb-preamble来控制,yes代表开启,no表示禁用,5.0之后默认开启
- 混合持久化是通过bgrewriteaof完成的,当开启混合持久化时,fork的子线程先将共享的内存副本全量的以rdb的方式写入aof文件,然后再将重写缓存区的增量命令以aof 的方式写入文件,完成后通知主线程更新统计信息,将新的aof文件替换旧的aof文件
- 优点:加载速度快,同时结合aof 的方式保存,数据丢失少
- 缺点:兼容性差,一旦开启混合持久化,在4.0版本之前都不识别该aof文件,阅读性差。
缓存穿透问题
缓存穿透是指查询一个一定不存在的数据,由于缓存不命中,并且出于容错考虑, 如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
可能造成原因:
1.业务代码自身问题2.恶意攻击。爬虫等等
危害
对底层数据源压力过大,有些底层数据源不具备高并发性。 例如mysql一般来说单台能够扛1000-QPS就已经很不错了
解决方案
1.缓存空对象
缺点:可能会缓存大量空对象,当缓存过期时仍然会有缓存穿透的问题
public class NullValueResultDO implements Serializable{
private static final long serialVersionUID = -6550539547145486005L;
}
public class UserManager {
UserDAO userDAO;
LocalCache localCache;
public UserDO getUser(String userNick) {
Object object = localCache.get(userNick);
if(object != null) {
if(object instanceof NullValueResultDO) {
return null;
}
return (UserDO)object;
} else {
User user = userDAO.getUser(userNick);
if(user != null) {
localCache.put(userNick,user);
} else {
localCache.put(userNick, new NullValueResultDO());
}
return user;
}
}
}
2.布隆过滤器
缺点:添加数据时,布隆过滤器也需要添加数据,但是不能删除数据,所以要定时维护更新布隆过滤里面的数据
1)、Google布隆过滤器的缺点
基于JVM内存的一种布隆过滤器重启即失效本地内存无法用在分布式场景不支持大数据量存储
2)、Redis布隆过滤器
可扩展性Bloom过滤器:一旦Bloom过滤器达到容量,就会在其上创建一个新的过滤器不存在重启即失效或者定时任务维护的成本:基于Google实现的布隆过滤器需要启动之后初始化布隆过滤器缺点:需要网络IO,性能比Google布隆过滤器低
缓存击穿.热点key重建缓存问题
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
我们知道,使用缓存,如果获取不到,才会去数据库里获取。但是如果是热点 key,访问量非常的大,数据库在重建缓存的时候,会出现很多线程同时重建的情况。因为高并发导致的大量热点的 key 在重建还没完成的时候,不断被重建缓存的过程,由于大量线程都去做重建缓存工作,导致服务器拖慢的情况。
解决方案
1.互斥锁
第一次获取缓存的时候,加一个锁,然后查询数据库,接着是重建缓存。这个时候,另外一个请求又过来获取缓存,发现有个锁,这个时候就去等待,之后都是一次等待的过程,直到重建完成以后,锁解除后再次获取缓存命中。
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;
}
互斥锁的优点是思路非常简单,具有一致性,但是互斥锁也有一定的问题,就是大量线程在等待的问题。存在死锁的可能性。
缓存雪崩问题
缓存雪崩是指机器宕机或在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
4:如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
redis的集群
- 主从架构
原理:若为master配置了一个slave,不管这个slave是否是第一次连接上master,它都会发送psync命令给master请求复制数据。master收到命令后,会在后台进行数据持久化通过bgsave上次最新的rdb快照文件,持久化时,master会继续接受客户端请求,他会把可能修改的数据及的请求缓存在内存中。当持久化进行完毕之后,master会把这份rdb文件数据集发送给slave,slave会把接收到的数据进行持久化生成rdb,然后在加载到内存中。然后,master再将之前缓存在内存中的命令发送给slave。
当master与slave之间的连接由于某些原因断开时,slave能过自动连接master,如果master收到了多个salve并发连接请求,他只会进行一次持久化,而不是一个连接一次,然后再问吧这份持久化数据发送给多个并发连接的slave。
解决主从风暴的问题方法:配置阶梯式主从架构
配置从节点实例,复制conf文件:
port 6389
pidfile /var/run/redis_6380.pid #把pid进程号写入pidfile配置文件
logfile "6380.log"
dir /usr/local/redis-5.0.3/data/6389 #指定数据存放目录
#配置主从复制
replicaof 127.0.0.1:6379 #从本机的6379redis实例复制数据,redis5.0之前用slaveof
replica-read-only yes #配置从节点只读
#启动从节点
redis-server redis.conf
#连接从节点
redis-cli -p 6380
#测试在6379实例上写数据,6380实例是否能及时同步数据
同步骤配置下一个从节点
- 哨兵模式的主从架构
原理:哨兵是特殊的redis服务,不提供读写服务,主要来健康redis实例的节点,哨兵架构下的client端第一次从哨兵中找出redis的主节点,后续就直接访问redis的主节点,不是每次都通过哨兵进行代理访问redis的主节点,当redis的主节点变化时,哨兵会第一时间感知到,并且将新的redis节点通知给client端(这里的redis的client端一般都实现了订阅功能,订阅哨兵发布的节点变动消息)
配置搭建:
复制一份sentinel.conf文件
修改相关配置:
port 26379
daemonize yes
pidfile "/var/run/redis-sentinel-26379.pid"
logfile "26379.log"
dir "/usr/local/redis-5.0.3/data"
#sentinel monitor <master-name> <ip> <redis-port> <quorum>
#quorum是数字,指明当前有多少sentinel认为master失效时(一般是 总数/2+1),master才算真正失效
sentinel monitor mymaster 192.168.0.60:6379 2
注释掉bind IP那行代码
protected-mode no 关闭自我保护模式,开启只有本机可以访问
启动哨兵实例
redis-sentinel redis-sentinel-26379.conf
查看sentinel的info信息
redis-cli -p 26379
info
可以看到sentinel的info里已经识别出redis的主从
同步骤可以配置其余的哨兵
- redis高可用集群cluster(最少需要三个主节点)
原理:redis cluster将所有数据划分为16384个槽位,每个节点负责其中一部分槽位,槽位的信息存储在每个节点中。
当集群的客户端连接集群时,他会得到一份集群的槽位配置信息并将其缓存在客户端本地,这样当客户端要查找某个key时,可以直接定位目标节点,同时因为信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。
跳转重定向:当客户端向一个错误的节点发出指令,该节点会发现指令大key所在的槽位并不归自己管理,这时她会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连接这个节点获取数据,客户端收到指令后处理跳转到正确的节点上操作,还会同步更新纠正本地的槽位映射表缓存,后续所有可以将使用新的槽位表映射表。
配置:
daemonize yes 后台启动
protect-mode no 关闭保护模式,开启的话本机才可以访问redis
注释掉bind
redis集群至少需要三个master节点,我们搭建三个master并且给每个master搭建一个slave节点
在第一台机器的/usr/local下创建文件夹redis-cluster,然后在其下面分别创建2个文件夹
mkdir -p /usr/local/redis-cluster
mkdir 8001 8004
把之前的redis.conf配置文件copy到8001下,修改内容:
daemonize yes
port 8001
dir /usr/local/redis-cluster/8001/ (指定数据文件位置,必须要指定不同的目录位置,不然会丢失数据)
cluster-enabled yes 启动集群模式
cluster-config-file nodes-8001.conf集群节点信息文件
cluster-node-timeout 5000
protected-mode no
appendonly yes
如果要设置密码需要如下配置
requirepass mima redis的访问密码
masterauth mima 集群的访问密码,与上面一致
上述步骤配置其他节点,端口号不同
#启动从节点
redis-server redis.conf
构建集群命令(最后的数字是一个主节点对应多少个从节点的意思),只需构建一次,服务器重启不需要重启构建
redis5.0使用/usr/local/bin/redis-cli --cluster create 192.168.0.104:7000 192.168.0.104:7001 192.168.0.104:7002 192.168.0.104:7003 192.168.0.104:7004 192.168.0.104:7005 --cluster-replicas 1
相关推荐
- Linux文件系统操作常用命令(linux文件内容操作命令)
-
在Linux系统中,有一些常用的文件系统操作命令,以下是这些命令的介绍和作用:#切换目录,其中./代表当前目录,../代表上一级目录cd#查看当前目录里的文件和文件夹ls#...
- 别小看tail 命令,它难倒了技术总监
-
我把自己以往的文章汇总成为了Github,欢迎各位大佬star...
- lnav:基于 Linux 的高级控制台日志文件查看器
-
lnav是一款开源的控制台日志文件查看器,专为Linux和Unix-like系统设计。它通过自动检测日志文件的格式,提取时间戳、日志级别等关键信息,并将多个日志文件的内容按时间顺序合并显示,...
- 声明式与命令式代码(声明模式和命令模式)
-
编程范式中的术语和差异信不信由你,你可能已经以开发人员的身份使用了多种编程范例。因为没有什么比用编程理论招待朋友更有趣的了,所以这篇文章可以帮助您认识代码中的流行范例。命令式编程命令式编程是我们从As...
- linux中的常用命令(linux常用命令和作用)
-
linux中的常用命令linux中的命令统称shell命令shell是一个命令行解释器,将用户命令解析为操作系统所能理解的指令,实现用户与操作系统的交互shell终端:我们平时输入命令,执行程序的那个...
- 提高工作效率的--Linux常用命令,能够决解95%以上的问题
-
点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf...
- 如何限制他人操作自己的电脑?(如何控制别人的电脑不让发现)
-
这段时间,小猪罗志祥正处于风口浪尖,具体是为啥?还不知道的小伙伴赶紧去补一下最近的娱乐圈八卦~简单来说,就是我们的小罗同事,以自己超强的体力,以及超强的时间管理能力,重新定义了「多人运动」的含义,重新...
- 最通俗易懂的命令模式讲解(命令模式百科)
-
我们先不讲什么是命令模式,先通过一个场景来引出命令模式,看看命令模式能解决什么样的问题。现在有一个渣男张三,他有还几个女朋友,你现在是不是还是单身狗,你就说你气不气?然后他需要每天分别叫几个女朋友起床...
- 互联网大厂后端必看!Spring Boot 中Runtime执行与停止命令?
-
你是否曾在使用SpringBoot开发项目时,遇到需要执行系统命令的场景?比如调用脚本进行文件处理,又或是启动外部程序?很多后端开发人员会使用Processexec=Runtime.get...
- Linux 常用命令(linux常用的20个命令面试)
-
日志排查类操作命令...
- Java字节码指令:if_icmpgt(0xA3)(java字节码使用的汇编语言)
-
if_icmpgt是Java字节码中的一条条件跳转指令,其全称是"IfIntegerCompareGreaterThan"。它用于比较两个整数值的大小。如果栈顶的第一个...
- 外贸干货|如何增加领英的曝光量和询盘
-
#跨境电商#...
- golang执行linux命令(golang调用shell脚本)
-
需求需要通过openssl生成rsa秘钥,然后保存该秘钥。代码实例packagemainimport("io/ioutil""bytes"&...
- LINUX磁盘挂载(linux磁盘挂载到windows)
-
1、使用root用户查看磁盘挂载情况:fdisk-l2、使用df查看当前磁盘挂载情况,根据和fdisk-l的结果进行对比,查看还有那些磁盘未使用3、挂载:mount磁盘挂载路径...
- Linux命令学习——nl命令(linux ln命令的使用)
-
nl命令主要功能为每一个文件添加行号,每一个输入的文件添加行号后发送到标准输出。当没有文件或文件为-时,读取标准输入...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)