redis

# Redis简介 Redis是一款内存高速缓存数据库。Redis全称为:**Remote Dictionary Server** (远程数据服务),使用C语言编写,Redis是一个key-value存储系统(键值存储系统),支持丰富的数据类型,如:String、list、set、zset、hash。 Redis是一种支持key-value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。支持网络,提供字符串,哈希,列表,队列,集合结构直接存取,基于内存,可持久化。 # 下载与安装 下载地址:https://redis.io/download/#redis-downloads 检查服务器上是否存在gcc-c++的环境 **指令:** ```shell gcc -v ``` 如果没有则不显示,先需安装gcc 在线安装 ``` yum install gcc-c++ ``` 上传离线压缩包或者下载到公共目录:/usr/local/redis/ ```$ $ tar -zxvf redis-6.2.6.tar.gz $ ln -s redis-6.2.6 redis #创建软连接 $ cd redis-6.2.6 $ make MALLOC=libc # 编译文件,编译后的文件在当前路径src目录下 ``` 出现以下即安装完成! Hint: It's a good idea to run 'make test' make[1]: Leaving directory /usr/local/redis/redis/src 如果报错“/bin/sh: cc: command not found” 通过 yum install gcc 解决。如果报错“zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory”,添加编译参数 MALLOC=libc 解决,完整命令如下 make PREFIX=/data/redis install MALLOC=libc 安装完,可以直接在启动src路径下启动服务 `./redis-server`,可以看到默认端口是 `6739`,默认配置文件是 `redis.conf`;直接运行退出会话后服务就关闭了,需要改下配置,改成后台运行; ``` # cp redis.conf redis_bak.conf # 备份配置文件 cp redis.conf redis_6379.conf # 新的配置文件(配置监听不同端口,redis支持单机多开,启动时指定配置文件) vim redis_6379.conf port 6379 # redis默认端口6739 daemonize yes # 以守护进程的方式运行 # 日志文件 logfile "/var/log/redis/redis_6379.log" # pid文件 pidfile /var/run/redis_6379.pid # RDB持久化文件 dbfilename dump_6379.rdb # AOF快照文件 appendfilename "appendonly_6379.aof" ``` ## 配置外部访问 ``` # redis监听的网卡ip地址(设置 127.0.0.1 仅本机访问;设置为 0.0.0.0 或注释掉,表示监听所有接口;指定多个ip时用空格隔开)这里理解可能会有误区,bind指定的ip并非远端调用者ip,而是本机的网卡对应的ip地址,一个主机可以有多个网卡,使用 ifconfig 查看; #注释bind #bind 127.0.0.1 192.168.248.128 # 实际操作中防火墙策略开放端口 # 将protected-mode模式修改为no(yes只允许本地访问) protected-mode no # 设置需要密码才能访问,123456为示例,请改为强密码 requirepass 123456 # 线上为了安全和方便,一般做法是防火墙配置端口白名单; # 开放6379端口 (ubuntu) sudo ufw allow 6379 # 重启防火墙(修改配置后要重启防火墙) sudo ufw reload # 开放6379端口 (CentOS) firewall-cmd --permanent --add-port=6379/tcp # 重启防火墙(修改配置后要重启防火墙) firewall-cmd --reload ``` ## 启动关闭服务 ``` # 启动(带配置文件):(每次修改完redis.conf都需要重启服务) ./src/redis-server redis_6379.conf # 启动(带指定某几个配置) ./src/redis-server redis_6379.conf --daemonize yes --port 6379 # 查看服务进程 ps aux|grep redis # 停止: ./src/redis-cli -p 6379 -a 123456 shutdown #kill -9 pid值,或pkill redis-server效果上可以,但极为不推荐 # kill 强行终止redis进程可能会导致redis持久化数据丢失,/var/run/redis_6379.pid进程文件无法删除,导致后续启动异常。 # 正确停止 Redis 的方式应该是向 Redis 发送 shutdown 命令。 ``` ## 配置环境变量 vim /etc/profile 最后一行增加:export PATH=$PATH:/data/redis/bin 最后执行:source /etc/profile ## 把redis注册为服务 ``` #!/bin/sh # chkconfig: 2345 10 90 #redis服务必须在运行级2,3,4,5下被启动或关闭,启动的优先级是90,关闭的优先级是10。 # description: Start and Stop redis REDISPORT=6379 #端口号,这是默认的,如果你安装的时候不是默认端口号,则需要修改 EXEC=./redis/src/redis-server #redis-server启动脚本的位置,你如果忘了可以用find或whereis找到 REDIS_CLI=./redis/src/redis-cli #redis-cli客户端启动脚本的位置,你如果忘了可以用find或whereis找到 PIDFILE=./pid/redis_${REDISPORT}.pid #这个也可以用find或whereis找到 CONF="./conf/redis_6379.conf" #redis.conf配置文件的位置,你如果忘了可以用find或whereis找到 AUTH="1234" case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed." else echo "Starting Redis server..." $EXEC $CONF fi if [ "$?"="0" ] then echo "Redis is running..." fi ;; stop) if [ ! -f $PIDFILE ] then echo "$PIDFILE exists, process is not running." else PID=$(cat $PIDFILE) echo "Stopping..." $REDIS_CLI -p $REDISPORT SHUTDOWN sleep 2 while [ -x $PIDFILE ] do echo "Waiting for Redis to shutdown..." sleep 1 done echo "Redis stopped" fi ;; restart|force-reload) ${0} stop ${0} start ;; *) echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2 exit 1 esac ``` 记得设置执行权限 ``` chmod a+x /etc/init.d/redis ``` 启动服务 ``` service redis start ``` 客户连接验证 ``` redis-cli ``` 出现:127.0.0.1:6379> redis-cli ,说明启动成功。 输入 auth 上边设置的redis密码 登录成功后可通过CONFIG GET 查看配置,例如:CONFIG GET bind* 输入quit退出 设置开机启动 开机启动:chkconfig redis on 查看启动是否设置成功: chkconfig --list 查看版本: redis-cli --version # 数据类型 ## 5种基础数据类型(常用命令) | 结构类型 | 结构存储的值 | 结构的读写能力 | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | | **String字符串** | String类型是二进制安全的,意思是 redis 的 string 可以包含任何数据。如数字,字符串,jpg图片或者序列化的对象。<br />可以是字符串、整数或浮点数 | 对整个字符串或字符串的一部分进行操作;对整数或浮点数进行自增或自减操作; | | **List列表** | Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),<br />一个列表最多可以包含 2^32^ - 1 个元素 (4294967295, 每个列表超过40亿个元素)。 | | | **Set集合** | Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 | | | **Hash散列** | Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。<br />Redis 中每个 hash 可以存储 2^32^ - 1 键值对(40多亿)。 | | | **Zset有序集合** | Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。<br />集合对象的编码可以是 intset 或者 hashtable.<br />Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。<br />集合中最大的成员数为 2^32^ - 1 (4294967295, 每个集合可存储40多亿个成员)。 | | ### String字符串命令 | 命令 | 简述 | 使用 | | ------ | ---------------------- | -------------- | | get | 获取存储在给定键中的值 | get key | | set | 设置存储在给定键中的值 | set key val | | del | 删除存储在给定键中的值 | del key | | incr | 将键存储的值加1 | incr key | | decr | 将键存储的值减1 | decr key | | incrby | 将键存储的值加上整数 | incrby key num | | decrby | 将键存储的值减去整数 | decrby key num | ### List列表 | 命令 | 简述 | 使用 | | ------ | ---------------------------------------------------------------------------------------------------------------------- | ---------------- | | lpush | 将给定值推入到列表左端,可以一次插入多个值 | lpush key val... | | rpush | 将给定值推入到列表右端,可以一次插入多个值 | rpush key val.. | | lpop | 从列表的左端弹出一个值,并返回被弹出的值 | lpop key | | rpop | 从列表的右端弹出一个值,并返回被弹出的值 | rpop key | | lrange | 获取列表在给定范围上的所有值 0:开始 -1:结束 。 0 -1 代表全部 | lrange key 0 -1 | | lindex | 通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 | lindex key index | ### Set集合 | 命令 | 简述 | 使用 | | --------- | ------------------------------------- | -------------------- | | sadd | 向集合添加一个或多个成员 | sadd key val1 val2 | | scard | 获取集合的成员个数 | scard key | | smembers | 返回集合中的所有成员 | smembers key | | sismember | 判断 member 元素是否是集合 key 的成员 | sismember key member | ### Hash散列 | 命令 | 简述 | 使用 | | ------- | ---------------------------------------- | ---------------------------- | | hset | 添加键值对 | hset hashkey sub-key1 value1 | | hget | 获取指定散列键的值 | hget hashkey field | | hgetall | 获取散列中包含的所有键值对 | hgetall key | | hdel | 如果给定键存在于散列中,那么就移除这个键 | hdel hash-key sub-key1 | ### Zset有序集合 Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。 | 命令 | 简述 | 使用 | | ------ | -------------------------------------------------------- | --------------------- | | zadd | 一个带有给定分值的成员添加到有序集合里面 | zadd | | zrange | 根据元素在有序集合中所处的位置,从有序集合中获取多个元素 | zrange key 0 -1 | | zrem | 如果给定元素成员存在于有序集合中,那么就移除这个元素 | zrem zset-key member1 | ## 3种特殊类型 # RDB和AOF机制详解 # Redis淘汰策略 **1. noeviction(默认策略)** :对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外) **2. allkeys-lru** :从所有key中使用LRU算法进行淘汰(LRU算法:即最近最少使用算法) **3. volatile-lru** :从设置了过期时间的key中使用LRU算法进行淘汰 **4. allkeys-random** :从所有key中随机淘汰数据 **5. volatile-random** :从设置了过期时间的key中随机淘汰 **6. volatile-ttl** :在设置了过期时间的key中,淘汰过期时间剩余最短的 当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误 ## 获取当前内存淘汰策略 ``` config get maxmemory-policy ``` ## 获取Redis能使用的最大内存大小 ``` config get maxmemory ``` 如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。32 位的机器最大只支持 4GB 的内存,而系统本身就需要一定的内存资源来支持运行,所以 32 位机器限制最大 3 GB 的可用内存 ## 设置淘汰策略 1.通过配置文件设置淘汰策略(修改redis.conf文件): ``` maxmemory-policy allkeys-lru ``` 通过命令修改淘汰策略: ``` 127.0.0.1:6379> config set maxmemory-policy allkeys-lru ``` 2.设置Redis最大占用内存大小 ``` #设置Redis最大占用内存大小为100M 127.0.0.1:6379> config set maxmemory 100mb ``` 配置文件设置最大占用内存大小 ``` maxmemory 20480mb ``` # Redis事务详解 Redis的事务提供了一种将多个命令请求打包,然后一次性、按顺序性地执行多个命令的机制。在事务执行期间,服务器不会中断事务而去执行其它客户端的命令请求,它会将事务中的所有命令执行完毕,然后才去处理其它客户端的命令请求。 # Redis三种集群模式 Redis有三种集群模式,分别是:主从模式、哨兵模式、Cluster模式。Rdis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master;后来为了高可用提出来**哨兵**模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充;所以在3.x提出cluster集群模式。 ## 主从模式 ![redis主从.png](https://cos.easydoc.net/44929674/files/lz0qk8go.png) ### 主从搭建步骤 主从修改的配置如下 复制三份配置文件 6379.conf ,6380.conf ,6381.conf 其中6380.conf和6381.conf下多增加 slaveof 192.168.214.128 6379 ``` # redis_base.conf 就是redis/src复制出来一份改了个名称 include redis_base.conf # redis默认端口6739 port 6739 # 以守护进程的方式运行 daemonize yes #保护模式关闭 protected-mode no # 日志文件 logfile "./log/redis_6739.log" # pid文件 pidfile ./pid/redis_6739.pid # RDB持久化文件 dbfilename dump_6739.rdb # AOF快照文件 appendfilename "appendonly_6379.aof" ``` 到此配置完毕。 启动三个redis ``` redis-service 6379.conf redis-service 6380.conf redis-service 6381.conf ``` 登录6379 ``` redis-cli -h 192.168.214.128 -p 6380 -a 123456 ``` 查看集群情况 ``` info replication ``` ![1668047915163.png](https://cos.easydoc.net/44929674/files/lk55hmsi.png) 出现上图情况则说搭建成功,到此主从搭建完毕 ## 哨兵机制(Redis Sentinel)详解 ![1667998534631.png](https://cos.easydoc.net/44929674/files/lk55ijeb.png) ## 分片集群(Redis Cluster) # Redis的监控详解 # Redis性能调优详解 # 缓存问题 ## 穿透,击穿, 雪崩, 污染 1. 缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这时缓存就永远不会生效,这些请求都打到数据库从而导致数据库压力过大。解决方案:可以使用布隆过滤器(BloomFilter)来过滤掉不存在的数据请求,避免直接查询数据库。 2. 缓存击穿 缓存击穿是指某个热点数据过期后,大量请求同时访问该数据,导致缓存失效,请求直接打到数据库,造成数据库压力过大。解决方案:可以设置热点数据的过期时间,或者使用分布式锁来控制并发访问。 3. 缓存雪崩 缓存雪崩是指在某一时刻,大量的缓存数据同时过期,导致大量请求直接打到数据库,造成数据库压力过大。解决方案:可以通过设置不同的过期时间、使用分布式锁、增加备份缓存等方式来避免缓存雪崩。 4. Redis数据一致性 为了保证Redis数据一致性,可以使用以下方法: - 使用事务(MULTI、EXEC、WATCH、UNWATCH等命令)来保证操作的原子性; - 使用乐观锁(Lua脚本)来保证数据的一致性; - 使用主从复制(replication)和哨兵模式(sentinel)来提高数据可用性。 5. 大key处理方案 对于大key的处理,可以采用以下方法: - 将大key拆分成多个小key,以降低单个key的大小; - 使用hash类型来存储大key的数据,以减少内存占用; - 定期对大key进行清理和优化,以保持Redis的性能。 6. SpringBoot集成Redis 在SpringBoot中集成Redis,可以通过以下步骤: - 引入相关依赖(spring-boot-starter-data-redis); - 配置Redis连接信息(application.properties或application.yml); - 使用RedisTemplate或StringRedisTemplate来进行Redis操作。 7. SpringBoot集成Redsesion Redsesion是一个基于Redis的分布式Session共享方案,可以在SpringBoot中通过以下步骤集成: - 引入相关依赖(redsesion-spring-boot-starter); - 配置Redsesion相关信息(application.properties或application.yml); - 在项目中使用Redsesion提供的API进行Session操作。 ### 锁的种类 1.可重入锁 ``` // 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30s lock.lock(); // 尝试拿锁10s后停止重试,返回false 具有Watch Dog 自动延期机制 默认续30s boolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 没有Watch Dog ,10s后自动释放 lock.lock(10, TimeUnit.SECONDS); // 尝试拿锁100s后停止重试,返回false 没有Watch Dog ,10s后自动释放 boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS); ``` 2.公平锁 3.读写锁 3.联锁 4.红锁