redis
1 redis 安装和配置
# redis 是什么
开源:基于c编写的,早起版本2w3千行
基于键值对的存储系统:字典形式
多种数据结构:字符串,hash,列表,集合,有序集合
高性能,功能丰富
#Redis特性(8个)
1 速度快:10w ops(每秒10w读写),数据存在内存中,c语言实现,数据单线程模型
2 持久化:rdb和aof
3 多种数据结构:
4 5大数据结构
BitMaps位图:布隆过滤器 本质是 字符串
HyperLogLog:超小内存唯一值计数,12kb HyperLogLog 本质是 字符串
GEO:地理信息定位 本质是有序集合
5 支持多种编程语言:基于tcp通信协议,各大编程语言都支持
6 功能丰富:发布订阅(消息) 事务(pipeline)
简单:源代码几万行,不依赖外部库
7 主从复制和高可用
主从复制:主服务器和从服务器,主服务器可以同步到从服务器中
高可用和分布式
2 源码安装
# 使用源码包
wget https://download.redis.io/redis-stable.tar.gz
# 解压
tar -xzvf redis-stable.tar.gz
# 改名
mv redis-stable/ redis
cd redis
make && make install
# /usr/local/bin/ 路径下会有可执行文件
# src目录下,就会有几个可执行文件
#redis-server--->redis服务器
#redis-cli---》redis命令行客户端
#redis-sentinel---》sentinel服务器,哨兵
#redis-benchmark---》redis性能测试工具
#redis-check-aof--->aof文件修复工具
#redis-check-dump---》rdb文件检查工具
mv redis.conf redis.conf.bak
vi redis.conf
# 修改配置文件启动
daemonize yes
pidfile /var/run/redis.pid
port 6379
dir ./data
logfile 6379.log
# 创建文件夹
mkdir data
# 启动
./bin/redis-server ./etc/redis.conf # 数据目录data在当前目录下
# 查看redis版本
redis-cli -v
redis-cli 7.2.5
3 redis-stack
# 下载地址:https://redis.io/downloads/ # centos的
wget https://packages.redis.io/redis-stack/redis-stack-server-7.2.0-v11.rhel7.x86_64.tar.gz
# 解压即用
tar -xzvf redis-stack-server-7.2.0-v11.rhel7.x86_64.tar.gz
# 文件夹重命名
mv redis-stack-server-7.2.0-v11 redis-stack-server
cd etc/
vi redis.conf
# 修改配置文件启动
daemonize yes
pidfile /var/run/redis1.pid
port 6380
dir ./data
logfile 6380.log
# 任意路径下敲
redis-server # 启动redis
# 查看redis版本
./bin/redis-cli -v
4 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 user: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 feld floatCounter #hincrby 浮点数 时间复杂度o(1)
5 列表类型
###插入操作###
#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
######删除操作#########
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的元素
############查询操作############
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 #获取列表长度
############修改操作############
lset key index newValue #设置列表指定索引值为newValue o(n)
lset listkey 2 ppp #把第二个位置设为ppp
############实战############
实现timeLine功能,时间轴:微博关注的人,按时间轴排列,在列表中放入关注人的微博的即可
############其他操作############
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
6 集合操作
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 另一个集合名字 boys girls
sinterstore 另一个集合名字 boys girls
#### 集合可以做什么
1 抽奖系统 :通过spop来弹出用户的id,活动取消,直接删除
2 点赞,点踩,喜欢等,用户如果点了赞,就把用户id放到该条记录的集合中
3 标签:给用户/文章等添加标签,sadd user:1:tags 标签1 标签2 标签3
给标签添加用户,关注该标签的人有哪些
4 共同好友:集合间的操作
#### 总结####
sadd:可以做标签相关
spop/srandmember:可以做随机数相关
sadd/sinter:社交相关
7 有序集合
#有一个分值字段,来保证顺序
#集合有序集合
集合:无重复元素,无序,element
有序集合:无重复元素,有序,element+score
#列表和有序集合
列表:可以重复,有序,element
有序集合:无重复元素,有序,element+score
## API使用
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 #对两个有序集合求并集
8 慢查询
# 我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询
# 慢查询是一个先进先出的队列,固定长度,保存在内存中--->通过设置慢查询,以后超过我们设置时间的命令,就会放在这个队列中
# 后期我们通过查询这个队列,过滤出 慢命令--》优化慢命令
# 设置记录所有命令
config set slowlog-log-slower-than 0
# 最多记录100条
config set slowlog-max-len 100
# 持久化到本地配置文件
config rewrite
# 查看慢查询队列
slowlog get [n] #获取慢查询队列
'''
日志由4个属性组成:
1)日志的标识id
2)发生的时间戳
3)命令耗时
4)执行的命令和参数
'''
slowlog len #获取慢查询队列长度
slowlog reset #清空慢查询队列
9 发布订阅
# 发布订阅(观察者模式):发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型
# 原生实现
-发布消息:
publish channel message
-订阅
subscribe channel
-接收消息
-只要发布者一发布,订阅者都会受到
python+redis实现
import redis
# 连接到Redis服务器
r = redis.Redis(host='localhost', port=6379, db=0)
# 订阅者
sub = r.pubsub()
sub.subscribe('channel')
for message in sub.listen():
print(message)
import redis
# 连接到Redis服务器
r = redis.Redis(host='localhost', port=6379, db=0)
# 发布者
r.publish('channel', 'message')
10 bitmap位图
#本质是字符串
#操作比特位
b 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
# 获取前3位中1的个数
bitcount name 0 3
11 hyperloglog
# 基于HyperLogLog算法:极小的空间完成独立数量统计,本质还是字符串
# 使用
-放值:pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4"
-统计个数:pfcount uuids
-判断一个值是否在里面:pfadd uuids "uuid1"
# 用处
-统计日活用户
-去重:爬过的网址,就不爬了--》存一下爬过的
-黑白名单
# 注意
-百万级别独立用户统计,百万条数据只占15k
-错误率 0.81%
-无法取出单条数据,只能统计个数
12 geo
# geo 地理位置信息
-GEO(地理信息定位):存储经纬度,计算两地距离,范围等
# js获取经纬度
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
console.log("经度:" + longitude);
console.log("纬度:" + latitude);
});
} else {
console.log("浏览器不支持Geolocation API");
}
# 增加地理位置信息
geoadd key longitude latitude member
# 把北京地理信息天津到cities:locations中
geoadd cities:locations 116.28 39.55 beijing
# 获取天津的经纬度 geopos key member #获取地理位置信息
geopos cities:locations tianjin
# 根据经纬度--》文字
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="my_application")
def geolocate_point(latitude, longitude):
location = geolocator.reverse(f"{latitude}, {longitude}")
return location.address
# 统计两个地理位置之间的距离
geodist cities:locations beijing tianjin km
# 统计某个地理位置方圆xx公里,有xx
georadiusbymember cities:locations beijing 150 km
13 持久化
# 什么是持久化
redis的所有数据保存在内存中,对数据的更新将异步的保存到硬盘上
# 持久化的实现方式
快照:某时某刻数据的一个完成备份,
-mysql的Dump:写一个mysql自动定时备份和清理前后端程序
-redis的RDB:某一刻,把内存中得数据,保存到硬盘上这个操作就是rbd的持久化
写日志:任何操作记录日志,要恢复数据,只要把日志重新走一遍即可
-mysql的 Binlog
-Redis的 AOF
rdb
# rdb方案 :三种方式
1 人工同步:客户端 : save # 同步操作,会阻塞其他命令--》单线程架构
2 人工异步:客户端:bgsave # 异步操作,不会阻塞其他命令
3 配置文件
save 900 1 #配置一条
save 300 10 #配置一条
save 60 10000 #配置一条
save 60 5
# 最佳配置
save 900 1
save 300 10
save 60 10000
dbfilename dump-6379.rdb #以端口号作为文件名,可能一台机器上很多reids,不会乱
dir ./bigdiskpath #保存路径放到一个大硬盘位置目录
stop-writes-on-bgsave-error yes #出现错误停止
rdbcompression yes #压缩
rdbchecksum yes #校验
# 确定 RDB问题
耗时,耗性能:
不可控,可能会丢失数据
aof
# 客户端每写入一条命令,都记录一条日志,放到日志文件中,如果出现宕机,可以将数据完全恢复
# AOF的三种策略
日志不是直接写到硬盘上,而是先放在缓冲区,缓冲区根据一些策略,写到硬盘上
always:redis–》写命令刷新的缓冲区—》每条命令fsync到硬盘—》AOF文件
everysec(默认值):redis——》写命令刷新的缓冲区—》每秒把缓冲区fsync到硬盘–》AOF文件
no:redis——》写命令刷新的缓冲区—》操作系统决定,缓冲区fsync到硬盘–》AOF文件
# 配置文件
appendonly yes #将该选项设置为yes,打开
appendfilename "appendonly-${port}.aof" #文件保存的名字
appendfsync everysec #采用第二种策略
dir /bigdiskpath #存放的路径
no-appendfsync-on-rewrite yes
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite yes
# 放在了文件中
appendonly.aof.1.base.rdb :永久的
appendonly.aof.1.incr.aof :临时的
appendonly.aof.manifest: 哪些文件存了数据