redis安装
基于Ubuntu安装过程
首先安装C语言环境
Redis是c语言开发的
# 安装依赖项 gcc/g++、make等开发工具
$ sudo apt-get install build-essential
$ sudo apt-get install gcc
$ sudo apt-get install g++
1.创建soft目录
#创建soft目录 用于放tar包
mkdir soft
#进入soft
cd soft
2.在官网找到redis对应版本
地址 https://redis.io/download
# 在soft 下载tar 执行下面语句
$ wget http://download.redis.io/releases/redis-6.0.6.tar.gz
# 解压
$ tar -xzvf redis-6.0.6.tar.gz
# 创建 mkdir /usr/local/redis 注:在这里建的目的遵守规则
$ mkdir /usr/local/redis
# 将解压的包移动到 mv redis-6.0.6 /usr/local/redis
$ mv redis-6.0.6 /usr/local/redis
3.编译
# 进入/usr/local/redis/redis-6.0.6
$ cd /usr/local/redis/redis-6.0.6
# 进行编译
$ make
问题:编译出现 -bash: make: command not found
先进行make 安装
$ apt-get install make
4.安装
# 最后安装redis /usr/local/redis为安装路径
$ make install PREFIX=/usr/local/redis
这里多了一个关键字
PREFIX=
这个关键字的作用是编译的时候用于指定程序存放的路径。比如我们现在就是指定了redis必须存放在/usr/local/redis目录。假设不添加该关键字Linux会将可执行文件存放在/usr/local/bin目录,库文件会存放在/usr/local/lib目录。配置文件会存放在/usr/local/etc目录。其他的资源文件会存放在usr/local/share目录。这里指定号目录也方便后续的卸载,后续直接rm -rf /usr/local/redis 即可删除redis。
安装有问题 rm -rf /usr/local/redis 然后再复数以上步骤
4.启动
# 进入/usr/local/redis/bin 启动
$ ./redis-server
# ctrl+c 就会自动退出
#改为后台启动
后台启动
# 把/usr/local/redis/redis-6.0.6/redis.conf复制到/usr/local/redis/bin目录下
$ cp redis.conf /usr/local/redis/bin/
# 编辑 vim redis.conf
$ vi redis.conf
# 找到 daemonize no
# 输入 i 进入编辑模式
# 改成 daemonize yes
# Esc退出编辑模式
# :qw! 强制退出并保存
# 后台启动
$ ./redis-server redis.conf
查看redis是否正在运行
# 查看进程得方式
$ ps -aux | grep redis
# 采取端口监听查看方式
$ netstat -lanp | grep 6379
redis-cli
$ ./redis-cli
redis-cli
是连接本地redis服务的一个命令,通过该命令后可以进入redis的脚本控制台输入exit可以退出redis脚本控制台
关闭服务
输入
redis-cli
进入控制台后输入命令shutdown
即可关闭运行中的Redis服务了
最后可以切换redis的ip
先关闭服务
修改redis.conf 192.168.29.130(为虚拟机ip)
bind 127.0.0.1 ----> bind 127.0.0.1 192.168.29.130
# 启动方式
$ ./redis-cli -h 192.168.29.130 -p 6379
RedisDesktopManager连接
# 修改redis.conf文件
$ vim redis.conf
有密码
- 后台启动 daemonize yes
- 绑定IP bind 127.0.0.1 192.168.29.130
- requirepass 属性新加得 requirepass 123456 设置密码为123456 设置完后远程登录需要添加密码
- 加了密码 进入后先进行密码输入 auth “123456”
无密码
protected-mode no (这样设置完以后远程登录不需要密码)
在linux操作命令
命令大全链接 redisdoc.com
# 切库 select 对应的库
$ select 1
# 清除所有库
$ flushall
# 清除当前的库
$ flushdb
# 查看该库所有数据(不建议使用)
$ keys *
# 查看该库数量
$ dbsize
# 查看值是否存在
$ exists wlr
# 删除 key
$ del wlr
# 修改 key
$ rename wlr wlr1
# 查看数据类型
$ type wlr
String
# EX(表示过期时间 秒 自动删除) PX(表示过期时间 毫秒 自动删除)
$ set wlr "wlr" EX 10
$ set wlr "wlr" PX 10
# NX(表示key不存在的时候才能成功) XX(表示key存在的时候才能成功)
$ set wlr "wlr" NX
$ set wlr "wlr" XX
# 批量set wlr lj 为key
$ mset wlr 123 lj 456
# 获取多个值
$ mget wlr lj
# 获取某个key中值的长度
$ strlen wlr
# 在某个key中追加值
$ append wlr "666"
# 根据索引返回指定位置的数据
$ getrange wlr 0 -1 (所有长度)
$ getrange wlr 0 3 (前4位)
# 对数值进行递增 incr 递增1 incrby 递增多个
$ incr wlr
$ incrby wlr 100
# 对数值进行递减 decr 递减1 decrby 递减 多个
$ decr wlr
$ decrby wlr 50
# 浮点数 incrbyfloat 递增
$ incrbyfloat f 1.1
# 修改 8位二进制 转成对应的ASCII码 setbit k1 7 0或1
$ setbit k1 7 0
$ getbit k1 0
$ bitpos k1 0 #返回的是0或1在第几位
$ bitcount k2 #返回的是1有多少个
# 分层存储 对应的 food表名 meat/friot ID名 后面的就是字段名
$ set food:meat:beaf 1
$ set food:meat:pork 2
$ set food:friot:apple 3
$ set food:friot:banana 4
$ keys food* #获取food下所有的数据
$ keys food:friot* #获取food:friot下所有的数据
$ mset food:meat:beaf 1 food:friot:apple 3....
hash
- 节省空间
- 减少key冲突
- 减少资源消耗
问题
- 不能对每个field进行设置过期时间
- 没有bit位操作
- 当field value 的值太大 cluster集群是对key%取模 这样对某单个redis节点压力太大
# 对hash的存值 hset h1 f(key) 6(value)
$ hset h1 f 6
$ hget h1 f
# 存多个
$ hmset h1 a 1 b 2 c 3
$ hmget h1 f a b c
# 整数
$ hset h2 a 1
# 递增
$ hincrby h2 a 10
# 无递减 可以用加负数来实现
$ hincrby h2 a -10
# 浮点
$ hset h2 b 2.6
# 浮点增加
$ hincrbyfloat h2 a 1.1
# key是否存在
$ hexists h1 a
# 删除field
$ hdel h1 a
# 返回key的个数
$ hlen h1
# hkeys h1
# hvals h1
# hgetall h1
# hget exists h1
List
有序队列 可重复 可实现简单消息队列
# l开头就是从左边开始 r开头就是从右边开始
# 增加元素
$ lpush queue c
$ rpush queue f
#弹出元素 相当于删除
$ lpop queue
$ rpop queue
#通过下标查询元素
$ lindex queue 0
# 通过范围查询元素
$ lrange queue 0 -1 #获取所有元素
#弹出没有数据进入阻塞状态 设置过期时间 秒
$ blpop key1 10
$ brpop key1 10
可以通过rpush lpop 实现简单的消息队列
set
无序集合 不重复
# 添加多个元素
$ sadd myset a b c d e f g
# 查询所有元素
$ smembers myset
# 查询所有数量
$ scard myset
# 随机查询 一个 或 多个元素
$ srandmember myset 1 (填写个数 不写默认一个)
# 随机查询平且删除 一个 或 多个元素
$ spop myset 1 (填写个数 不写默认一个)
# 删除 一个/多个 元素
$ srem myset b a...
# 判断某个元素是否在集合中
$ sismember myset a #返回 0为不是 1为是
####商品筛选
# 获取差集(只在第一个集合,不再第二个集合)
$ sdiff set1 set 2
# 获取交集(在两个集合中都存在)
$ sinter set1 set2
# 获取并集(合并后去重)
$ sunion set1 set2
根据spop 可以实现 抽奖 点赞 签到...功能
用like:t1001来维护t1001这条微博的所有点赞用户
点赞了这条微博: sadd ike:t1001u3001
取消点赞: srem like:t1001u3001
是否点赞: dismember like:t1001u3001
点赞的所有用户: members like:t1001
点赞数: scard ike:t1001
zset
有序集合 跟据 value(分值)排序 当分值相等 会根据key的ASCLL来进行排序 默认小到大
# 加值
$ zadd myzset 1 a 2 b 3 c
# 根据下标区间查询值 withscores 显示分值
$ zrange myzset 0 -1 withscores
# 反转 大的排前面
$ zrevrange myzset 0 -1 withscores
# 根据分值区间查询key
$ zrangebyscore myzset 2 3
# 移除 一个/多个元素
$ zrem myzset b c...
# 查询个数
$ zcard myzset
# 对某个key的分值进行增加
$ zincrby myzset 5 c
# 获取某个key的位置 也就是下标
$ zrank myzset c
# 获取某个key 的分值
$ zscore myzset c
可用于排行榜 点击数加1 zincrby a:20201225 1 n6111
获取今天点击最多的10条:
zrevrange myzset:20201225 0 9 withscores
Geo,HyperLogLogs geo hll
Geo 保存地理位置数据类型 保存经度 纬度信息 HyperLogLogs 基于基数(个数不重复)的统计方法
Geo
# 添加多个城市的经度 纬度信息
$ geoadd citys 121.48 31.22 sh 113.01 28.19 wlr ...
# 获取数据
$ geopos citys wlr sh...
# 获取城市之间的距离 后面跟着单位 m km mi(英里) ft(英尺)
$ geodist citys wlr sh km
# 基于某一个指定的位置 找出不超过某一个范围的所有符合条件的元素
$ georadius citys 113.01 28.19 5 km
# 基于某一个指定的key 找出不超过某一个范围的所有符合条件的元素
$ georadiusbymember citys wlr 20 km
HyperLogLogs 数据量较大 会丢失精度
# 添加元素
$ pfadd log 1 2 3 4 5 6
# 查询有多少个元素
$ pfcount log
# 合并
$ pfmerge result log wlr
发布订阅
subscribe psubscribe 发布模式都会进行阻塞 必须 打开新的窗口进行订阅
# 订阅频道:可以一次订阅多个
$ subscribe channel-1 channel-2 channel-3
# 向指定频道发布消息
$ publish channel-1 123
# 取消订阅(不能在订阅状态下使用)
$ unsubscribe channel-1
--------------------------------------------
#消费端1,关注运动信息
$ psubscribe *sport
#消费端2,关注所有新闻
$ psubscribe news*
Redis事务
- 按顺序执行
- 不会受到其他客户端的干扰
$ set NX # 实现锁机制
$ expire TTL #设置时间有效期
$ del
# 开启事务
$ multi
# 执行事务
$ exec
# 关闭事务
$ discard
# 乐观锁 起到监视作用
$ watch
# 取消监视
$ anwatch
# 开启multi后
# 在执行exec之前遇到异常 全部不会执行
# 在执行exec之后遇到异常 只有发生异常的不会执行
# 所以redis不能用这种方法实现原子性来保证数据的一致性
Lua脚本
- 批量执行命令
- 保证数据的原子性
- 操作集合的复用
$ eval lua-script key-num [key1 key2 ...] [value1 value2 ...]
# eval代表执行Lua语言的命令
# lua- script代表Lua语言脚本内容
# key-num表示参数中有多少个key,需要注意的是 Redis中key是从1开始的 如果没有key的参数 那么写0
# [key1 key2 ...]是key作为参数传递给Lua语言,也可以不填,但是需要和key-num的个数对应起来
# [valuel value2 ...]这些参数传递给Lua语言,它们是可填可不填
# 在Lua脚本中写Redis命令
$ redis.call(command,key[param1,param2...])
例:eval "redis.call('set',KEYS[1],ARGV[1])" 1 wlr 111
# command是命令 包括set、get、del等
# key是被操作的键
# param1 param2 ...代表给key的参数。
# Lua脚本文件
例:
redis.call('set','wlr','66666')
return redis.call('get','wlr')
# 执行
$ ./redis-cli --eval wlr.lua 0
# 缓存Lua脚本
$ script load 脚本
# 会生成一个摘要值
# 然后根据摘要值进行执行 需要携带参数个数
$ evalsha "摘要值" 0
数据淘汰与内存回收
定期策略
- 定时过期:到了key的过期时间,立即删除
- 惰性过期:key被访问的时候才判断是否过期,过期则删除
- 定期过期:每隔一段时间,清除一定数量的过期的key
淘汰策略
LRU Least Recently Used (最近最少使用)
LFU Least Frequently Used (最不常用)
volatile 就是只会淘汰设置TTL(过期时间)的key
allkeys 所有的
volatile-lru
allkeys-lru
volatile-lfu
allkeys-lfu
volatile-random # 随机淘汰即将要过期的key
allkevs-random
volatile-ttl # 优先淘汰即将要过期的key
noeviction # 无淘汰策略
#在redis.conf配置文件中 有 maxmemory <bytes> 属性 来配置 能够使用的最大内存做限制并通过maxmemory_policy noeviction 设置淘汰策略
LRU淘汰机制
#可以用 链表+hashMap来模拟 LRU 算法 比较消耗内存
redis 用的是 随机采样 方式
# 在redis.conf配置文件中 有 maxmemory-samples 5 属性 来设置
淘汰热度最低的
每个key 都有一个LRU属性字段 用于存时间戳值 取值是取 全局变量 server.lruclock 中的值
server.lruclock 通过 serverCron 每隔100ms 进行更新一次
淘汰机制是 用key中 存取的 LRU时间戳字段 与 server.lruclock 的差值越大 属性 热点越低
LFU淘汰机制
1.key最后被访问的时间
2.key被访问的频率(计数)
# 在redis.conf配置文件中 有 lfu-log-factor 10 属性 来设置 计数的增长速度 越大增长的就越慢
# 在redis.conf配置文件中 有 lfu-decay-time 1 属性 来设置 表示有几分钟没访问 就 减少多少
持久化机制
RDB
RDB Redis DataBase 默认方案 在内存满足一定的条件 就会写到磁盘RDB文件中 当系统重启时又归附给内存
自动触发
配置规则触发 在redis.conf配置文件中 有save 900 1 … 规则代表900秒内有一次key被修改了 就会触发写入RDB文件中 还有配置写入的路径 dir ./ 名称 dbfilename dump.rdb
调用shutdown触发
flushall触发
手动触发
用于迁移数据的时候
- save 当数据量大的时候会导致阻塞 不建议使用
- bgsave 在后台异步进行数据备份
注:要备份 dump.rdb
特点
- 内容紧凑
- 不影响主进程
- 恢复大数据集时速度快
- 同步频率
不足:无法做到秒级持久化 不是时时的
AOF
AOF Append Only File 默认不开启 开启后 持久化以日志的形式记录服务器所处理的每一个写 删除操作 查询操作不会记录 以文本的方式记录 可以打开文件看到详细的操作记录
注 AOF开启了 会优先使用AOF
在redis.conf配置文件中 有 appendonly on 属性 改成 yes appendfilename “appendonly.aof” 为默认的AOF文件
存储路径和 RDP一样 路径为 dir ./
触发机制
appendfsync everysec 表示 每一秒中 触发一次
no 表示 不会触发
always 表示 每一次写入 或 修改 都会触发
AOF文件过大
有一个重写命令 手动重写 bgrewriteaof
自动触发 重写
在redis.conf配置文件中 有 auto-aof-rewrite-percentage 100 属性 根据比例
auto-aof-rewrite-min-size 64mb 根据文件大小控制
开启后 注
先 重写 bgrewriteaof 并备份 RDB文件
防止 AOF文件为空 把数据清空
在并发情况下RDB性能比较好
特点
- 同步频率 多种频率写入
- 文件大小 可以压缩
- 性能 比较好
pipeline(管道)
Redis 使用的是客户端-服务器(CS)模型和请求/响应协议的 TCP 服务器。这意味着通常情况下一个请求会遵循以下步骤:
客户端向服务端发送一个查询请求,并监听 Socket 返回,通常是以阻塞模式,等待服务端响应。服务端处理命令,并将结果返回给客户端。
Redis 客户端与 Redis 服务器之间使用 TCP 协议进行连接,一个客户端可以通过一个 socket 连接发起多个请求命令。每个请求命令发出后 client 通常会阻塞并等待 redis 服务器处理,redis 处理完请求命令后会将结果通过响应报文返回给 client,因此当执行多条命令的时候都需要等待上一条命令执行完毕才能执行
而管道(pipeline)可以一次性发送多条命令并在执行完后一次性将结果返回,pipeline 通过减少客户端与 redis 的通信次数来实现降低往返延时时间 而且 Pipeline 实现的原理是队列 而队列的原理是时先进先出 这样就保证数据的顺序性
Redis集群与分布式
集群主从复制
用集群原因
- 性能 在并发非常高的情况下 性能就会不行
- 扩展 水平扩容问题 redis所有数据都是放入内存中 数据量太大会出于硬件限制
- 可用性,安全 防止单点故障
# 启用三台 linux redis slaveof(旧命令) replicaof(新命令)
# 其中一台为主机 在另外两台 redis.conf 中加入 slaveof 192.168.... 6379 (配置或者命令)
#或者 ./redis-server --slaveof 192.168.... 6379 (启动参数)
# 查看连接情况
# info replication
从节点不能写入key 只能读
# 断开和主节点的连接
$ slaveof no one
主从复制步骤
- 连接 有一个replicationCron定时任务 每隔1秒 会去检测是否有主节点要去连接 有的话会去建立一个socket网络连接 连接成功后 会专门建一个文件事件复制的处理器
- 数据同步 连接成功后 第一次会通过bgsave命令 生成一个本地RDB快照文件 把数据全量发送给从节点 同时这样会产生问题 1.如果从节点 之前有数据 会把之前数据全部干掉 再去加载RDB中的数据 2.数据量比较大的时候 主节点在传输RDB文件同时 又在进行 写入操作保存到缓存中 从节点会先接收RDB文件 再接收 缓存数据 保证了 数据同步的问题
- 命令传播 通过异步的复制 把命令 传给 从节点 如果在这途中 从节点断开了 然后又连上 主节点 会记录(slave_repl_offset:… master_repl_offset)偏移量 去记录 有没有复制完毕
不足
- 单个节点数据量太多时,同步比较耗时
- 需要手动切换主从
哨兵Sentinel
监控(monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常
提醒(Notifation):当被监控的某个 Redis 服务器出现问题时 Sentinel 可以通过 API 向管理员或者其他应用程序发送通知
自动故障转移(Automatic failover):当一个主服务器不能正常工作时 Sentinel 会开始一次自动故障迁移操作 它会将失效主服务器的其中一个从服务器升级为新的主服务器 并让失效主服务器的其他从服务器改为复制新的主服务器 当客户端试图连接失效的主服务器时 集群也会向客户端返回新主服务器的地址 使得集群可以使用新主服务器代替失效服务器
开启哨兵模式 至少需要3个Sentinel实例(奇数个,否则无法选举Leader)
创建sentinel.conf配置文件
daemonize yes
port 26379
protected-mode no
dir "/usr/local/soft/redis-6.0.6/sentinel-tmp"
sentinel monitor redis-master 192.168.254.137 6379 2
sentinel down-after-milliseconds redis-master 30000
sentinel failover-timeout redis-master 180000
sentinel parallel-syncs redis-master 1
sentinel.conf配置文件内容 三台机器相同
配置 | 作用 |
---|---|
protected-mode | 是否允许外部网络访问,yes不允许 |
dir | sentinel的工作目录 |
sentinel monitor | sentinel监控的redis主节点 |
down-after-milliseconds(毫秒) | master宕机多久,才会被Sentinel主观认为下线 |
sentinel failover-timeout(毫秒) | 1 同一个sentinel对同一个master两次failover之间的间隔时间。2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。3.当想要取消一个正在进行的failover所需要的时间。 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。 |
parallel-syncs | 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。 |
# 启动Redis和Sentinel
$ cd /usr/local/soft/redis-6.0.6/src
$ ./redis-server ./redis.conf
$ ./redis-sentinel ./sentinel.conf
# 其他方式
$ ./redis-server ./sentinel.conf --sentinel
服务下线
主观下线与客观下线
当服务发送ping 超过了 down-after-milliseconds 这个设置的时间没有响应 哨兵为主观下线
同时sentinel询问其他sentinel节点 如果超过一半以上的节点 说master节点下线了 这个时候就是客观下线
当主下线 sentinel开始选举 会先选一个 sentinel - leader 通过 Raft(共识)算法(先到先得 少数服从多数)选举成功后 进行故障转移流程 会把该节点 的 slaveof 通过 slaveof no one 命令 变成独立的 在其他节点 slaveof xxx xx
选举m影响因素
- 断开连接的时常 (slaveof 和 sentinel 断开时间太长了 直接失去选举权)
- 如果都保持良好的连接取 优先级 (redis.conf 文件中 replica-priority 100 越小优先级越高)
- 以上都保持良好 就 看 谁在 主节点 复制(偏移量offset)的数据越多 选举概率越大
- 进程id最小
代理分片
客户端jedis 提供了可以分片连接池 不需要写分片逻辑
代理Proxy
- Twemproxy
- Codis
- redis Cluster (redis 自带)
Redis
简称REmote DIctionary Service
远程字典服务
sql
- 扩容 动态扩容(分库分表)(不容易)
- 数据格式不灵活 (删减字段修改幅度太大)
- 读写的压力较大 …等
nosql
- 非结构化数据
- 数据与数据没有关联
- 海量数据存储,高并发读写
- 支持分布式:数据分片,扩缩容简单等
特点
1.跨进程,分布式
2.丰富的数据类型
3.丰富的功能
4.编程语言的支持
应用场景
- 缓存
- 分布式Session
- set NX EX 分布式锁
- incr 全局ID
- incr 计数器
- incr 限流
- 位操作 统计
jedis
如果有多个线程操作 要用到 线程池 保证安全问题
jedis第一个实例
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.254.137",6379);
jedis.set("wlr","wlr");
System.out.println(jedis.get("wlr"));
jedis.close();
}
lettuce
springBoot 2.0版本以上 有一个默认的客户端 封装了lettuce
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Redisson
官方推荐
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.5</version>
</dependency>