编程-知识星球
2020-04-17 17:12:58 0 举报
AI智能生成
编程(主要JAVA相关的学习 目前更新redis系列)
作者其他创作
大纲/内容
JAVA
redis学习
redis安装
直接编译安装(推荐)
gcc的准备(有了 略过)
yml install gcc-c++
下载安装
wget 官网下载链接
解压缩
tar -zxvf xxx.tar.gz
进入该目录
cd xxxx
编译安装
make && make install
redis.conf的配置
安全模式的修改
密码的修改
bind那行注释掉
若是port不是6379 需自行修改
设置以守护进程的方式启动redis
设置daemonize yes
启动redis
redis-server redis.conf
redis-cli -a 密码
端口查看
netstat -anpt
ps -ef|grep redis
杀死端口
kill -9 pid
使用docker
下载版本 运行(名字 端口 密码)
宿主机
redis-cli -a 密码
非宿主机
docker exec -it 名字 redis-cli -a 密码
直接安装
centos
yum install redis
ubuntu
apt-get install redis
mac
brew install redis
在线体验
补充
只是为了学习 并且懒的动手安装的 可以使用windows版
为了查看数据方便 直观 可下载redis可视化工具
redis五种数据类型
string
前言
最简单的一种数据类型
在redis中 所有key都是字符串
我们只是对value 进行相应的划分
redis字符串 --> 动态字符串 可修改 扩容 减少频繁的分配
常用命令
set系列
set
给key赋值
mset
批量存储
setnx
在key不存在时 操作 还有xx 与之相反
msetnx
set命令会覆盖原本内容
而setnx 必须得为空 才可
批量setnx
setrange
从某个索引/index 开始覆盖 不够在扩展
get系列
get
获取key的value
getrange
可以提取value中部分/全部内容(起始,终止位置)
getset
先获取 在更新key
mget
批量获取
get —> 查询 无修改权限
append
key存在
在对应的value后追加值
key不存在
创建心的键值对
值加减操作
减
decr
对value 减1
value is 数字
正常使用
value 不存在
默认设置为0 在减1
decrby
在decr基础上多了步长
加
incr
给key中value 自增1
incrby
给key中的value 自增(自己设置的步长)
incrbyfloat
和incrby类似 不过步长可设为浮点数
strlen
查询字符长度
日期相关
ttl
查询key的有效期
setex
设置key中value 的同时 设置过期时间
psetex
和setex类似 它是毫秒级操作
BIT命令
前言
redis中 字符串以二进制方式来存储的 BIT相关命令就是对二进制进行操作的
命令
getbit
获取bit上某个位置的bit值(0或1)
setbit
修改bit上某个位置的bit值
bitcount
统计二进制数据中1的总数
list
入栈
lpush
key不存在
创建一个空列表
在进行push操作
key存在
将所有指定值插入列表头部
rpush
列表尾部插入所有指定的值
lrange
返回列表指定区间内的元素
出栈
lpop
移除 返回列表头元素
bloop
阻塞式的lpop
rpop
移除 返回列表尾元素
lindex
返回列表中 下标为index元素
ltrim
对列表进行修剪
llen
返回列表 key 的长度
LREM
根据参数 count 的值,移除列表中与参数 value 相等的元素
set
sadd
添加一个/多个元素 到集合中
查询
smembers
获取一个key下所有元素
sismember
返回某个成员是否存在集合中
srem
移除指定元素
scard
返回集合的数量
srandmember
随机返回一个元素
spop
随机出栈 返回一个元素
smove
讲一个元素从一个集合移动到另一个集合中去
差集
sdiff
差集
sdiffstore
将差集保存一个新的集合中
交集
sinterstore
并集
sunion
sunionstore
结果保存到新的集合中
hash
前言
key(字符串) value(key/value键值对)
类似 key --> map
常用命令
设置
hset
添加值
hmset
批量设置
hsetnx
默认字段存在 覆盖 现在字段为空 该命令执行
查询
hget
获取值
hvals
获取所有的value
hkeys
查询所有的key
hgetall
获取所有的key和value
hdel
删除指定字段
hexists
返回字段是否存在
增减操作
hincrby
value自增
hincrbyfloat
自增一个浮点数
长度
hlen
返回 某个key中value的数量
hstrlen
返回某个key中某个字段的字符串的长度
zset
命令
zadd
添加指定元素到有序集合中
zscore
见名知意
返回member的score
顺序
zrange
返回集合中的一组元素
zrevrange
返回一组倒置元素
数量
zcard
返回元素个数
zcount
返回score在某一个区间内的成员数量
zlexcount
计算有序集合中的成员数量
zrangebyscore
按照score范围返回值
zrangebylex
返回规定区间中的成员
元素排名
从小到大
zrank
从大到小
zrevrank
zincrby
score自增
zinterstore
求交集
zrem
推出一个元素
说明
既然zset有key score member这些成分 那么围绕它们展开方法 做到心中有数
补充
通用key
del
删除一个key/value
dump
序列化
exists
判断key是否存在
日期
ttl
查看key的有效期
pttl
毫秒级别ttl
expire
给key设置有效期
若在key过期之前重新赋值 过期时间会失效
persist
移除key的过期时间
keys *
查看所有key
针对四种数据类型(list/hash/set/zset)
初次使用 容器不存在 会自动创建
若是容器内空元素 自动删除容器 释放内存
redis的基本应用
分布式锁
分布式锁问题
问题(与解决版本相对应)
v1
比如用户的一个简单操作 一个线程 从数据库中读取数据 --> 然后在内存中修改 --> 修改后 重新写入数据库
单线程没问题 但是在多线程呢(读 改 写 非原子操作)
v2
但是在v1中 若是中途出现异常 比如阻塞/直接就凉了 del无法释放 锁得不到释放 外面的线程进不来 里面的线程出不去
v3
由于不是原子性操作 假如在setnx和expire设置过期之间出现异常 这时过期还没生效 异常出现了 该怎么处理呢
v4
虽然我们为每个锁设置了超时时间 但是还是会有问题
假如A处理业务耗时长 当它还没有处理完成 A锁被释放了 这是B进入了 执行中 A处理完成了 释放了B锁 C进来了 B释放C锁 C未处理完 一环套一环 出现了混乱
待续。。
分布式锁解决思路
解决
思路
v1(无异常情况下)
先进去一个线程占位 让其他进来的线程 在外面等/放弃
用什么指令呢
setnx 执行完成后 del释放key
v2
需要添加一个过期时间 哪怕出错 过时就释放(在expire设置过期时间)
v3
既然setnx和expire会出现问题 那么redis有没有合并的api呢
redis有一个set(key,value,new SetParams().nx().ex(过期时间))
v4
1 尽量减少业务量耗时长的业务在锁中操作
2 针对锁value设置成随机字符串 每次释放锁时 需要比对随机字符串是否一致 若一致在释放 否则不释放
由于读 改 存 非原子性操作 引入lua脚本
lua脚本的好处
redis内置的 且有很多好用的包 值得拥有
lua脚本可以执行多个reids命令
多个命令一次执行 解决网络给redis带来的性能问题
lua脚本使用思路
1 在redis服务端写好lua脚本 然后客户端去调用脚本(推荐)
创建.lua脚本 会用到call eval ARGV。。。判断。。。 (将释放操作放入lua脚本中)以及将lua脚本合并到redis上的命令 script load 生成校验码
客户端针对sha校验码以及需要的参数API --> evalsha 调用lua脚本
2 客户端编写脚本 每次需要执行时 在将脚本发送到reids上去执行
待续。。
redis做消息队列
介绍
常用的消息队列(专业 多功能)
RabbitMQ
RocketMQ
ActiveMQ
大数据里的kafka
redis消息队列
不专业
不需要非常专业的消息中间件
无太多高级特性
对于消息可靠性要求高的 别考虑
适合简单场景
简单应用
消息队列
确定数据结构
初步确定
用list数据结构实现
出栈/入栈
lpush/rpush
lpop/rpop
实现
在客户端设置/维护一个死循环 可以不断的读取消息 若是没有消息 陷入死循环 但这样太浪费资源了
更改
可以使用blpop/brpop
延迟消息队列
前言
延时 --> 需要时间 还有相对应的值 用zset的score(时间) value存进redis 持续的读取消息(轮询)
消息处理
字符串直接发送
对象 需要对其进行序列化
导入json(序列化和反序列化)
思路
定义一个消息类
定义消息id 内容等
定义一个消息处理类
入队(生产消息)
将消息加进去 首先对消息赋值(实例)好后 序列化对象 zset --> key 时间(System.currentTimeMillis() + 6000 延时设置)+value 后台打印一下日志
出队(消费消息 )
当线程不中断时 消息循环
利用zrangeByScore 得到一个成员
对得到的成员进行判断
为空
比如休眠600ms 在去查看队列里是否有值
存在
读取消息(比如xxx.iterator().next()读取) 并且将其移除消息队列(zrem命令)
zrem命令
ZREM key member [member ...]
移除成功1个 返回1 假如是n个 返回n
移除成功1个 返回1 假如是n个 返回n
若是>0
将拿到的消息反序列化
打印当然日期 一会好比对
定一个调用类
调用消息处理类 创建实例
new 2个线程(提供者 --> 入栈 消费者 --> 出栈) 在run提供数据 调用数据
运行2个线程
比如6s的延时是否生效
若是要中断消费者
运行结束后 等待一会 释放完了
开启中断
interrupted
HyperLogLog
举例
网站访客
存储一个IPv4 ip地址(最多15个字节) 假如用户越来越多唯一IP用户越来越多
存储IP的问题
访问量
主要参数
网页浏览量 pv(page view)
用户访问 (uv user view)
统计
第三方工具
cnzz
友盟
。。。
自己实现
pv
redis计数器实现
思路
前端
为每个用户生成一个唯一id(登陆/未登录的用户都统计) id伴随请求发送到后端
后端
接收到请求
通过set中sadd命令存储id
scard统计集合大小
uv
去重
存储问题
精度需求不是特别的精准
主角HyperLogLog
简介
用来解决这个问题的
不怎么精确 有误差(0.81%) 不过够用
优点
拥有去重方案
所需内存不会因为集合的大小而改变(内存开销是固定的)
数据量少时 察觉不到误差
命令
pfadd
类似sadd
添加时 遇到重复记录自动去重
pfcount
统计数据(返回集合的近似基数)
pfmerge
合并多个统计结果 自动去重多集合中的重复元素(返回并集)
代码简单实现
遍历pfadd添加 pfcount统计
布隆过滤器
场景
HyperLogLog
有偏差
pfadd
pfcount
pfmerge
HyperLogLog的局限性
假设昨天我浏览的都是吃的 今天我在去看 希望看到的是 你给我推送类似的 但是不要重复太多 对于我来说 和昨天重复太多 第一个这家可能资本不雄厚 内容重复率太高 第二 体验差 毕竟现在人耐心是越来那啥了 那么如何推送内容去重呢
思路
效率低(不推荐)
根据用户昨天的浏览记录 在每次推送前先比对内容(判断是否用户之前已经看过了) 经过筛选后 将新内容推送给用户
救火员
Bloom Filter
介绍
优点
可以解决去重问题
缺点
不太精确
类比
set集合(不太精确的那种) 利用自带的包含方法去对比某个对象是否存在
值不存在 肯定不存在
值存在 不一定存在
举例
假设用户浏览记录为a 现在将推送消息 先在a中判断是否推送过
没推送过(a里面无这些记录)
那就一定没推送过
推送过
a可能没有这条消息 虽然概率极低
好比你相亲 家里的媒人(题外话 就会先打听一下 家庭等情况 当然没有这事 我最喜欢了) 以及问家里人之前相亲的历史记录 媒人根据手头的资源 先比对是否之前相亲过
未相亲过
肯定没相亲过 嗯 可以推送一波
相亲过
就不会推送给你(可能浏览记录不存在 错过了 但是概率极低 毕竟老江湖)
原理
一个BIT数组(数据 --> 下标快速找数据) 以及不同的hash函数
add操作
通过hash函数给元素提供(hash运算)一个整数索引值
拿到索引值 对数组长度进行取模
得到位置后 设置为1
现在就可以解释存在与否的问题了
过程
将值hello(先对元素(hash运算) 得出1 2 3)和数组长度取模(最后结果 1 2 3) 并对每个生成的哈希值指向的 bit 位置 设为1
现在你是有一栋楼的主人 出租给租客 现在有3个人住进来了 并给了他们磁卡
判断是否存在
存在(为啥不一定存在)
现在呢又来一个值xixi 最终它的哈希函数返回1 4 6
二房东将其中一套之前租给别人的房 租给后来的人
之前的是1 2 3 那么1被覆盖了
现在的主人(使用权 别较真呀 说我可以强行收回啥的 方便理解而已 毕竟我没有房子 不知道什么是真香)是别人了
这时 我想判断hello是否存在 根据位置 肯定存在的 但是内容变了
登陆楼层管理系统 查了一下记录 这栋楼的某某室 有人住 说明有人 虽然这个房子住着人 但是这个房子租客 换成了别人
不存在
现在查询值 它的哈希函数返回2 4 6 2为0 那么肯定不存在
现在查询该栋某室 未住人 空房间肯定没人
关系
误判的概率与位数组的大小成反比 位数组越大 误判概率越小 占用内存越大 反之
你的拥有的房的栋数越多 住的下人越多 被覆盖的可能性更小 算了 洗洗睡吧
安装方式
docker
直接安装
代码
bf.reserve key 0.0001 1000000
若我们未自行定义参数的话 该命令在我们使用bf.add 命令时自动创建的默认参数
参数介绍
三个参数从左往右依次对应
key
键
error_rate
期望错误率 期望错误率越低 占用空间就越大 反之
capacity
初始容量(预计数量) 当实际的数量超过预计容量时 错误率升高
典型场景
概念区分
缓存击穿
缓存和数据库都没有这个数据 用户不断请求
缓存穿透
缓存中没有数据 但数据库中有的数据(一般是缓存时间到期)
雪崩
在缓存中数据大批量到过期时间 而查询数据量巨大 引起数据库崩溃
缓存击穿
缓存流程
假设有上亿条数据 查询数据库 效率低且数据库的压力 响应速度估计。。。缓存处理流程 前台请求 后台先从缓存中取数据 取到直接返回结果 取不到时从数据库中取 数据库取到更新缓存 并返回结果 数据库也没取到 那直接返回空结果
针对缓存
我的数据不通过缓存(恶意请求 缓存肯定没有的数据 比如携带很多虚假(不存在)的用户) redis无法拦截 直接访问数据库 可能导致雪崩
解决
针对过来的请求 先判断数据是否存在 若存在 才允许去数据库中查询 否则就不去数据库中查询 使用布隆过滤器 就很合适
0 条评论
下一页