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.红锁