百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

redis的简单与集群搭建

wptr33 2024-12-31 15:02 39 浏览

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

相关推荐

[常用工具] git基础学习笔记_git工具有哪些

添加推送信息,-m=messagegitcommit-m“添加注释”查看状态...

centos7安装部署gitlab_centos7安装git服务器

一、Gitlab介1.1gitlab信息GitLab是利用RubyonRails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。...

太高效了!玩了这么久的Linux,居然不知道这7个终端快捷键

作为Linux用户,大家肯定在Linux终端下敲过无数的命令。有的命令很短,比如:ls、cd、pwd之类,这种命令大家毫无压力。但是,有些命令就比较长了,比如:...

提高开发速度还能保证质量的10个小窍门

养成坏习惯真是分分钟的事儿,而养成好习惯却很难。我发现,把那些对我有用的习惯写下来,能让我坚持住已经花心思养成的好习惯。...

版本管理最好用的工具,你懂多少?

版本控制(Revisioncontrol)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。...

Git回退到某个版本_git回退到某个版本详细步骤

在开发过程,有时会遇到合并代码或者合并主分支代码导致自己分支代码冲突等问题,这时我们需要回退到某个commit_id版本1,查看所有历史版本,获取git的某个历史版本id...

Kubernetes + Jenkins + Harbor 全景实战手册

Kubernetes+Jenkins+Harbor全景实战手册在现代企业级DevOps体系中,Kubernetes(K8s)、Jenkins和Harbor组成的CI/CD流水...

git常用命令整理_git常见命令

一、Git仓库完整迁移完整迁移,就是指,不仅将所有代码移植到新的仓库,而且要保留所有的commit记录1.随便找个文件夹,从原地址克隆一份裸版本库...

第三章:Git分支管理(多人协作基础)

3.1分支基本概念分支是Git最强大的功能之一,它允许你在主线之外创建独立的开发线路,互不干扰。理解分支的工作原理是掌握Git的关键。核心概念:HEAD:指向当前分支的指针...

云效Codeup怎么创建分支并进行分支管理

云效Codeup怎么创建分支并进行分支管理,分支是为了将修改记录分叉备份保存,不受其他分支的影响,所以在同一个代码库里可以同时进行多个修改。创建仓库时,会自动创建Master分支作为默认分支,后续...

git 如何删除本地和远程分支?_git怎么删除远程仓库

Git分支对于开发人员来说是一项强大的功能,但要维护干净的存储库,就需要知道如何删除过时的分支。本指南涵盖了您需要了解的有关本地和远程删除Git分支的所有信息。了解Git分支...

git 实现一份代码push到两个git地址上

一直以来想把自己的博客代码托管到github和coding上想一次更改一次push两个地址一起更新今天有空查资料实践了下本博客的github地址coding的git地址如果是Gi...

git操作:cherry-pick和rebase_git cherry-pick bad object

在编码中经常涉及到分支之间的代码同步问题,那就需要cherry-pick和rebase命令问题:如何将某个分支的多个commit合并到另一个分支,并在另一个分支只保留一个commit记录解答:假设有两...

模型文件硬塞进 Git,GitHub 直接打回原形:使用Git-LFS管理大文件

前言最近接手了一个计算机视觉项目代码是屎山就不说了,反正我也不看代码主要就是构建一下docker镜像,测试一下部署的兼容性这本来不难但是,国内服务器的网络环境实在是恶劣,需要配置各种镜像(dock...

防弹少年团田柾国《Euphoria》2周年 获世界实时趋势榜1位 恭喜呀

当天韩国时间凌晨3时左右,该曲在Twitter上以“2YearsWithEuphoria”的HashTag登上了世界趋势1位。在韩国推特实时趋势中,从上午开始到现在“Euphoria2岁”的Has...