文章目录
一、centos上安装redis
1.编译安装
1. 先去redis官网的下载页面,左边就是源码包,我直接选择最新版的,直接右键复制链接(Download7.2.4)
wget https://github.com/redis/redis/archive/7.2.4.tar.gz
2. 解压
tar -xzvf 7.2.4.tar.gz
3. 因为是源码包,没有可执行文件,只能编译安装
来到源码包目录下才能执行下面命令 cd redis-7.2.4/
make && make install
'''
Redis 在CentOS7中部署如果出现make的问题
原因:因为Resdis 是C实现的,需要gcc来进行编译,所以原因是系统未安装gcc:
第一步:yum install -y gcc g++ gcc-c++ make
第二步,如果再次执行make,如果make出现错误为:致命错误,执行下面这个命令
make MALLOC=libc
'''
安装完毕后,会自动在环境变量中配置 /usr/local/bin/ 路径下会有可执行文件
4. src目录下,就会有几个可执行文件
redis-server--->redis服务器
redis-cli--->redis命令行客户端
redis-benchmark--->redis性能测试工具
redis-check-aof--->aof文件修复工具
redis-check-rdb--->rdb文件检查工具
redis-sentinel--->sentinel服务器,哨兵
5. 把src路径放到环境变量,这样在任意路径敲redis-server都能找到
2.redis-stack(可执行文件)安装
1. 先去redis官网的下载页面,选择可执行文件,然后点击All installation options ->进入到更多下载页面中,选择复制下载链接
2. 然后把链接切断成tar.gz结尾即可,就可以放到centos机器上,安装了
https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v9.rhel7.x86_64.tar.gz (切断的复制链接)
wget https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v9.rhel7.x86_64.tar.gz
3. 然后解压即用
tar -xzvf redis-stack-server-7.2.0-v9.rhel7.x86_64.tar.gz
4. 名字有点长,可以给它重命名一下(只用Move移动即可)
mv redis-stack-server-7.2.0-v9 redis-stack-server
5. 配置环境变量
cd ~ # 回家
ls -al # 查看当前文件夹下所有文件和文件夹(包含隐藏的,环境变量就在隐藏的里面,隐藏的都是点开头)
' .bash_profile 这个文件就是配置用户环境变量'
vi .bash_profile
'在进入之前先查看一下redis的bin目录 cd redis-stack-server/bin/,使用pwd查看路径/root/redis-stack-server/bin'
方式一:
PATH=$PATH:/root/redis-stack-server/bin 进入文件中,配置这个即可
方式二:
PATH=$PATH:$HOME/bin:/root/redis-stack-server/bin 直接在这句后面:/root/redis-stack-server/bin拼接即可
6. 配置环境变量之后,在任意路径下敲 redis-cli、redis-server都能找到
redis-cli
redis-server
3.补充Linux中配置环境变量
'mac/linux/win的环境变量,是一堆key-value,在操作系统整个运行过程中拿到'
-特殊的一个变量PATH: 在window中以 ;分割(在linux/mac中以 :分割),放了很多路径,这些路径下的可执行文件,可以在任意路径下执行
-用户变量,系统变量
-用户环境变量--->只有当前用户能用
-系统环境变量--->所有用户都用
-windows:
'输出环境变量'
echo %PATH% (变量前后用百分号包裹)
echo %WORKON_HOME%
'配置环境变量:点点点的直接添加即可,是图形化界面'
-mac,linux
'输出环境变量'
echo $PATH
-用户环境变量(每个用户家路径里)
cd ~ '回家'
ls -al '查看当前文件夹下所有文件和文件夹(包含隐藏的,环境变量就在隐藏的里面,隐藏的都是点开头)'
.bash_profile '这个文件就是配置用户环境变量'
vi .bash_profile '编辑环境变量-->等同于windows中直接在环境变量的用户变量中新建的一个变量名和变量值(key/value)'
source .bash_profile ' 让配置生效 (仅仅在当前窗口生效,其他窗口需要关闭重启才能生效)'
-系统环境变量
4.redis启动
方式一:最简单启动
'''
因为我使用了上面两种安装方式都安装了,所以我需要看一下启动的到底是哪个的
方法一:whereis redis-server # 查看redis-server安装目录,也可以查看redis-cli等
redis-server: /root/redis-stack-server/bin/redis-server
方法二:先查看进程号,在使用 ls -l /proc/对应的程序进程号/cwd
ps -ef|grep redis
ls -l /proc/54438/cwd
方法三:which redis-server # 同方法一一样,仅返回的没有方法一全面
/root/redis-stack-server/bin/redis-server
'''
redis-server # 启动服务
ps -ef|grep redis # 查看进程
# yum install net-tools -y
netstat -antpl|grep redis #查看端口
redis-cli -h ip -p port ping #命令查看
方式二:动态参数启动
redis-serve --port 6380 # 启动,自己设置监听6380端口
redis-cli -p 6380 # 默认客户端是监听6379,因为我这里改了端口,所以需要指定端口
方式三:配置文件启动
#配置文件启动(6379对应手机按键MERZ,意大利女歌手Alessia Merz的名字)
#通过redis-cli连接,输入config get * 可以获得默认配置
#在redis目录下创建config目录,copy一个redis.conf文件
#daemonize--》是否是守护进程启动(no|yes)
#port---》端口号
#logfile--》redis系统日志
#dir--》redis工作目录
1 cd redis-7.2.4/ # 进入到目录
2 mv redis.conf redis.conf.bak # 备份一下原来的配置文件
3 vi redis.conf # 在文件中写入
daemonize yes #是否以守护进程启动
pidfile /var/run/redis.pid #进程号的位置,删除
port 6379 #端口号
dir "/root/redis-7.2.4/data" #工作目录
logfile 6379.log #日志位置
4 redis-server ./redis.conf # 后台运行
二、redis客户端
1.配置允许远程链接
'配置允许远程链接'
1.使用配置文件启动redis
daemonize yes
bind 0.0.0.0
protected-mode no
requirepass 1234
pidfile /var/run/redis.pid
port 6379
dir "/root/redis-7.2.4/data"
logfile 6379.log
2.后台启动服务
redis-server ./redis.conf
3.允许远程链接
-方式一: win :redis-cli -h 192.168.200.100 -p 6379 -a 1234 # 最好不要直接把密码一并链接
redis-cli -h 192.168.200.100 -p 6379 # 最好使用这种方式分两次链接
auth 1234
-方式二:resp 远程链接
4.如果连不上,就关闭防火墙
systemctl stop firewalld
systemctl status firewalld
2.客户端连接,常用操作
'客户端连接,常用操作'
有密码的情况可以两种登陆方式
-方式一:redis-cli -h 127.0.0.1 -p 6370 -a 1324
-方式二:先登陆,再通过auth输入密码
ping # 返回PONG,说明联通的
'redis配置文件'
'redis-cli进入'
CONFIG GET * # 390对--》100多键值--》配置信息,例如:
-bind
-port
CONFIG SET maxmemory 128M # 设置最大使用的内存
CONFIG set requirepass 4321 # 设置密码 ---》客户端可以改服务端密码
# 永久生效
CONFIG REWRITE # 保存到配置文件
'''
由于redis的上面的这个操作的rewrite这个命令,再加上linux操作系统可以通过一个端口写文件,导致可以根据redis直接入侵到linux操作系统,
这样如果别人写了一个木马,那就会自动拉起来。
如果写入自己的签名的公钥,用自己的私钥解公钥,自己解自己,所以直接替换公钥,就是通过redis。这也就是入侵提权
可以查看这篇文章如何入侵的:https://cloud.tencent.com/developer/article/1440296
'''
'redis 入侵到 linux操作系统'
避免:
-运行redis进程的用户不要用root
-运行redis的用户不允许登录
三、redis使用场景
1 缓存系统:使用最广泛的就是缓存
2 计数器:网站访问量,转发量,评论数(文章转发,商品销量,单线程模型,不会出现并发问题)
django可以并发操作数据库,并且数据没有错乱,这是因为mysql是一个服务并且支持并发访问的,处理了并发安全的问题,所以不需要我们从程序端考虑并发安全问题
redis它没有锁,但是为什么不存在并发安全问题?因为redis数据读写是单线程的
3 消息队列:发布订阅,阻塞队列实现(简单的分布式,blpop:阻塞队列,生产者消费者)
IPC 进程间通信
具体到项目:遇到问题发通知
-1w个问题,都要发通知
-只要产生问题---》就把问题丢到消息队列中---》另一端起程序,发送消息
4 排行榜:有序集合(阅读排行,点赞排行,推荐(销量高的,推荐))
5 社交网络:很多特效跟社交网络匹配,粉丝数,关注数
6 实时系统:垃圾邮件处理系统,布隆过滤器
四、redis命令(在redis-cli中操作)
1.redis通用命令
'上一篇博客其实是写了redis的命令,不过是在python中操作,这里直接在redis-cli中操作,其实大差不差'
1-keys
#打印出所有key
keys *
#打印出所有以he开头的key
keys he*
#打印出所有以he开头,第三个字母是h到l的范围
keys he[h-l]
#三位长度,以he开头,?表示任意一位
keys he?
#keys命令一般不在生产环境中使用,生产环境key很多,时间复杂度为o(n),用scan命令
2-dbsize 计算key的总数
dbsize #redis内置了计数器,插入删除值该计数器会更改,所以可以在生产环境使用,时间复杂度是o(1)
3-exists key 时间复杂度o(1)
#设置a
set a b
#查看a是否存在
exists a
(integer) 1
#存在返回1 不存在返回0
4-del key 时间复杂度o(1)
删除成功返回1,key不存在返回0
5-expire key seconds 时间复杂度o(1)
expire name 3 #3s 过期
ttl name #查看name还有多长时间过期
persist name #去掉name的过期时间
6-type key 时间复杂度o(1)
type name #查看name类型,返回string
7-其他
info命令:内存,cpu,主从相关
client list 正在连接的会话
client kill ip:端口 # redis-cli -a 654321 client kill 10.0.0.1:64794
dbsize 总共有多少个key
flushall 清空所有
flushdb 只清空当前库
select 数字 选择某个库 总共16个库
monitor 记录操作日志,夯住
redis-cli -a 654321 monitor
2.字符串类型
1---基本使用get,set,del
get name #时间复杂度 o(1)
set name lqz #时间复杂度 o(1)
del name #时间复杂度 o(1)
2---其他使用incr,decr,incrby,decrby
# 如果值不存在,就会创建
incr age #对age这个key的value值自增1
decr age #对age这个key的value值自减1
incrby age 10 #对age这个key的value值增加10
decrby age 10 #对age这个key的value值减10
'统计网站访问量(单线程无竞争,天然适合做计数器)'
3---set,setnx,setxx
set name lqz #不管key是否存在,都设置
setnx name lqz #key不存在时才设置(新增操作)
set name lqz nx #同上
set name lqz xx #key存在,才设置(更新操作)
4---mget mset
mget key1 key2 key3 #批量获取key1,key2.。。时间复杂度o(n)
mset key1 value1 key2 value2 key3 value3 #批量设置时间复杂度o(n)
#n次get和mget的区别
#n次get时间=n次命令时间+n次网络时间
#mget时间=1次网络时间+n次命令时间
5---其他:getset,append,strlen
getset name lqznb #设置新值并返回旧值 时间复杂度o(1)
append name 666 #将value追加到旧的value 时间复杂度o(1)
strlen name #计算字符串长度(注意中文) 时间复杂度o(1)
6---其他:incrybyfloat,getrange,setrange
increbyfloat age 3.5 #为age自增3.5,传负值表示自减 时间复杂度o(1)
getrange key start end #获取字符串制定下标所有的值 时间复杂度o(1)
setrange key index value #从指定index开始设置value值 时间复杂度o(1)
# 缓存
# 计数器
3.hash类型
1---hget,hset,hdel
hget key field #获取hash key对应的field的value 时间复杂度为 o(1)
hset key field value #设置hash key对应的field的value值 时间复杂度为 o(1)
hdel key field #删除hash key对应的field的值 时间复杂度为 o(1)
#测试
hset user:1:info age 23
hget user:1:info age
hset user:1:info name lqz
hgetall user:1:info
hdel user:1:info age
2---hexists,hlen
hexists key field #判断hash key 是否存在field 时间复杂度为 o(1)
hlen key #获取hash key field的数量 时间复杂度为 o(1)
hexists user:1:info name
hlen user:1:info #返回数量
3---hmget,hmset
hmget key field1 field2 ...fieldN #批量获取hash key 的一批field对应的值 时间复杂度是o(n)
hmset key field1 value1 field2 value2 #批量设置hash key的一批field value 时间复杂度是o(n)
4--hgetall,hvals,hkeys
hgetall key #返回hash key 对应的所有field和value 时间复杂度是o(n)
hvals key #返回hash key 对应的所有field的value 时间复杂度是o(n)
hkeys key #返回hash key对应的所有field 时间复杂度是o(n)
'小心使用hgetall'
1 计算网站每个用户主页的访问量
hincrby course:1:info pageview count
2 缓存mysql的信息,直接设置hash格式
其他操作
##其他操作 hsetnx,hincrby,hincrbyfloat
hsetnx key field value #设置hash key对应field的value(如果field已存在,则失败),时间复杂度o(1)
hincrby key field intCounter #hash key 对英的field的value自增intCounter 时间复杂度o(1)
hincrbyfloat key field floatCounter #hincrby 浮点数 时间复杂度o(1)
4.列表类型
1 插入操作
#rpush 从右侧插入
rpush key value1 value2 ...valueN #时间复杂度为o(1~n)
#lpush 从左侧插入
#linsert
linsert key before|after value newValue #从元素value的前或后插入newValue 时间复杂度o(n) ,需要遍历列表
linsert listkey before b java
linsert listkey after b php
2 删除操作
lpop key #从列表左侧弹出一个item 时间复杂度o(1)
rpop key #从列表右侧弹出一个item 时间复杂度o(1)
lrem key count value
#根据count值,从列表中删除所有value相同的项 时间复杂度o(n)
1 count>0 从左到右,删除最多count个value相等的项
2 count<0 从右向左,删除最多 Math.abs(count)个value相等的项
3 count=0 删除所有value相等的项
lrem listkey 0 a #删除列表中所有值a
lrem listkey -1 c #从右侧删除1个c
ltrim key start end #按照索引范围修剪列表 o(n)
ltrim listkey 1 4 #只保留下表1--4的元素
3 查询操作
lrange key start end #包含end获取列表指定索引范围所有item o(n)
lrange listkey 0 2
lrange listkey 1 -1 #获取第一个位置到倒数第一个位置的元素
lindex key index #获取列表指定索引的item o(n)
lindex listkey 0
lindex listkey -1
llen key #获取列表长度
4 修改操作
lset key index newValue #设置列表指定索引值为newValue o(n)
lset listkey 2 ppp #把第二个位置设为ppp
5 实战
实现timeLine功能,时间轴,微博关注的人,按时间轴排列,在列表中放入关注人的微博的即可
6 其他操作
blpop key timeout #lpop的阻塞版,timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)
brpop key timeout #rpop的阻塞版,timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)
#要实现栈的功能 先进后出
lpush+lpop
#实现队列功能
lpush+rpop
#固定大小的列表
lpush+ltrim
#消息队列
lpush+brpop
5.集合类型(Set)
sadd key element # 向集合key添加element(如果element存在,添加失败) o(1)
srem key element # 从集合中的element移除掉 o(1)
scard key # 计算集合大小
sismember key element # 判断element是否在集合中
srandmember key count # 从集合中随机取出count个元素,不会破坏集合中的元素 (抽奖)
spop key # 从集合中随机弹出一个元素
smembers key # 获取集合中所有元素 ,无序,小心使用,会阻塞住
sdiff user:1:follow user:2:follow #计算user:1:follow和user:2:follow的差集
sinter user:1:follow user:2:follow #计算user:1:follow和user:2:follow的交集
sunion user:1:follow user:2:follow #计算user:1:follow和user:2:follow的并集
sdiff|sinter|suion + store destkey... #将差集,交集,并集结果保存在destkey集合中
sdiffstore xxx number1 number2
sinterstore xxx number1 number2
SUNIONSTORE myset myset1 myset2
'总结'
# 实战
抽奖系统 :通过spop来弹出用户的id,活动取消,直接删除
点赞,点踩,喜欢等,用户如果点了赞,就把用户id放到该条记录的集合中
标签:给用户/文章等添加标签,sadd user:1:tags 标签1 标签2 标签3
给标签添加用户,关注该标签的人有哪些
共同好友:集合间的操作
# 总结4
sadd:可以做标签相关
spop/srandmember:可以做随机数相关
sadd/sinter:社交相关
6.有序集合(ZSet)
特点 有一个分值字段,来保证顺序
key score value
user:ranking 1 lqz
user:ranking 99 lqz2
user:ranking 88 lqz3
'集合有序集合'
集合:无重复元素,无序,element
有序集合:无重复元素,有序,element+score
'列表和有序集合'
列表:可以重复,有序,element
有序集合:无重复元素,有序,element+score
'API使用 zset'
zadd key score element # score可以重复,可以多个同时添加,element不能重复 o(logN)
zrem key element # 删除元素,可以多个同时删除 o(1)
zscore key element # 获取元素的分数 o(1)
zincrby key increScore element # 增加或减少元素的分数 o(1)
zcard key # 返回元素总个数 o(1)
zrank key element # 返回element元素的排名(从小到大排)
zrange key 0 -1 # 返回排名,不带分数 o(log(n)+m) n是元素个数,m是要获取的值
zrange player:rank 0 -1 withscores # 返回排名,带分数
zrangebyscore key minScore maxScore # 返回指定分数范围内的升序元素 o(log(n)+m) n是元素个数,m是要获取的值
zrangebyscore user:1:ranking 90 210 withscores # 获取90分到210分的元素
zcount key minScore maxScore # 返回有序集合内在指定分数范围内的个数 o(log(n)+m)
zremrangebyrank key start end # 删除指定排名内的升序元素 o(log(n)+m)
zremrangebyrank user:1:rangking 1 2 # 删除升序排名中1到2的元素
zremrangebyscore key minScore maxScore # 删除指定分数内的升序元素 o(log(n)+m)
zremrangebyscore user:1:ranking 90 210 # 删除分数90到210之间的元素
'其他操作'
zrevrank #从高到低排序
zrevrange #从高到低排序取一定范围
zrevrangebyscore #返回指定分数范围内的降序元素
zinterstore #对两个有序集合交集
zunionstore #对两个有序集合求并集
# 实战
排行榜:音乐排行榜,销售榜,关注榜,游戏排行榜
五、慢查询(排查redis慢的问题)
# MySQL 中的 7 种日志介绍
# 生命周期
客户端编写命令 get name---》通过网络 ---》到服务端---》服务端执行命令【查询,修改。。】---》返回数据通过网络返回给客户端
我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询.
慢查询发生在第三阶段
客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
# 两个配置 配置慢查询
-只要是超过慢查询时间的命令,都会被记录
-后期通过记录分析,哪些命令是慢的,尽量在生成环境中避免
-慢查询是一个队列,里面记录了,超过你设定时间的命令
- 通过两个配置:
slowlog-log-slower-than 慢于多少微秒的都会被记录
slowlog-max-len 队列长度是多少
-配置文件直接配置
# 设置记录所有命令
config set slowlog-log-slower-than 0
# 最多记录100条
config set slowlog-max-len 100
# 持久化到本地配置文件
config rewrite
-查看慢查询队列
slowlog get 100
slowlog len #获取慢查询队列长度
slowlog reset #清空慢查询队列
# 总结:
开启慢查询记录后---》只要超过某个时间的命令,都会被记录到慢查询队列中
后期我们可以通过慢查询队列中的命令,优化程序,提高redis使用效率
六、pipline(管道)与事务
'redis 其实不支持事务,但是可以通过pipline来模拟事务,pipline只支持单实例redis,如果做集群,没有pipiline'
redis 支持事务吗?
不支持,可以通过pipline实现,但是集群环境不能用pipline
'python客户端实现'
import redis
pool = redis.ConnectionPool(host='10.0.0.111', port=6379)
r = redis.Redis(connection_pool=pool)
# pipe = r.pipeline(transaction=False)
#创建pipeline
pipe = r.pipeline(transaction=True)
#开启事务
pipe.multi()
pipe.set('name', 'lqz')
#其他代码,可能出异常
pipe.set('role', 'nb')
pipe.execute()
'原生redis操作,模拟事务'
0 开启两个客户端
在第一个客户端执行 mutil 开启事务,放到管道中一次性执行
multi # 开启事务
set name lqz
set age 18
exec
在第二个客户端去查询,如果第一个客户端没有执行exec ,查询不到第一个事务未提交的数据
'隔离级别---》读已提交级别'
'原生redis 通过watch+pipline(multi) 模拟乐观锁'
0 开启两个客户端
1 第一个客户端上 在开启事务之前,先watch
wathc age # 看了一眼,是10
multi
decr age # 它有可能被别人操作了
exec
另一台机器
multi
decr age
exec # 先执行,上面的执行就会失败(乐观锁,被wathc的事务不会执行成功)
'集成到python项目中实现基于redis利用redis的乐观锁,实现秒杀系统的数据同步(基于watch实现),'
import redis
from threading import Thread
def choose(name, conn):
# conn.set('count',10)
with conn.pipeline() as pipe:
# 先监视,自己的值没有被修改过
conn.watch('count')
# 事务开始
pipe.multi()
old_count = conn.get('count')
count = int(old_count)
# input('我考虑一下')
# time.sleep(random.randint(1, 2))
if count > 0: # 有库存
pipe.set('count', count - 1)
# 执行,把所有命令一次性推送过去
ret = pipe.execute()
print(ret)
if len(ret) > 0:
print('第%s个人抢购成功' % name)
else:
print('第%s个人抢购失败' % name)
if __name__ == '__main__':
conn = redis.Redis(host='10.0.0.111', port=6379,password='654321')
for i in range(100):
t = Thread(target=choose, args=(i, conn))
t.start()
七、发布订阅
'发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型(后订阅了,无法获取历史消息)'
如果是消息队列
生产者生产了一条消息---》只会有一个消费者消费
如果是发布定义---》发布订阅---》观察者模式
生产者生产了一条消息---》所有订阅生产者的消费者都会收到消息
实际操作
-发布者发布消息
publish channel01 "hello world"
-订阅者01订阅频道 channel01
subscribe channel01
-订阅者02订阅频道 channel01
subscribe channel01
实际用途
-只要设计到,一个人发生变化,其他人都收到通知的情况,就可以使用发布订阅
-如何用,在python中?
员工1 ,员工2 关注了 张三
张三发送一篇文章---》文章保存到数据库了---》信号绑定一个函数--》在函数中发布消息
conn.publish('user_zhangsan_new_article','文章id')
启动一个程序---》等待别人发布消息---》只要别人发布了消息---》取出频道--》去mysql查看哪些员工订阅了这个频道---[员工1 ,员工2]--->发邮件通知
八、bitmap位图
操作比特位
c i g
01100011 01101001 01100111
set hello big #放入key位hello 值为big的字符串
getbit hello 0 #取位图的第0个位置,返回0
getbit hello 1 #取位图的第1个位置,返回1 如上图
我们可以直接操纵位
setbit key offset value #给位图指定索引设置值
setbit hello 7 1 #把hello的第7个位置设为1 这样,big就变成了cig
独立用户统计---》统计日活---》用户量足够大--》节约内存
-10亿用户 用户id1 2 3 10亿
-统计日活,只要用户登录,只要用户登录,就把用户id放到集合中
-晚上只需要统计一下 集合大小---》就能统计出日活
-使用集合存储---》1--》32位--》1亿用户 5千万左右---》需要 200MB空间
int8个比特位表示范围 -128--127之间
int16
int32 个比特位表示范围 -2的31次方---2的31次方 2147483648
-使用位图---》12.5MB空间
九、HyperLogLog
基于HyperLogLog算法:极小的空间完成独立数量统计,极小内存实现去重
- 爬虫去重
- 黑白名单
- 垃圾邮件过滤
1.pfadd key element # 向hyperloglog添加元素,可以同时添加多个
pfadd urls "www.baidu.com" "www.cnblogs.com" "www.lqz.com"
pfadd uuids "uuid1" "uuid2"
2.pfcount key # 计算hyperloglog的独立总数
pfcount urls # 查询统计个数
3.pfadd urls 值 # 返回0表示在,返回1 表示不在
pfadd urls "www.baidu.com"
4.pfmerge 新元素名 key1 key2
pfadd uuids1 "uuid1" "uuid2" "uuid3" "uuid4"
pfadd uuids2 "uuid3" "uuid4" "uuid5" "uuid6"
pfmerge uuidsall uuids1 uuids2 #合并
pfcount uuidsall #统计个数 返回6
总结
百万级别独立用户统计(用户id可以不是数字),万条数据只占15k
错误率 0.81%
无法取出单条数据,只能统计个数
十、GEO
测试文件准备,5个城市纬度
城市 | 经度 | 纬度 | 简称 |
---|---|---|---|
北京 | 116.28 | 39.55 | beijing |
天津 | 117.12 | 39.08 | tianjin |
石家庄 | 114.29 | 38.02 | shijiazhuang |
唐山 | 118.01 | 39.38 | tangshan |
保定 | 115.29 | 38.51 | baoding |
GEO(地理信息定位):存储经纬度,计算两地距离,范围等
geoadd key longitude latitude member #增加地理位置信息
geoadd cities:locations 116.28 39.55 beijing #把北京地理信息天津到cities:locations中
geoadd cities:locations 117.12 39.08 tianjin
geoadd cities:locations 114.29 38.02 shijiazhuang
geoadd cities:locations 118.01 39.38 tangshan
geoadd cities:locations 115.29 38.51 baoding
geopos key member #获取地理位置信息
geopos cities:locations beijing #获取北京地理信息
geodist key member1 member2 [unit]#获取两个地理位置的距离 unit:m(米) km(千米) mi(英里) ft(尺)
geodist cities:locations beijing tianjin km #北京到天津的距离,89公里
georadius key logitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
#获取指定位置范围内的地理位置信息集合
'''
withcoord:返回结果中包含经纬度
withdist:返回结果中包含距离中心节点位置
withhash:返回解雇中包含geohash
COUNT count:指定返回结果的数量
asc|desc:返回结果按照距离中心店的距离做升序/降序排列
store key:将返回结果的地理位置信息保存到指定键
storedist key:将返回结果距离中心点的距离保存到指定键
'''
georadiusbymember cities:locations beijing 150 km
'''
1) "beijing"
2) "tianjin"
3) "tangshan"
4) "baoding"
'''
geo的本质是zset类型(有序集合),因此可以使用zset的删除,删除指定的geo例如 member: zerm cities:locations beijing
十一、持久化
1.介绍
'把redis数据从内存保存到硬盘上的过程称之为持久化'
把数据保存在硬盘上永久存储 过程叫持久化
'所有的数据库,持久化方案'
快照:某时某刻数据的一个完成备份
-mysql的Dump: mysqldump -uroot -p123456 -luffy >/data/mysqlDump/mydb.sql
-redis的RDB:
写日志:任何操作记录日志,要恢复数据,只要把日志重新走一遍即可
-mysql的 Binlog
-Redis的 AOF
'redis 主要的两种持久化方案'
- rdb:快照方案
- aof:日志方案
2.RDB持久化
'三种方案触发rdb'
方案一:
同步方案,在redis-cli中敲save命令,就会在对应的redis文件夹下的data文件夹下生成一个dump.rdb,
这样,如果关闭redis(shutdown),后重启数据也会存在,这样就做到了持久化。
'注意:如果数据量很大,执行save命令就会阻塞redis,会导致其他命令都执行不了'
reids客户端敲一个命令:save
方案二:
异步方案:在redis-cli中敲bgsave命令,同上面方法一样,但是它是一个异步方式,不会阻塞redis
'因为在执行bgsave命令的时候,它开启了一个异步线程,进行执行持久化操作,所以不会阻塞redis'
redis客户端敲一个命令:bgsave
方案三:
配置文件方案,只要符合条件,会自动生成rdb文件
save 900 1
save 300 10
save 60 10000
如果60s中改变了1w条数据,自动生成rdb
如果300s中改变了10条数据,自动生成rdb
如果900s中改变了1条数据,自动生成rdb
'方案三演示:'
save 60 2 # 60s内改了2条数据,就做rdb的持久化
dbfilename dump-6379.rdb # 以端口号作为文件名,可能一台机器上很多reids,不会乱
dir /bigdiskpath # 保存路径放到一个大硬盘位置目录
stop-writes-on-bgsave-error yes # 出现错误停止
rdbcompression yes # 压缩
rdbchecksum yes # 校验
'只要在工作目录下有rdb文件,redis重启,就会加载,整个load到内存中'
但是RDB做持久化,比较耗时、耗性能,并且不可控,不小心删掉就没有了,可能会丢失数据
3.AOF持久化
AOF的三种策略
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每秒一次fsync,丢失1秒数据 | 不用管 |
缺点 | IO开销大,一般的sata盘只有几百TPS | 丢1秒数据 | 不可控 |
'AOF方案:客户端每写入一条命令,都记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复'
-AOF的三种策略
日志不是直接写到硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到硬盘上
always:redis–》写命令刷新的缓冲区—》每条命令fsync到硬盘—》AOF文件
everysec(默认值):redis——》写命令刷新的缓冲区—》每秒把缓冲区fsync到硬盘–》AOF文件
no:redis——》写命令刷新的缓冲区—》操作系统决定,缓冲区fsync到硬盘–》AOF文件
AOF重写
随着命令的逐步写入,并发量的变大, AOF文件会越来越大,通过AOF重写来解决该问题
'未重写' '重写'
set hello world
set hello java set hello hehe
set hello hehe
incr counter
incr counter ======>>> incryby counter 2
rpush mylist a
rpush mylist b rpush myslist a b c
rpush mylist c
过期数据
'本质就是把过期的,无用的,重复的,可以优化的命令,来优化 这样可以减少磁盘占用量,加速恢复速度'
咱们只需要做好配置,触发aof重写后,redis会自动开启aof重写,优化 日志
-配置
uto-aof-rewrite-min-size AOF文件重写需要尺寸
auto-aof-rewrite-percentage AOF文件增长率
AOF持久化方案(配置方案)
'aof和rdb是能够同时开启的,不会有任何影响,aof是写日志,rdb是备份文件'
# 在redis.cconf 配置中添加
appendonly yes #将该选项设置为yes,打开
appendfilename "appendonly-6379.aof" #文件保存的名字
appendfsync everysec #采用第二种策略
no-appendfsync-on-rewrite yes #在aof重写的时候,是否要做aof的append操作,因为aof重写消耗性能,磁盘消耗,正常aof写磁盘有一定的冲突,这段期间的数据,允许丢失
tail -f appendonly-6379.aof.1.incr.aof # 实时查看
'那么具体使用rdb还是aof?'
这个取决于公司,如果对数据要求高,不允许丢失,那就必须选择AOF。如果允许丢失,就选择RDB
4.混合持久化
混合持久化原理:在
开启混合持久化
的情况下,AOF 重写时会把 Redis 的持久化数据
,以RDB 的格式写入到 AOF 文件的开头
,之后的数据再以AOF 的格式化追加的文件的末尾
。它的目的就是为了加快恢复
'目的是为了加快恢复'
配置方案:基于AOF的,所以需要开AOF
appendonly yes
appendfilename "appendonly-6379.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
aof-use-rdb-preamble yes # 开启了混合持久化,并不需要开启rdb也会有rdb文件
5.RDB和AOF比较
命令 | rdb | aof |
---|---|---|
启动优先级 | 低 | 高(挂掉重启,会加载aof的数据) |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
轻重 | 重 | 轻 |
rdb最佳策略
rdb关掉,主从操作时
集中管理:按天,按小时备份数据
主从配置,从节点打开
aof最佳策略
开:缓存和存储,大部分情况都打开,
aof重写集中管理
everysec:通过每秒刷新的策略
最佳策略
小分片:每个redis的最大内存为4g
缓存或存储:根据特性,使用不通策略
时时监控硬盘,内存,负载网络等
有足够内存