高校网站建设目的,企业宣传图片模板,wordpress 分类 过滤,做网站的软件有哪些一 Redis概述
1 为什么要用NoSQL 单机Mysql的美好年代
在90年代#xff0c;一个网站的访问量一般都不大#xff0c;用单个数据库完全可以 轻松应付。在那个时候#xff0c;更多的都是静态网页#xff0c;动态交互类型的网站不多。 遇到问题#xff1a; 随着用户数的增长…一 Redis概述
1 为什么要用NoSQL 单机Mysql的美好年代
在90年代一个网站的访问量一般都不大用单个数据库完全可以 轻松应付。在那个时候更多的都是静态网页动态交互类型的网站不多。 遇到问题 随着用户数的增长Tomcat和数据库之间竞争资源单机性能不足以支撑业务。 Tomcat与数据库分开部署
Tomcat和数据库分别独占服务器资源显著提高两者各自性能。 新的问题: 随着用户数的增长并发读写数据库成为瓶颈。 引入本地缓存和分布式缓存
通过缓存能把绝大多数请求在读写数据库前拦截掉大大降低数据库压力。其中涉及的技术包括使用memcached作为本地缓存 使用Redis作为分布式缓存。 注意 缓存抗住了大部分的访问请求随着用户数的增长并发压力主要落在单机的Tomcat上响应逐渐变慢。 引入反向代理实现负载均衡
在多台服务器上分别部署Tomcat使用反向代理软件Nginx把请求均匀分发到每个Tomcat中。 新的挑战 反向代理使应用服务器可支持的并发量大大增加但并发量的增长也意味着更多请求穿透到数据库单机的数据库最终成为瓶颈。 数据库读写分离
由于数据库的写入压力增加Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负大部分网站开始使用主从复制技术来达到读写分离以提高读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了 新的挑战 业务逐渐变多不同业务之间的访问量差距较大不同业务直 接竞争数据库相互影响性能。 数据库按业务分库
把不同业务的数据保存到不同的数据库中使业务之间的资源竞争降低对于访问量大的业务可以部署更多的服务器来支撑。
为什么用NoSQL
用户的个人信息社交网络地理位置用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘那 SQL数据库已经不适合这些应用了, NoSQL数据库的发展也却能很好的处理这些大的数据。 2 什么是NoSQL 什么是NoSQL
NoSQL(NoSQL Not Only SQL)意即“不仅仅是SQL”泛指非关系型的数据库。随着互联网web2.0网站的兴起传统的关系数据库在应付特别是超大规模和高并发类型纯动态网站已经显得力不从心暴露了很多难以克服的问题。
结构化数据和非结构化数据 结构化数据指的是由二维表结构来逻辑表达和实现的数据严格遵循数据格式与长度规范也称作为行数据。 非结构化数据指的是数据结构不规则或不完整没有任何预定义的数据模型不方便用二维逻辑表来表现的数据例如办公文档Word、文本、图片、HTML、各类报表、视频音频等。
NoSQL的四大分类 KV型NoSql代表----Redis
KV型NoSql顾名思义就是以键值对形式存储的非关系型数据库是最简单、最容易理解也是大家最熟悉的一种NoSql因此比较快地带过。
特点
数据基于内存读写效率高KV型数据时间复杂度为O(1)查询速度快 注意 KV型NoSql最大的优点就是高性能利用Redis自带的 BenchMark做基准测试TPS可达到10万的级别性能非常强劲。 列式NoSql代表----HBase
列式NoSql大数据时代最具代表性的技术之一了以HBase为代表。
关系行数据库数据 注意 看到每行有name、phone、address三个字段这是行式存储 的方式且可以观察id 2的这条数据即使phone字段没有 它也是占空间的。 列式数据库数据 注意 查询时只有指定的列会被读取不会读取所有列列数据被组织到一起一次磁盘IO可以将一列数据一次性读取到内存中 文档型NoSql代表----MongoDB
什么是文档型NoSql呢文档型NoSql指的是将半结构化数据存储为文档的一种NoSql文档型NoSql通常以JSON或者XML格式存储数据。 注意 关系型数据库是按部就班地每个字段一列存在MongDB里面就是一个JSON字符串存储。 搜索型NoSql代表----ElasticSearch 传统关系型数据库主要通过索引来达到快速查询的目的但是在全文搜索的场景下索引是无能为力的like查询一来无法满足所有模糊匹配需求二来使用限制太大且使用不当容易造成慢查询搜索型NoSql的诞生正是为了解决关系型数据库全文搜索能力较弱的问题ElasticSearch是搜索型NoSql的代表产品。 关系型数据库和非关系型数据及其区别
关系型数据库 关系型数据库最典型的数据结构是表由二维表及其之间的联系所组成的一个数据组织
优点
易于维护都是使用表结构格式一致使用方便SQL语言通用可用于复杂查询复杂操作支持SQL可用于一个表以及多个表之间非常复杂的查询。
缺点
读写性能比较差尤其是海量数据的高效率读写固定的表结构灵活度稍欠
非关系型数据库 优点
格式灵活存储数据的格式可以是key,value形式、文档形式、图片形式等等文档形式、图片形 式等等使用灵活应用场景广泛而关系型数据库则只支持基础类型。速度快nosql可以使用硬盘或者随机存储器作为载体而关系型数据库只能使用硬盘高扩展性成本低nosql数据库部署简单基本都是开源软件。
缺点
不提供sql支持学习和使用成本较高无事务处理数据结构相对复杂复杂查询方面稍欠。
3 当下NoSQL经典应用 当下应用是SQL和NoSQL一起使用 淘宝商品信息如何存放 商品基本信息
名称、价格、出厂信息、生产厂商商家信息等 关系型数据库就可以解决。 注意淘宝内部用的Mysql是里面的大牛自己改造过的。 商品描述、详情、评论 多文件信息描述类IO读写性能变差不能使用Mysql数据库使用 MongDB。
商品的图片 分布式文件系统
淘宝自己的TFSGoogle的GFSHadoop的HDFS阿里云的OSS
商品关键字 搜索引擎 elasticsearch 或者 ISerach
商品热门的波段信息
内存数据库 Redis Tair Memache
遇到的问题
数据类型太多数据源繁多数据要改造
发现问题 难点 数据类型多样性数据源多样性和变化重构数据源改造而数据服务平台不需要大面积重构 解决问题
UDSL统一数据服务平台 UDSL热点缓存设计 4 Redis是什么 Redis是什么
Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库。
特性
基于内存运行性能高效支持分布式理论上可以无限扩展key-value存储系统开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、KeyValue数据库并提供多种语言的API
谁在用Redis
Github 京东 微博 阿里巴巴 百度 美团 搜狐
二 Redis安装
1 Linux下安装Redis 下载地址
Redis官方网址https://redis.io/
下载Redis redis-6.2.6.tar.gz上传至CentOS并解压解压后得到redis-6.2.4目 录
解压命令
tar -zxvf redis-6.2.6.tar.gz -C /usr/local安装GCC
安装C语言编译环境
yum install -y gcc通过使用 gcc --version 命令打印 GCC 版本来验证 GCC 编译器是否被成功安装:
gcc --version安装Redis
编译Redis
在redis目录下(/usr/local/redis-6.2.6)执行
make安装Redis
在redis目录下(/usr/local/redis-6.2.6)执行
make install安装目录 /usr/local/redis-6.2.6/src 注意 redis-benchmarkRedis自带的基准性能测试工具 redis-check-aof对有问题的 AOF 文件进行修复AOF和RDB 文件后面会说明 redis-check-rdb对有问题的 RDB文件进行修复 redis-sentinelRedis集群使用 redis-cli客户端 redis-server服务器启动 服务启动
前台启动/usr/local/redis-6.2.6/src下执行
./redis-server后台启动
修改redis.conf文件
daemonize yes #由no改为yes启动服务
./redis-server ../redis.conf客户端启动
/usr/local/redis-6.2.6/src下执行
./redis-cliping命令可以检测服务器是否正常服务器返回PONG
127.0.0.1:6379 ping
PONG2 Docker下安装Redis 下载最新Redis镜像
docker pull redis注意 可以用docker pull redis命令下载最新版本的Redis镜像也可 以用“docker pull redis:标签”命令下载指定版本的Redis。 启动Redis容器
docker run -itd --name myFirstRedis -p 6379:6379 redis:latest观察Redis启动效果
docker logs myFirstRedis注意 如果直接在Linux等环境上启动Redis服务器就能直接看到启动后的效果。 查看Redis的版本
先确保myFirstRedis容器处于Up状态。进入容器的命令行交互窗口。
docker exec -it myFirstRedis /bin/bash redis-server --versionRedis服务器和客户端
Redis是基于键值对存储的NoSQL数据库其中的数据是存储在 Redis服务器里的。和传统的MySQL数据库服务器相似一个Redis 服务器可以同多个客户端创建连接。
docker exec -it myFirstRedis /bin/bash redis-cli3 基本知识 默认16数据库
Redis是一个字典结构的存储服务器一个Redis实例提供了多个用来存储数据的字典客户端可以指定将数据存储在哪个字典中。
这与在一个关系数据库实例中可以创建多个数据库类似如下图所 示所以可以将其中的每个字典都理解成一个独立的数据库。 Redis默认支持16个数据库可以通过调整Redis的配置文件 redis/redis.conf中的databases来修改这一个值设置完毕后重启 Redis便完成配置。 Redis 使用的到底是多线程还是单线程
因为Redis是基于内存的操作CPU不是Redis的瓶颈Redis的瓶颈 最有可能是机器内存的大小或者网络带宽。既然单线程容易实现 而且CPU不会成为瓶颈那就顺理成章地采用单线程的方案了。
IO多路复用技术 redis 采用网络IO多路复用技术来保证在多连接的时候 系统的高吞吐量。
大白话解释
假设你是一个机场的空管 你需要管理到你机场的所有的航线 包括进港出港 有些航班需要放到停机坪等待有些航班需要去登机口接乘客。
最简单的做法就是你去招一大批空管员然后每人盯一架飞机 从进港接客排位出港航线监控直至交接给下一个空港 全程监控。 遇到的问题 很快你就发现空管塔里面聚集起来一大票的空管员交通稍微繁忙一点新的空管员就已经挤不进来了。空管员之间需要协调屋子里面就1, 2个人的时候还好几十号人以后 基本上就成菜市场 了。空管员经常需要更新一些公用的东西比如起飞显示屏比如下一个小时后的出港排期最 后你会很惊奇的发现每个人的时间最后都花在了抢这些资源上。 怎么解决
这个东西叫flight progress strip. 每一个块代表一个航班不同的槽代表不同的状态然后一个空管员可以管理一组这样的块一组航班而他的工作就是在航班信息有新的更新的时候把对应的块放到不同的槽子里面。
结论
这里“多路”指的是多个网络连接“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求尽量减少网络IO的时间消耗且Redis在内存中操作数据的速度非常快内存内的操作不会成为这里的性能瓶颈主要以上两点造就 了Redis具有很高的吞吐量。
切换数据库
语法结构
select number示例
# 默认使用 0 号数据库
redis 127.0.0.1:6379 SET db_number 0
OK
# 使用 1 号数据库
redis 127.0.0.1:6379 SELECT 1
OK清空当前库
Redis Flushdb 命令用于清空当前数据库中的所有 key。
语法结构
127.0.0.1:6379 FLUSHDB示例
127.0.0.1:6379 FLUSHDB通杀全部库
Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数 据库的所有key )。
语法结构
redis 127.0.0.1:6379 FLUSHALL示例
# 清空所有数据库的所有 key
redis 127.0.0.1:6379flushall
OK为什么默认端口6379
意大利的一位广告女郎名字叫Merz全名Alessia Merz。 6379 Merz 三 Redis数据类型
1 key键 keys
查看当前库中所有的key 。
语法结构
keys *有3个通配符 *, ? ,[]
*: 通配任意多个字符?: 通配单个字符[]: 通配括号内的某1个字符
示例
keys *注意 生产已经禁止。因为长时间阻塞redis而导致其他客户端的命令请求一直处于阻塞状态。 更安全的做法是采用scan。 新版本也进行了替代
root6c068b3fbf29:/data# redis-cli --scan
u*
user1
userexists
判断某个key是否存在返回1表示存在0不存在。
语法结构
exists key示例
#查看k1是否存在如果存在返回1
exists k1
# 查看k1 k2 k3是否存在如果k1 k2存在k3不存在则
返回2
exists k1 k2 k3注意 可以设置多个key只返回存在的个数但不返回哪一个存在/ 不存在。 type
查看当前key 所储存的值的类型。返回当前key所储存的值的类型 如string 、list等。
语法结构
type key示例
type k1del
删除已存在的key不存在的 key 会被忽略。
语法结构
del key示例
可以设置多个key返回删除成功的个数。
# 删除k1如果成功返回1失败返回0
del k1
# 删除k1 k2 k3如果k1 k2存在k3不存在则返回2
del k1 k2 k3expire
给key设置time秒的过期时间。设置成功返回 1 。 当 key 不存在返回 0。
语法结构
expire key time示例
# 给k1设置10秒后过期
expire k1 10ttl
以秒为单位返回 key 的剩余过期时间。
语法结构
ttl key示例
ttl k1注意 当 key 不存在时返回 -2 。 当 key 存在但没有设置剩余生存时间时返回 -1 。 否则以秒为单位返回 key 的剩余生存时间。 persist
移除给定 key 的过期时间使得 key 永不过期。
语法结构 persist key示例
persist k1注意 当过期时间移除成功时返回 1 。 如果 key 不存在或 key 没有设置过期时间返回 0 。 2 String 简介
String是Redis最基本的类型一个key对应一个value。String是二进制安全的意味着String可以包含任何数据比如序列化对象或者一张图片。String最多可以放512M的数据。
常用命令
set
用于设置给定 key 的值。如果 key 已经存储其他值 set 就重写旧值且无视类型。
语法格式
set key value示例
127.0.0.1:6379 set k1 v1
OKget
用于获取指定 key 的值。如果 key 不存在返回 nil 。
语法格式
get key示例
127.0.0.1:6379 get k1
v1append
将给定的value追加到key原值末尾。
语法格式:
append key value示例
127.0.0.1:6379 APPEND k1 k1
(integer) 4
127.0.0.1:6379 APPEND k1 k2
(integer) 6注意 如果 key 已经存在并且是一个字符串 append 命令将 value 追加到 key 原来的值的末尾。如果 key 不存在 append 就简单地将给定 key 设为 value 就像执行 set key value 一 样。 strlen
获取指定 key 所储存的字符串值的长度。当 key 储存的不是字符串值时返回一个错误。
语法格式
strlen key示例
127.0.0.1:6379 strlen k1
(integer) 6setex
给指定的 key 设置值及time 秒的过期时间。如果 key 已经存在 setex命令将会替换旧的值并设置过期时间。
语法格式
setex key time value示例
#向Redis中设置一个k1的键值对并且10秒后过期
127.0.0.1:6379 setex k1 10 v1
OKsetnx
只有在key不存在时设置key的值
语法格式
setnx key value示例
127.0.0.1:6379 setnx k1 v1
(integer) 0
127.0.0.1:6379 setnx k4 v4
(integer) 1getrange
获取指定区间范围内的值类似between…and 的关系
语法格式
getrange key start end示例
127.0.0.1:6379 set k5 abcd123xxx
OK
127.0.0.1:6379 getrange k5 2 4
cd1setrange
设置指定区间范围内的值类似between…and 的关系
语法结构
setrange key offset value示例
127.0.0.1:6379 set k6 abcd1234
OK
127.0.0.1:6379 setrange k6 1 xxx
(integer) 8
127.0.0.1:6379 get k6
axxx1234incr
将 key 中储存的数字值增一。
语法格式
incr key示例
#因为Redis中不存在k1所以先初始化为0再递增值为1
127.0.0.1:6379 incr k1
(integer) 1# incr k1 存在k1递增后k1的值为2
127.0.0.1:6379 incr k1
(integer) 2# 如果value不是数字就会报错
127.0.0.1:6379 set k2 v2
OK
127.0.0.1:6379 incr k2
(error) ERR value is not an integer or out of range 注意 如果 key 不存在那么 key 的值会先被初始化为 0 然后再执行 incr 操作。如字符串类型的值不能表示为数字、或者是其他类型那么返回一个错误。 decr
将 key 中储存的数字值减一。
语法格式
decr key示例
127.0.0.1:6379 decr k1
(integer) 1
127.0.0.1:6379 decr k1
(integer) 0
127.0.0.1:6379 decr k1
(integer) -1
127.0.0.1:6379 decr k1
(integer) -2
#如果
set k2 v2
decr k2 因为k2不为数值Redis返回一个错误注意 如果 key 不存在那么 key 的值会先被初始化为 0 然后再执行 decr 操作。如字符串类型的值不能表示为数字、或者是其他类型那么返回一个错误。 incrby/decrby key step
将key存储的数字值按照step进行增减。
127.0.0.1:6379 incrby k1 10
(integer) 20注意 如果 key 不存在那么 key 的值会先被初始化为 0 然后再执行 incrby/decrby 命令。如字符串类型的值不能表示为数字、或者是其他类型那么返回一个错误。 mset
同时设置一个或多个 key-value 。
语法格式
mset key1 value1 key2 value2示例
127.0.0.1:6379 mset k1 v1 k2 v2 k3 v3
OKmget
返回所有(一个或多个)给定 key 的值。
语法格式
mget key1 key2示例
127.0.0.1:6379 mget k1 k2 k3
1) v1
2) v2
3) v3注意: 如果给定的 key 里面有某个 key 不存在那么这个 key 返回 特殊值 nil 。 getset
将给定key值设为value并返回key的旧值old value简单一 句话先get然后立即set。
语法格式
getset key value示例
127.0.0.1:6379 getset k1 wcc
v1
127.0.0.1:6379 get k1
wcc使用场景
value 除了是字符串以外还可以是数字。
计数器统计多单位的数量粉丝数对象缓存存储分布式锁
3 List 简介
List是简单的字符串列表按照插入顺序排序。你可以添加一个元素到列表的头部左边或者尾部右边。底层是一个双向链表 对两端操作性能极高通过索引操作中间的节点性能较差。 一个List最多可以包含 2 32 − 1 2^{32}-1 232−1个元素 每个列表超过40亿个元素。
常用命令
lpush/rpush
从左边头部/右边尾部插入一个或多个值。
语法结构
lpush/rpush key1 value1 value2 value3……示例
#从左边放入v1 v2 v3
127.0.0.1:6379 lpush k1 v1 v2 v3
(integer) 3#从右边放入v4 v5 v6
127.0.0.1:6379 rpush k1 v4 v5 v6
(integer) 6lrange
返回key列表中的start和end之间的元素包含start和end。 其 中 0 表示列表的第一个元素-1表示最后一个元素。
语法结构
lrange key start end示例
#取出列表里前3个值结果为v3 v2 v1
127.0.0.1:6379 lrange k1 0 2
#取出列表里全部值结果为v3 v2 v1 v4 v5 v6
127.0.0.1:6379 lrange k1 0 -1lpop/rpop
移除并返回第一个值或最后一个值。
语法格式
lpop/rpop key示例
lpop k1 从列表中删除v3并返回当前列表全部值v2 v1 v4 v5 v6
rpop k1 从列表中删除v6并返回当前列表全部值v2 v1 v4 v5注意 值在键在值光键亡。 lindex
获取列表index位置的值(从左开始)。
语法结构
lindex key index示例
lindex k1 0llen
获取列表长度。
语法结构
llen key示例
127.0.0.1:6379 llen k1
(integer) 6lrem
从左边开始删除与value相同的count个元素。
语法结构
lrem key count value示例
#从左边开始删除k1列表中2个v1元素
lrem k1 2 v1linsert
在列表中value值的前边/后边插入一个new value值从左开始。
语法结构
linsert key before/after value newvalue示例
linsert k1 before v1 v5 在v1前面插入一个v5lset
将索引为index的值设置为value
语法结构
lset key index value示例
lset k1 0 v11使用场景
消息队列排行榜最新列表
4 Set 简介
与List类似是一个列表功能但Set是自动排重的当需要存储一个列表数据又不希望出现重复数据时Set是一个很好的选择。
Set是String类型的无序集合它底层其实是一个value为null的 hash表所以添加、删除、查找的时间复杂度都是O(1)。
常用命令
sadd
将一个或多个元素添加到集合key中已经存在的元素将被忽略。
语法结构
sadd key value1 value2……示例
#向集合中添加值最终只有v1 v2 v3 v4 v5 v6
127.0.0.1:6379 sadd k1 v1 v2 v2 v3 v4 v5 v6smembers
取出该集合的所有元素。
语法结构
smembers key示例
127.0.0.1:6379 smembers k1sismember
判断集合key中是否含有value元素如有返回1否则返回0。
语法结构
sismember key value示例
sismember k1 v1scard
返回该集合的元素个数。
语法结构
scard key示例
scard k1srem
删除集合中的一个或多个成员元素不存在的成员元素会被忽略。
语法结构
srem key value1 value2……示例
# 删除v1 v2
srem k1 v1 v2spop
随机删除集合中一个元素并返回该元素。
语法结构
spop key示例
spop k1 随机删除一个元素并返回srandmember
随机取出集合中count个元素但不会删除。
语法结构 srandmember key count示例
#随机取出集合中的2个元素
srandmember k1 2smove
将value元素从sourcekey集合移动到destinationkey集合中。
语法结构
smove sourcekey destinationkey value示例
smove k1 k2 v5 将元素v5从集合k1中移动到集合k2注意 如果 sourcekey集合不存在或不包含指定的 value元素则 smove 命令不执行任何操作仅返回 0 。 sinter
返回两个集合的交集元素。
语法结构
sinter key1 key2示例
sinter k1 k2sunion
返回两个集合的并集元素。
语法结构
sunion key1 key2示例
sunion k1 k2sdiff
返回两个集合的差集元素key1中的不包含key2
语法结构
sdiff key1 key2示例
sdiff k1 k2使用场景
黑白名单随机展示好友关注人粉丝感兴趣的人集合
5 Hash 简介
Hash是一个键值对的集合。Hash 是一个 String 类型的 field字 段 和 value值 的映射表hash 特别适合用于存储对象。
Hash存储结构优化
如果field数量较少存储结构优化为类数组结构如果field数量较多存储结构使用HashMap结构
常用命令
hset
给key集合中的field赋值value。
语法结构
hset key field value示例
127.0.0.1:6379 hset user name baizhan
(integer) 1
127.0.0.1:6379 hset user age 3
(integer) 1注意 如果哈希表不存在一个新的哈希表被创建并进行 HSET 操作。如果字段已经存在于哈希表中旧值将被重写。 hget
从key哈希中取出field字段的值。
语法结构
hget key field示例
127.0.0.1:6379 hget user name
baizhanhmset
批量设置哈希的字段及值。
语法结构
hmset key field1 value1 field2 value2……示例
127.0.0.1:6379 hmset user1 name baizhan age 15
OKhexists
判断指定key中是否存在field
语法结构
hexists key field示例:
127.0.0.1:6379 hexists user1 name
(integer) 1
127.0.0.1:6379 hexists user1 xxx
(integer) 0注意 如果哈希表含有给定字段返回 1 。 如果哈希表不含有给定字段或 key 不存在返回 0 。 hkeys
获取该哈希中所有的field。
语法结构
hkeys key示例
127.0.0.1:6379 hkeys user1
1) name
2) agehvals
获取该哈希中所有的value。
语法结构
hvals key示例
127.0.0.1:6379 hvals user1
1) baizhan
2) 15hincrby
为哈希表key中的field字段的值加上增量increment
语法结构:
hincrby key field increment示例
127.0.0.1:6379 hincrby user1 age 10
(integer) 25注意 增量也可以为负数相当于对指定字段进行减法操作。如果哈希表的 key 不存在一个新的哈希表被创建并执行 hincrby 命令。如果指定的字段不存在那么在执行命令前字段的值被初始化为 0 。对一个储存字符串值的字段执行 hincrby 命令将造成一个错误。 hincrby user1 age 10 对user中的age字段做运算增加10hdel
删除哈希表 key 中的一个或多个指定字段不存在的字段将被忽略。
语法结构
hdel key field1 field2……示例
127.0.0.1:6379 hdel user1 age
(integer) 1hsetnx
给key哈希表中不存在的的字段赋值 。
语法结构:
hsetnx key field value示例:
127.0.0.1:6379 hsetnx user1 age 10
(integer) 1注意 如果哈希表不存在一个新的哈希表被创建并进行 hsetnx 操作。 如果字段已经存在于哈希表中操作无效。 如果 key 不存在一个新哈希表被创建并执行 hsetnx 命令。 使用场景
购物车存储对象
6 Zset 简介
Zset与Set非常相似是一个没有重复元素的String集合。不同之处是Zset的每个元素都关联了一个分数score这个分数被用来按照从低分到高分的方式排序集合中的元素。集合的元素是唯一的 但分数可以重复。 注意 因为元素是有序的所以可以根据分数score或者次序 position来获取一个范围内的元素。 常用命令
zadd
将一个或多个元素value及分数score加入到有序集key中。
语法结构
zadd key score1 value1 score2 value2……示例
zadd k1 100 java 200 c 300 python 400 php注意 如果某个元素已经是有序集的元素那么更新这个元素的分数值并通过重新插入这个元素来保证该元素在正确的位置上。 分数值可以是整数值或双精度浮点数。 如果有序集合 key 不存在则创建一个空的有序集并执行 zadd 操作。 zrange
返回key集合中的索引start和索引end之间的元素包含start和 end。
语法结构
zrange key start end [withscores]示例
zrange k1 0 -1 返回集合中所有元素
zrange k1 0 -1 withscores 返回集合中所有元素并携带元素分数注意 其中元素的位置按分数值递增(从小到大)来排序。 其中 0 表示列表的第一个元素-1表示最 后一个元素。withscores是可选参数是否返回分数。 zrangebyscore
返回key集合中的分数minscore 和分数maxscore 之间的元素包 含minscore 和maxscore 。其中元素的位置按分数值递增(从小 到大)来排序。
语法结构
zrangebyscore key minscore maxscore [withscores]示例
zrangebyscore k1 200 400 返回200-400分之间的元素递增排序zincrby
为元素value的score加上increment的值。
语法结构
zincrby key increment value示例
zincrby k1 50 java 给java元素加上50分zrem
删除该集合下value的元素
语法结构
zrem k1 php 删除phpzcount
统计该集合在minscore 到maxscore分数区间中元素的个数。
语法结构 zcount key minscore maxscore示例
zcount k1 100 300 统计100分到300分中间元素的个数zrank
返回value在集合中的排名从0开始。
语法结构
zrank key value示例
zrank k1 c 返回c排名使用场景
延时队列排行榜限流
7 Bitmaps 简介
在计算机中用二进制位作为存储信息的基本单位1个字节等于8位。
例如 “abc” 字符串是由 3 个字节组成计算机存储时使用其二进制表示abc分别对应的ASCII码是97、98、99对应的二进制是 01100001、01100010、01100011在内存中表示如下 合理地使用位能够有效地提高内存使用率和开发效率。
Redis提供了Bitmaps这个 “数据结构” 可以实现对位的操作 常用命令
setbit
设置Bitmaps中某个偏移量的值。
语法结构
setbit key offset value示例
redis中bitmaps可以用来统计用户信息eg活跃天数、打卡天 数、登录天数
bitmaps位图都是操作二进制来进行记录就只有0和1两个状态
127.0.0.1:6379 setbit zhangsan 1 1 # 往zhangsan中添加数据第1天打卡
(integer) 1
127.0.0.1:6379 setbit zhangsan 2 0 # 第2天未打卡
(integer) 0
127.0.0.1:6379 setbit zhangsan 3 1 # 第3天打卡
(integer) 0
127.0.0.1:6379 setbit zhangsan 4 0 # 第4天未打卡
(integer) 0
127.0.0.1:6379 setbit zhangsan 5 1 # 第5天打卡
(integer) 0
127.0.0.1:6379 setbit zhangsan 6 0 # 第6天未打卡
(integer) 0
127.0.0.1:6379 setbit zhangsan 7 1 # 第7天打卡
(integer) 0127.0.0.1:6379 getbit zhangsan 1 #获取第一天的打卡状态
(integer) 1
127.0.0.1:6379 bitcount zhangsan # 统计所有打卡天数
(integer) 4getbit
获取Bitmaps中某个偏移量的值。
语法结构 getbit key offset示例
获取key的offset 的值。
getbit sign 3 如果偏移量未设置值则也返回0。
getbit sign 99 获取偏移量为99的值结果为0bitcount
统计字符串被设置为1的bit数量。一般情况下给定的整个字符串都会被进行统计可以选择通过额外的start和end参数指定字节 组范围内进行统计包括start和end0表示第一个元素-1表示 最后一个元素。
语法结构
bitcount key [start end]示例
bitcount sign 获取整个字符串被设置为1的bit数量bitop
将多个bitmaps通过求交集/并集方式合并成一个新的bitmaps。
语法结构
bitop and/or destkey sourcekey1 sourcekey2……示例
bitop and k3 k1 k2 通过求交集将k1 k2合并成k3
bitop or k3 k1 k2 通过求并集将k1 k2合并成k38 Geospatia 简介
GEOGeographic,地理信息的缩写。该类型就是元素的二维坐标在地图上就是经纬度。Redis基于该类型提供了经纬度设置、 查询、范围查询、距离查询、经纬度Hash等常见操作。
常用命令
geoadd
用于存储指定的地理空间位置可以将一个或多个经度 (longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
语法结构
geoadd key longitude latitude member示例
# 将北京的经纬度和名称添加到china
geoadd china 116.405285 39.904989 beijing
# 将成都和上海的经纬度、名称添加到china
geoadd china 104.065735 30.659462 chengdu 121.472644 31.231706 shanghaigeopos
从给定的 key 里返回所有指定名称(member)的位置经度和纬 度不存在的返回 nil
语法结构
geopos key member [member ……]示例
返回china中名称为shanghai和beijing的经纬度
geopos china shanghai beijinggeodist
用于返回两个给定位置之间的距离。 语法结构
geodist key member1 member2 [m|km|ft|mi]参数说明 m 米默认单位。km 千米。mi 英里。ft 英尺。 示例
# 返回shanghai和beijing之间的距离结果1067597.9668单位米
geodist china shanghai beijing# 返回shanghai和chengdu之间的距离结果1660.0198单位是千米
geodist china shanghai chengdu kmgeoradius
以给定的经纬度longitude latitude为中心 返回键包含的位置元素当中 与中心的距离不超过给定最大距离radius 的所有位 置元素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ICFVaTu8-1692870460036)(F:\001-after-end\笔记\12-高并发处理与数据缓存\02-assets\56.PNG)]
语法结构
georadius key longitude latitude radius m|km|ft|mi 示例
#获取经纬度110 30为中心在china内1200公里范围内的所有元素。
georadius china 110 30 1200 km使用场景
附近的电影院附近的好友离最近的火锅店
9 Hyperloglog 简介
在我们做站点流量统计的时候一般会统计页面UV(独立访客:unique visitor)和PV(即页面浏览量page view)。redis HyperLogLog是用来做基数统计的算法HyperLogLog的优点是在输入元素的数量或者体积非常非常大时计算基数所需的空间总是固定的、并且是很小的。
什么是基数
比如数据集{1,3,5,7,5,7,8}那么这个数据集的基数集为{1,3,5,7,8}, 基数(不重复元素)为5.基数估计就是在误差可接受的范围内快速计算基数。
常用命令
pfadd
将所有元素参数添加到 Hyperloglog 数据结构中。
语法结构
pfadd key element1 element2……示例
如果至少有个元素被添加返回 1 否则返回 0。
pfadd book1 uid1 uid2 uid3注意 添加元素到HyperLogLog中如果内部有变动返回1没有返回 0。 pfcount
计算Hyperloglog 近似基数可以计算多个Hyperloglog 统计基数总数。
语法结构
pfcount key1 key2……示例
pfcount book1 #计算book1的基数结果为3
pfadd book2 uid3 uid4 #添加两个元素到book2中
pfcount book1 book2 #统计两个key的基数总数结果为5pfmerge
将一个或多个Hyperloglogsourcekey1 合并成一个 Hyperloglog destkey 。
语法结构 pfmerge destkey sourcekey1 sourcekey2……示例
比如每月活跃用户可用每天活跃用户合并后计算。
#将book1和book2合并成book结果为5
pfmerge book book1 book2使用场景
基数不大数据量不大就用不上会有点大材小用浪费空间有局限性就是只能统计基数数量而没办法去知道具体的内容是什 么和bitmap相比属于两种特定统计情况简单来说 HyperLogLog 去重比 bitmaps 方便很多一般可以bitmap和 hyperloglog配合使用bitmap标识哪些用户活跃。
网站PV统计网站UV统计统计访问量(IP数)统计在线用户数统计每天搜索不同词条的个数统计文章真实阅读数
四 Redis可视化工具 下载Redis Desktop Manager 注意 官网https://rdm.dev/pricing 选择安装路径 连接Redis服务
关闭防火墙
systemctl stop firewalld.service关闭保护模式
protected-mode no开启远程访问
redis默认只允许本地访问要使redis可以远程访问可以修改 redis.conf。
注释掉bind 127.0.0.1 可以使所有的ip访问redis配置连接服务 配置信息 五 Java整合Redis
1 Jedis操作 什么是Jedis
Jedis是Redis官方推荐的Java连接开发工具。
引入Jedis
创建maven工程
引入maven依赖 dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion3.6.0/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependencyJedis连接到redis
//第一个参数是ip地址第二个参数是端口
Jedis jedis new Jedis(192.168.56.31,6379);注意 在连接之前需要开放redis连接服务redis.conf中注释掉 bind 127.0.0.1 ,然后 protected-mode no关闭防火墙。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpRsMXrP-1692870460042)(F:\001-after-end\笔记\12-高并发处理与数据缓存\02-assets\65.PNG)] 测试相关数据类型 public class RedisTest {private Jedis jedis;Beforepublic void conn() {jedis new Jedis(192.168.66.100, 6379);}Afterpublic void close() {//jedis使用完毕需要关闭jedis.close();}Testpublic void testConnection() {//通过ping()方法向redis发送一个ping命令服务器返回一个PongString msg jedis.ping();System.out.println(msg);}Testpublic void testString() {//设置一个keyjedis.set(k1, v1);//设置一个keyjedis.set(k2, 1);//获取一个keyString res jedis.get(k1);//对某一个key自增Long ires jedis.incr(k2);}Testpublic void testKeys() {//返回所有的keySetString keys jedis.keys(*);keys.stream().forEach(System.out::println);//返回该key剩余过期时间Long time jedis.ttl(k1);System.out.println(time);}Testpublic void testList() {//向list中添加数据jedis.lpush(list1, v1, v2, v3);//返回list全部数据ListString list jedis.lrange(list1, 0, -1);list.stream().forEach(System.out::println);}Testpublic void testSet() {//向set中添加数据jedis.sadd(set1, v1, v2, v2, v3);//查看该集合中有多少个元素SetString set jedis.smembers(set1);set.stream().forEach(System.out::println);}Testpublic void testHash() {//设置一个hashjedis.hset(user, age, 25);jedis.hset(user, name, lxx);//获取该key的所有valueListString list jedis.hvals(user);list.stream().forEach(System.out::println);}Testpublic void testZset() {//向zset中添加一条数据jedis.zadd(zset1, 100, java);jedis.zadd(zset1, 200, c);//获取所有的值SetString set jedis.zrange(zset1, 0, -1);set.stream().forEach(System.out::println);}Testpublic void testBitmaps() {//将b1偏移量为0的位设置为1jedis.setbit(b1, 0, 1);//将b1偏移量为1的位设置为0jedis.setbit(b1, 1, 0);//统计字符串被设置为1的bit数量Long bitCount jedis.bitcount(b1);System.out.println(bitCount);}Testpublic void testGeospatia() {//添加一条地理信息数据jedis.geoadd(china, 116.40400, 39.92800, kunming);}Testpublic void testHyperloglog() {//将所有元素参数添加到 Hyperloglog 数据结构中。jedis.pfadd(book, uid1, uid2, uid3);}} 注意 其实jedis中的方法基本同redis命令一致。 2 Spring-Data-Redis 简介
Spring-Data-Redis是spring大家族的一部分通过简单的配置访问 Redis服务对Reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装RedisTemplate提供了Redis各种操作、异常处理及序列化 支持发布订阅。
RedisTemplate介绍
Spring封装了RedisTemplate对象来进行对Redis的各种操作它支 持所有的Redis原生的api。
org.springframework.data.redis.core Class RedisTemplateK,V注意 K模板中的Redis key的类型模板中的Redis key的类型通常为String 如 RedisTemplateString,Object。 V模板中的Redis value的类型 RedisTemplate中定义了对5种数据结构操作
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序setStringRedisTemplate与RedisTemplate
两者的关系是StringRedisTemplate继承RedisTemplate。两者的数据是不共通的也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据RedisTemplate只能管理 RedisTemplate中的数据。SDR默认采用的序列化策略有两种一种是String的序列化策略一种是JDK的序列化策略。StringRedisTemplate默认采用的是String的序列化策略保存的key和value都是采用此策略序列化保存的。 RedisTemplate默认采用的是JDK的序列化策略保存的key和 value都是采用此策略序列化保存的。
pom.xml添加依赖 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis-reactive/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies在application.properties中配置
#Redis服务器连接地址
spring.redis.host192.168.66.100
#Redis服务器连接端口
spring.redis.port6379
#连接池最大连接数使用负值表示没有限制
spring.redis.pool.max-active8
#连接池最大阻塞等待时间使用负值表示没有限制
spring.redis.pool.max-wait-1
#连接池中的最大空闲连接
spring.redis.pool.max-idle8
#连接池中的最小空闲连接
spring.redis.pool.min-idle0
#连接超时时间毫秒
spring.redis.timeout30000
自定义序列化
/*** 自定义序列化方式*/
Configuration
public class RedisConfig {Beanpublic RedisTemplateString, ObjectredisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplateString, Object redisTemplate new RedisTemplate();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setConnectionFactory(redisConnectionFactory);return redisTemplate;}
}使用redisTemplate进行各类型的CURD操作
String数据类型操作
SpringBootTest
public class StringTest {Autowiredprivate RedisTemplate redisTemplate;//添加元素Testpublic void testSet() {redisTemplate.opsForValue().set(k1, v1);}//获取元素Testpublic void testGet() {Object k1 redisTemplate.opsForValue().get(k1);System.out.println(k1);}//添加元素并设置过期时间Testpublic void testSetex() {redisTemplate.opsForValue().set(k1, v1, 10, TimeUnit.SECONDS);}}Hash类型的操作
SpringBootTest
public class HashTest {Autowiredprivate RedisTemplate redisTemplate;//添加元素Testpublic void testHset() {redisTemplate.opsForHash().put(user, name, lxx);}//获取数据Testpublic void testHget() {Object o redisTemplate.opsForHash().get(user, name);System.out.println(o);}
}
set类型的操作
SpringBootTest
public class SetTest {Autowiredprivate RedisTemplate redisTemplate;//添加元素Testpublic void testSset() {redisTemplate.opsForSet().add(k1, v1, v2, v2, v3);}//获取set的长度Testpublic void testSgetSize() {Long size redisTemplate.opsForSet().size(k1);System.out.println(size);}//获取元素Testpublic void testSgetAll() {Set set redisTemplate.opsForSet().members(k1);set.stream().forEach(System.out::println);}}
zset类型的操作
SpringBootTest
public class ZsetTest {Autowiredprivate RedisTemplate redisTemplate;//添加元素Testpublic void testZadd() {redisTemplate.opsForZSet().add(s1, java, 100);redisTemplate.opsForZSet().add(s1, python, 200);}//获取元素Testpublic void testZrange() {Set set redisTemplate.opsForZSet().range(s1, 0, -1);set.stream().forEach(System.out::println);}}
list类型的操作
SpringBootTest
public class ListTest {Autowiredprivate RedisTemplate redisTemplate;//添加元素Testpublic void testLrpush() {redisTemplate.opsForList().rightPush(l1, v1);redisTemplate.opsForList().rightPush(l1, v2);}//获取元素Testpublic void testLrange() {List list redisTemplate.opsForList().range(l1, 0, -1);list.stream().forEach(System.out::println);}}3 Redis构建web应用实践_网页缓存 创建springboot项目
pom.xml dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- MyBatisPlus --dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.0/version/dependency!-- mysql驱动 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion5.1.48/version/dependency!-- lombok --dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependency!-- redis --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis-reactive/artifactId/dependency!-- fastjson --dependencygroupIdcom.alibaba.fastjson2/groupIdartifactIdfastjson2/artifactIdversion2.0.23/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency/dependencies编写配置文件application.yml
spring:# 数据源datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/student?characterEncodingutf-8username: rootpassword: 123456#Redisredis:host: 192.168.66.100 #Redis服务器连接地址port: 6379 #Redis服务器连接端口pool:max-active: 8 #连接池最大连接数使用负值表示没有限制max-wait: -1 #连接池最大阻塞等待时间使用负值表示没有限制max-idle: 8 #连接池中的最大空闲连接min-idle: 0 #连接池中的最小空闲连接timeout: 3000 #连接超时时间毫秒创建实体类
Data
NoArgsConstructor
AllArgsConstructor
public class Student {private Integer id;private String name;private String sex;private String address;
}编写持久层
public interface StudentMapper extends BaseMapperStudent {
}编写业务层
Service
public class StudentService {Autowiredprivate StudentMapper studentMapper;Autowiredprivate StringRedisTemplate redisTemplate;public Student findById(Integer id) {// 从Redis中获取缓存String studentStr redisTemplate.opsForValue().get(student:id: id);// 判断是否有缓存if (StringUtils.isEmpty(studentStr)) {//如果没有//根据id查询学生信息Student student studentMapper.selectById(id);//添加缓存redisTemplate.opsForValue().set(student:id: id, JSON.toJSONString(student));return student;} else {//如果有// 直接把json数据转为goods对象Student student JSON.parseObject(studentStr, Student.class);return student;}}
}
编写控制层
RestController
RequestMapping(/student)
public class StudentController {Autowiredprivate StudentService studentService;GetMapping(/{id})public Student findById(PathVariable Integer id) {return studentService.findById(id);}}使用Jmeter进行压力测试
六 Redis配置文件 在Redis的解压目录下有个很重要的配置文件 redis.conf 关于 Redis的很多功能的配置都在此文件中完成的一般为了不破坏安装的文件出厂默认配置最好不要去改。
units单位
配置大小单位开头定义基本度量单位只支持bytes大小写不敏感。 INCLUDES
Redis只有一个配置文件如果多个人进行开发维护那么就需要多个这样的配置文件这时候多个配置文件就可以在此通过 include /path/to/local.conf 配置进来而原本的 redis.conf 配置文件就作为一个总闸。 NETWORK 参数 bind:绑定redis服务器网卡IP默认为127.0.0.1,即本地回环地址。这样的话访问redis服务只能通过本机的客户端连接而无法通过远程连接。如果bind选项为空的话那会接受所有 来自于可用网络接口的连接。port指定redis运行的端口默认是6379。由于Redis是单线程模型因此单机开多个 Redis进程的时候会修改端口。timeout设置客户端连接时的超时时间单位为秒。当客户端在这段时间内没有发出任何指令那么关闭该连接。默认值为0表示不关闭。tcp-keepalive 单位是秒表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康 状态避免服务器一直阻塞官方给出的建议值是300s如果设置为0则不会周期性的检测。 GENERAL 具体配置详解 daemonize:设置为yes表示指定Redis以守护进程的方式启动后台启动。默认值为 no pidfile:配置PID文件路径当redis作为守护进程运行的时候它会把 pid 默认写到 /var/redis/run/redis_6379.pid 文件里面 loglevel 定义日志级别。默认值为notice有如下4种取值 debug记录大量日志信息适用于开发、测试阶段verbose较多日志信息notice适量日志信息使用于生产环境warning仅有部分重要、关键信息才会被记录logfile 配置log文件地址,默认打印在命令行终端的窗口上 databases设置数据库的数目。默认的数据库是DB 0 可以在每个连接上使用select 命令 选择一个不同的数据库dbid是一个介于0到databases - 1 之间的数值。默认值是 16也就 是说默认Redis有16个数据库。 SNAPSHOTTING
这里的配置主要用来做持久化操作。 参数 save这里是用来配置触发 Redis的持久化条件也就是什么时候将内存中的数据保存到硬盘 save 900 1表示900 秒内如果至少有 1 个 key 的值变化则保存 save 300 10表示300 秒内如果至少有 10 个 key 的值变化 则保存 save 60 10000表示60 秒内如果至少有 10000 个 key 的值变 化则保存 REPLICATION 参数 slave-serve-stale-data默认值为yes。当一个 slave 与 master 失去联系或者复制正在进行的时候 slave 可能会有两种表现 如果为 yes slave 仍然会应答客户端请求但返回的数据 可能是过时或者数据可能是空的在第一次同步的时候如果为 no 在你执行除了 info he salveof 之外的其他命令时slave 都将返回一个 “SYNC with master in progress” 的错误 slave-read-only配置Redis的Slave实例是否接受写操作即Slave是否为只读Redis。默认 值为yes。 repl-diskless-sync主从数据复制是否使用无硬盘复制功能。默认值为no。 repl-diskless-sync-delay当启用无硬盘备份服务器等待一段时间后才会通过套接字向从 站传送RDB文件这个等待时间是可配置的。 repl-disable-tcp-nodelay同步之后是否禁用从站上的TCP_NODELAY 如果你选择yes redis会使用较少量的TCP包和带宽向从站发送数据。 SECURITY requirepass设置redis连接密码。
比如: requirepass 123 表示redis的连接密码为123。 CLIENTS 参数 maxclients 设置客户端最大并发连接数默认无限制Redis 可以同时打开的客户端连接数为Redis进程可以打开的最大文件。 描述符数-32redis server自身会使用一些如果设置 maxclients为0 。表示不作限制。当客户端连接数到达限制时 Redis会关闭新的连接并向客户端返回max number of clients reached错误信息 MEMORY MANAGEMENT 参数 maxmemory设置Redis的最大内存如果设置为0 。表示不作限制。通常是配合下面介绍 的maxmemory-policy参数一起使用。 maxmemory-policy 当内存使用达到maxmemory设置的最大值时redis使用的内存清除 策略。有以下几种可以选择 volatile-lru 利用LRU算法移除设置过过期时间的 key (LRU:最近使用 Least Recently Used )allkeys-lru 利用LRU算法移除任何keyvolatile-random 移除设置过过期时间的随机keyallkeys-random 移除随机kevolatile-ttl 移除即将过期的key(minor TTL)noeviction noeviction 不移除任何key只是返回 一个写错误 默认选项 maxmemory-samples LRU 和 minimal TTL 算法都不是精准的算法但是相对精确的算法(为了节省内存)。随意你可以选择样本大小进行检redis默认选择3个样本进行检测你可 以通过maxmemory-samples进行设置样本数。 APPEND ONLY MODE 参数 appendonly默认redis使用的是rdb方式持久化这种方式在许多应用中已经足够用了。但 是redis如果中途宕机会导致可能有几分钟的数据丢失根据save来策略进行持久化 Append Only File是另一种持久化方式 可以提供更好的持久化特性。Redis会把每次写入 的数据在接收后都写入appendonly.aof文件每次启动时Redis都会先把这个文件的数据读 入内存里先忽略RDB文件。默认值为no。appendfilename aof文件名默认是appendonly.aofappendfsyncaof持久化策略的配置no表示不执行fsync由操作系统保证数据同步到磁 盘速度最快always表示每次写入都执行fsync以保证数据同步到磁盘everysec表示每 秒执行一次fsync可能会导致丢失这1s数据 LUA SCRIPTING 参数 lua-time-limit一个lua脚本执行的最大时间单位为ms。默 认值为5000. REDIS CLUSTER 参数 cluster-enabled集群开关默认是不开启集群模式。cluster-config-file集群配置文件的名称。cluster-node-timeout 可以配置值为15000。节点互连超时的阀值集群节点超时毫秒数cluster-slave-validity-factor 可以配置值为10。 七 Redis其他功能
1 发布与订阅 什么是发布与订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式发送者 (pub) 发 送消息订阅者 (sub) 接收消息。
什么时候用发布订阅
看到发布订阅的特性用来做一个简单的实时聊天系统再适合不过 了。再比如在一个博客网站中有100个粉丝订阅了你当你发布新文章就可以推送消息给粉丝们。 Redis的发布与订阅 发布订阅命令行实现
订阅
语法格式
subcribe 主题名字示例
127.0.0.1:6379 subcribe channel-1
Reading messages... (press Ctrl-C to quit)
1) subscribe
2) channel-1
3) (integer) 1发布命令
语法格式
publish channel-1 hello示例
打开另一个客户端给channel1发布消息hello
127.0.0.1:6379 puhlish channel-1 hello
(integer) 1注意 返回的1是订阅者数量。 打开第一个客户端可以看到发送的消息
127.0.0.1:6379 subcribe channel-1
Reading messages... (press Ctrl-C to quit)
1) subscribe
2) channel-1
3) (integer) 1
1) message
2) channel-1
3) hello注意 发布的消息没有持久化如果在订阅的客户端收不到hello只能收到订阅后发布的消息。 2 慢查询 什么是慢查询
慢查询顾名思义就是比较慢的查询但是究竟是哪里慢呢
Redis命令执行的整个过程 两点说明 慢查询发生在第3阶段客户端超时不一定慢查询但慢查询是客户端超时的一个可能因素慢查询日志是存放在Redis内存列表中。 什么是慢查询日志
慢查询日志是Redis服务端在命令执行前后计算每条命令的执行时长当超过某个阈值是记录下来的日志。日志中记录了慢查询发生的时间还有执行时长、具体什么命令等信息它可以用来帮助开发和运维人员定位系统中存在的慢查询。
如何获取慢查询日志
可以使用 slowlog get 命令获取慢查询日志在 slowlog get 后面还可以加一 个数字用于指定获取慢查询日志的条数比如获取3条慢查询日志
127.0.0.1:6379 SLOWLOG get 3
1) 1) (integer) 02) (integer) 16400565673) (integer) 117804) 1) FLUSHALL5) 127.0.0.1:434066) 参数 唯一标识ID命令执行的时间戳命令执行时长执行的命名和参数 如何获取慢查询日志的长度
可以使用 slowlog len 命令获取慢查询日志的长度。 slowlog len
(integer) 121注意当前Redis中有121条慢查询日志。 怎么配置慢查询的参数 命令执行时长的指定阈值 slowlog-log-slower-than。 slowlog-log-slower-than的作用是指定命令执行时长的阈值执行 命令的时长超过这个阈值时就会被记录下来 存放慢查询日志的条数 slowlog-max-len。 slowlog-max-len的作用是指定慢查询日志最多存储的条数。实际上Redis使用了一个列表存放慢查询日志slowlog-max-len就是 这个列表的最大长度。
如何进行配置
查看慢日志配置
查看redis慢日志配置登陆redis服务器使用redis-cli客户端连接 redis server
127.0.0.1:6379 config get slow*
1) slowlog-max-len
2) 128
3) slowlog-log-slower-than
4) 10000慢日志说明 10000阈值单位微秒此处为10毫秒128慢日志记录保存数量的阈值此处保存128条。 修改Redis配置文件
比如把slowlog-log-slower-than设置为1000slowlog-max-len 设置为1200
slowlog-log-slower-than 1000
slowlog-max-len 1200使用 config set 命令动态修改。
比如还是把slowlog-log-slower-than设置为1000slowlog-maxlen设置为1200 config set slowlog-log-slower-than 1000
OKconfig set slowlog-max-len 1200
OKconfig rewrite
OK实践建议
slowlog-max-len配置建议
线上建议调大慢查询列表记录慢查询时Redis会对长命令做截断操作并不会占用大量内存。增大慢查询列表可以减缓慢查询被剔除的可能例如线上可设置为1000以上。
slowlog-log-slower-than配置建议
默认值超过10毫秒判定为慢查询需要根据Redis并发量调整该值。由于Redis采用单线程响应命令对于高流量的场景如果命令执行时间在1毫秒以上那么Redis 最多可支撑OPS不到1000。因此对于高OPS场景的Redis建议设置为1毫秒。
3 流水线pipeline 1次网络命令通信模型 经历了1次时间 1次网络时间 1次命令时间。
批量网络命令通信模型 经历了 n次时间 n次网络时间 n次命令时间
什么是流水线 经历了 1次pipeline(n条命令) 1次网络时间 n次命令时间这大大减少了网络时间的开销这就是流水线。
案例展示
从北京到上海的一条命令的生命周期有多长 执行一条命令在redis端可能需要几百微秒而在网络光纤中传输只 花费了13毫秒。 注意 在执行批量操作而没有使用pipeline功能会将大量的时间耗费 在每一次网络传输的过程上而使用pipeline后只需要经过一 次网络传输然后批量在redis端进行命令操作。这会大大提高了效率。 pipeline-Jedis实现
首先引入jedis依赖包
dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion2.9.0/version
/dependency 没有pipeline的命令执行 Jedis jedis new Jedis(127.0.0.1,6379);for ( int i 0 ; i 10000 ; i ){jedis.hset(hashkey: i , field i , value i);} 注意 在不使用pipeline的情况下使用for循环进行每次一条命令的执行操作耗费的时间可能达到 1w 条插入命令的耗时为50s。 使用pipeline Jedis jedis new Jedis(127.0.0.1,6379);for ( int i 0; i 100 ; i) {Pipeline pipeline jedis.ppipelined();for (int j i * 100 ; j (i 1) * 100 ; j) {pipeline.hset(hashkey: j,field j, value j);}pipeline.syncAndReturnAll();}八 Redis数据安全
1 持久化机制概述 由于Redis的数据都存放在内存中如果没有配置持久化Redis重 启后数据就全丢失了于是需要开启Redis的持久化功能将数据保 存到磁盘上当Redis重启后可以从磁盘中恢复数据。
持久化机制概述
对于Redis而言持久化机制是指把内存中的数据存为硬盘文件 这样当Redis重启或服务器故障时能根据持久化后的硬盘文件恢复数据。
持久化机制的意义
redis持久化的意义在于故障恢复。比如部署了一个redis作为 cache缓存同时也可以保存一些比较重要的数据。 Redis提供了两个不同形式的持久化方式
RDB(Redis DataBase)AOF(Append Only File)
2 RDB持久化机制实战 RDB是什么
在指定的时间间隔内将内存的数据集快照写入磁盘也就是行话讲的快照它恢复时是将快照文件直接读到内存里。 注意 这种格式是经过压缩的二进制文件。 配置dump.rdb文件
RDB保存的文件在redis.conf中配置文件名称默认为 dump.rdb。 439440 # The filename where to dump the DB441 dbfilename dump.rdb442rdb文件的保存位置也可以修改。默认在Redis启动时命令行所在的目录下。
dir ./触发机制-主要三种方式
RDB配置 快照默认配置 save 3600 1表示3600秒内一小时如果至少有1个key的值变化则保存。save 300 100表示300秒内五分钟如果至少有100个 key 的值变化则保存。save 60 10000表示60秒内如果至少有 10000个key的值变化则保存。 配置新的保存规则
给redis.conf添加新的快照策略30秒内如果有5次key的变化则触发快照。
配置修改后需要重启Redis服务。
save 3600 1
save 300 100
save 60 10000
save 30 5flushall
执行flushall命令也会触发rdb规则。
save与bgsave
手动触发Redis进行RDB持久化的命令有两种
save 该命令会阻塞当前Redis服务器执行save命令期间Redis不能处理其他命令直到RDB过程完成为止不建议使用。bgsave 执行该命令时Redis会在后台异步进行快照操作快照同时还可以响应客户端请求。
高级配置
stop-writes-on-bgsave-error
默认值是yes。当Redis无法写入磁盘的话直接关闭Redis的写操作。 rdbcompression
默认值是yes。对于存储到磁盘中的快照可以设置是否进行压缩存储。如果是的话redis会采用LZF算法进行压缩。如果你不想消耗 CPU来进行压缩的话可以设置为关闭此功能但是存储在磁盘上的快照会比较大。 rdbchecksum
默认值是yes。在存储快照后我们还可以让redis使用CRC64算法来进行数据校验但是这样做会增加大约10%的性能消耗如果希望获取到最大的性能提升可以关闭此功能。
恢复数据
只需要将rdb文件放在Redis的启动目录Redis启动时会自动加载 dump.rdb并恢复数据。
优势
适合大规模的数据恢复对数据完整性和一致性要求不高更适合使用节省磁盘空间恢复速度快
劣势
在备份周期在一定间隔时间做一次备份所以如果Redis意外down掉的话就会丢失最后一次快照后的所有修改。
3 AOF持久化机制实战 AOF是什么
以日志的形式来记录每个写操作将Redis执行过的所有写指令记录下来。 AOF默认不开启
可以在redis.conf中配置文件名称默认为appendonly.aof。 注意 AOF文件的保存路径同RDB的路径一致如果AOF和RDB同时启动Redis默认读取AOF的数据。 AOF启动/修复/恢复
开启AOF
设置Yes修改默认的appendonly no改为yes
appendonly yes注意 修改完需要重启redis服务。 设置数据。
set k11 v11
set k12 v12
set k13 v13
set k14 v14
set k15 v15AOF同步频率设置 参数 appendfsync always 始终同步每次Redis的写入都会立刻记入日志性能较差但数据完整性比较好。 appendfsync everysec 每秒同步每秒记入日志一次如果宕机本秒的数据可能丢失。 appendfsync no redis不主动进行同步把同步时机交给操作系统。 优势
备份机制更稳健丢失数据概率更低。可读的日志文本通过操作AOF稳健可以处理误操作。
劣势
比起RDB占用更多的磁盘空间。恢复备份速度要慢。每次读写都同步的话有一定的性能压力。
4 如何选用持久化方式 不要仅仅使用RDB
RDB数据快照文件都是每隔5分钟或者更长时间生成一次这个时候就得接受一旦redis进程宕机那么会丢失最近5分钟的数据。
也不要仅仅使用AOF
你通过AOF做冷备没有RDB做冷备来的恢复速度更快。RDB每次简单粗暴生成数据快照更加健壮可以避免AOF这种复杂的备份和恢复机制的bug。
综合使用AOF和RDB两种持久化机制
用AOF来保证数据不丢失作为数据恢复的第一选择用RDB来做不同程度的冷备在AOF文件都丢失或损坏不可用的时候还可以使用RDB来进行快速的数据恢复。
九 Redis事务
1 事务的概念与ACID特性 数据库层面事务
在数据库层面事务是指一组操作这些操作要么全都被成功执行要么全都不执行。
数据库事务的四大特性
AAtomic原子性将所有SQL作为原子工作单元执行要么全部执行要么全部不执行CConsistent一致性事务完成后所有数据的状态都是一致的即A账户只要减去了100B 账户则必定加上了100IIsolation隔离性如果有多个事务并发执行每个事务作出的修改必须与其他事务隔离DDuration持久性即事务完成后对数据库数据的修改被持久化存储。
Redis事务
Redis事务是一组命令的集合一个事务中的所有命令都将被序列化按照一次性、顺序性、排他性的执行一系列的命令。
Redis事务三大特性
单独的隔离操作事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中不会被其他客户端发送来的命令请求所打断没有隔离级别的概念队列中的命令没有提交之前都不会实际的被执行因为事务提交前任何指令都不会被实际执行也就不存在”事务内的查询要看到事务里的更新在事务外查询不能看到”。不保证原子性redis同一个事务中如果有一条命令执行失败其后的命令仍然会被执行没有回滚
Redis事务执行的三个阶段 开启以 MULTI 开始一个事务入队将多个命令入队到事务中接到这些命令并不会立即执行而是放到等待执行的事务队列里面执行由 EXEC 命令触发事务
2 Redis事务基本操作 Multi、Exec、discard
事务从输入Multi命令开始输入的命令都会依次压入命令缓冲队列 中并不会执行直到输入Exec后Redis会将之前的命令缓冲队列 中的命令依次执行。组队过程中可以通过discard来放弃组队。
例子
正常执行
127.0.0.1:6379 set t1 1
OK
127.0.0.1:6379 MULTI
OK
127.0.0.1:6379(TX) set id 12
QUEUED
127.0.0.1:6379(TX) get id
QUEUED
127.0.0.1:6379(TX) incr t1
QUEUED
127.0.0.1:6379(TX) incr t1
QUEUED
127.0.0.1:6379(TX) get t1
QUEUED
127.0.0.1:6379(TX) EXEC
1) OK
2) 12
3) (integer) 2
4) (integer) 3
5) 3放弃事务
127.0.0.1:6379 MULTI
OK
127.0.0.1:6379(TX) set name z3
QUEUED
127.0.0.1:6379(TX) set age 29
QUEUED
127.0.0.1:6379(TX) incr t1
QUEUED
127.0.0.1:6379(TX) DISCARD
OK全体连坐
127.0.0.1:6379 MULTI
OK
127.0.0.1:6379(TX) set name z3
QUEUED
127.0.0.1:6379(TX) get name
QUEUED
127.0.0.1:6379(TX) incr t1
QUEUED
127.0.0.1:6379(TX) get t1
QUEUED
127.0.0.1:6379(TX) set email
(error) ERR wrong number of arguments for
set command
127.0.0.1:6379(TX) exec
(error) EXECABORT Transaction discarded
because of previous errors.注意 命令集合中含有错误的指令注意是语法错误均连坐全部失败。 冤有头债有主
127.0.0.1:6379 MULTI
OK
127.0.0.1:6379(TX) set age 11
QUEUED
127.0.0.1:6379(TX) incr t1
QUEUED
127.0.0.1:6379(TX) set email abc163.com
QUEUED
127.0.0.1:6379(TX) incr email
QUEUED
127.0.0.1:6379(TX) get age
QUEUED
127.0.0.1:6379(TX) exec
1) OK
2) (integer) 5
3) OK
4) (error) ERR value is not an integer or
out of range
5) 11注意 运行时错误即非语法错误正确命令都会执行错误命令返 回错误。 十 Redis主从复制
1 主从复制概述 概述
在现有企业中80%公司大部分使用的是redis单机服务在实际的场景当中单一节点的redis容易面临风险。 面临问题: 机器故障。我们部署到一台 Redis 服务器当发生机器故障时需要迁移到另外一台服务器并且要保证数据是同步的。容量瓶颈。当我们有需求需要扩容 Redis 内存时从 16G 的内存升到 64G单机肯定是满足 不了。当然你可以重新买个 128G 的新机器。 解决办法
要实现分布式数据库的更大的存储容量和承受高并发访问量我们会将原来集中式数据库的数据分别存储到其他多个网络节点上。 注意 Redis 为了解决这个单一节点的问题也会把数据复制多个副本部署到其他节点上进行复制实现 Redis的高可用实现对数据的冗余备份从而保证数据和服务的高可用。 什么是主从复制
主从复制是指将一台Redis服务器的数据复制到其他的Redis服务器。前者称为主节点(master)后者称为从节点(slave),数据的复制是单向的只能由主节点到从节点。 主从复制的作用
数据冗余 主从复制实现了数据的热备份是持久化之外的一种数据冗余方式。故障恢复 当主节点出现问题时可以由从节点提供服务实现快速的故障恢复实际上是一种服务的冗余。负载均衡 在主从复制的基础上配合读写分离可以由主节点提供写服务由从节点提供读服务 即写Redis数据时应用连接主节点读Redis数据时应用连接从节点分担服务器负载尤其是在写少读多的场景下通过多个从节点分担读负载可以大大提高Redis服务器的并发量。高可用基石 除了上述作用以外主从复制还是哨兵和集群能够实施的基础因此说主从复制是 Redis高可用的基础。
2 主从复制环境搭建 编写配置文件
新建redis6379.conf
include /usr/local/redis-6.2.6/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdbredis6380.conf
include /usr/local/redis-6.2.6/redis.conf
pidfile /var/run/redis_6380.pid
port 6380
dbfilename dump6380.rdb建redis6381.conf
include /usr/local/redis-6.2.6/redis.conf
pidfile /var/run/redis_6381.pid
port 6381
dbfilename dump6381.rdb启动三台redis服务器
./redis-server ../redis6379.conf
./redis-server ../redis6380.conf
./redis-server ../redis6381.conf查看系统进程
[rootlocalhost src]# ps -ef |grep redis
root 40737 1 0 22:05 ?
00:00:00 ./redis-server *:6379
root 40743 1 0 22:05 ?
00:00:00 ./redis-server *:6380
root 40750 1 0 22:05 ?
00:00:00 ./redis-server *:6381
root 40758 40631 0 22:05 pts/0
00:00:00 grep --colorauto redis查看三台主机运行情况
#打印主从复制的相关信息
./redis-cli -p 6379
./redis-cli -p 6380
./redis-cli -p 6381
127.0.0.1:6379 info replication
127.0.0.1:6380 info replication
127.0.0.1:6381 info replication配从库不配主库
语法格式
slaveof ip port示例
在6380和6381上执行。
127.0.0.1:6380 slaveof 127.0.0.1 6379
OK127.0.0.1:6381 slaveof 127.0.0.1 6379
OK在主机上写在从机上可以读取数据
set k1 v13 主从复制原理剖析 主从复制可以分为3个阶段
连接建立阶段即准备阶段数据同步阶段命令传播阶段
复制过程大致分为6个过程 1、保存主节点master信息。
执行 slaveof 后 查看状态信息
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up2、从节点slave内部通过每秒运行的定时任务维护复制相关逻辑当定时任务发现存在新的主节点后会尝试与该节点建立网络连接 3、从节点与主节点建立网络连接
从节点会建立一个 socket 套接字从节点建立了一个端口为51234 的套接字专门用于接受主节点发送的复制命令。 4、发送ping命令
连接建立成功后从节点发送 ping 请求进行首次通信。 作用 检测主从之间网络套接字是否可用。检测主节点当前是否可以接受命令 。 4、权限验证。
如果主节点设置了 requirepass 参数则需要密码验证从节点必 须配置 masterauth 参数保证与主节点相同的密码才能通过验证 如果验证失败复制将终止从节点重新发起复制流程。
5、同步数据集。
主从复制连接正常通信后对于首次建立复制的场景主节点会把持有的数据全部发送给从节点这部分操作是耗时最长的步骤。 主从同步策略
主从刚刚连接的时候进行全量同步全同步结束后进行增量同步。当然如果有需要slave 在任何时候都可以发起全量同步。 redis 策略是无论如何首先会尝试进行增量同步如不成功要求从机进行全量同步。
6、命令持续复制。
当主节点把当前的数据同步给从节点后便完成了复制的建立流程。接下来主节点会持续地把写命令发送给从节点保证主从数据 一致性。
十一 Redis哨兵机制
1 哨兵监控概述 Redis主从复制缺点
当主机 Master 宕机以后我们需要人工解决切换。 暴漏问题一旦主节点宕机写服务无法使用就需要手动去切换重新选取主节点手动设置主从关系。 主从切换技术
当主服务器宕机后需要手动把一台从服务器切换为主服务器这就需要人工干预费事费力还会造成一段时间内服务不可用。这不是一种推荐的方式更多时候我们优先考虑哨兵模式。
哨兵概述
哨兵模式是一种特殊的模式首先Redis提供了哨兵的命令哨兵是 一个独立的进程作为进程它会独立运行。其原理是哨兵通过发送命令等待Redis服务器响应从而监控运行的多个Redis实例。 哨兵作用
集群监控负责监控redis master和slave进程是否正常工作消息通知如果某个redis实例有故障那么哨兵负责发送消息作为报警通知给管理员故障转移如果master node挂掉了会自动转移到slave node上配置中心如果故障转移发生了通知client客户端新的master地址
2 哨兵监控环境搭建 新建sentinel-26379.conf文件
#端口
port 26379
#守护进程运行
daemonize yes
#日志文件
logfile 26379.log
sentinel monitor mymaster 127.0.0.1 6379 2 参数 sentinel monitor mymaster 192.168.92.128 6379 2 配置的含义是 该哨兵节点监控192.168.92.128:6379这个主节点该主节点的名称是mymaster最后的2的含义与主节点的故障判定有关至少需要2个哨兵节点同意才能判定主节点故障并进行故障转移。 新建sentinel-26380.conf文件
#端口
port 26380
#守护进程运行
daemonize yes
#日志文件
logfile 26380.log
sentinel monitor mymaster 127.0.0.1 6379 2
新建sentinel-26381.conf文件
#端口
port 26381
#守护进程运行
daemonize yes
#日志文件
logfile 26381.log
sentinel monitor mymaster 127.0.0.1 6379 2
哨兵节点的启动两种方式
redis-sentinel ../sentinel-26379.conf
redis-sentinel ../sentinel-26380.conf
redis-sentinel ../sentinel-26381.conf查看哨兵节点状态
[rootlocalhost src]# ./redis-cli -p 26379
127.0.0.1:26379
127.0.0.1:26379
127.0.0.1:26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:namemymaster,statusok,address192.
168.66.100:6379,slaves2,sentinels33 哨兵工作原理详解 监控阶段 注意 sentinel(哨兵1)-----向master(主)和slave(从)发起info拿到全信息。 sentinel(哨兵2)-----向master(主)发起info就知道已经存在的sentinel(哨兵1)的信息并且 连接slave(从)。 sentinel(哨兵2)-----向sentinel(哨兵1)发起subscribe(订阅)。 通知阶段
sentinel不断的向master和slave发起通知收集信息。
故障转移阶段
通知阶段sentinel发送的通知没得到master的回应就会把master 标记为SRI_S_DOWN,并且把master的状态发给各个sentinel其他 sentinel听到master挂了说我不信我也去看看并把结果共享 给各个sentinel当有一半的sentinel都认为master挂了的时候 就会把master标记为SRI_0_DOWN。 问题来了 这时就要把master给换掉了到底谁当Master呢。 投票方式 方式 自己最先接到哪个sentinel的竞选通知就会把票投给它。 剔除一些情况 不在线的 响应慢的 与原来master断开时间久的 优先级原则 4 故障转移 概述
演示当主节点发生故障时哨兵的监控和自动故障转移功能。
演示故障转移
使用kill命令杀掉主节点
ps aux |grep redis
kill -9 pid查看哨兵节点信息
如果此时立即在哨兵节点中使用info Sentinel命令查看。
[rootlocalhost src]# ./redis-cli -p 26379
127.0.0.1:26379 info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:namemymaster,statusok,address127.0.0.1:6380,slaves2,sentinels3注意 哨兵发现主节点故障并转移需要一段时间。 重启6379节点
[rootlocalhost src]# ./redis-cli info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up配置文件都会被改写
故障转移阶段哨兵和主从节点的配置文件都会被改写
include /usr/local/redis-6.2.6/redis.conf
pidfile /var/run/redis_6379.pid
port 6379
dbfilename dump6379.rdb
# Generated by CONFIG REWRITE
daemonize yes
protected-mode no
appendonly yes
slowlog-max-len 1200
slowlog-log-slower-than 1000
save 5 1
user default on nopass ~* * all
dir /usr/local/redis
replicaof 127.0.0.1 6380结论
哨兵系统中的主从节点与普通的主从节点并没有什么区别故障发现和转移是由哨兵来控制和完成的。哨兵节点本质上是redis节点。每个哨兵节点只需要配置监控主节点便可以自动发现其他的哨兵节点和从节点。在哨兵节点启动和故障转移阶段各个节点的配置文件会被重写(config rewrite)。
十二 Redis集群
1 Cluster模式概述 Redis有三种集群模式
主从模式Sentinel模式Cluster模式
哨兵模式的缺点 缺点 当master挂掉的时候sentinel 会选举出来一个 master选举的时候是没有办法去访问 Redis的会存在访问瞬断的情况哨兵模式对外只有master节点可以写slave节点只能用于读。尽管Redis单节点最多支持 10W的QPS但是在电商大促的时候写数据的压力全部在master上。Redis的单节点内存不能设置过大若数据过大在主从同步将会很慢在节点启动的时候时间特别长 Cluster模式概述 Redis集群是一个由多个主从节点群组成的分布式服务集群它具有复制、高可用和分片特性。 Redis集群的优点
Redis集群有多个master可以减小访问瞬断问题的影响Redis集群有多个master可以提供更高的并发量Redis集群可以分片存储这样就可以存储更多的数据
2 Cluster模式搭建 Redis的集群搭建最少需要3个master节点我们这里搭建3个 master每个下面挂一个slave节点总共6个Redis节点 环境准备
第1台机器 192.168.66.108 8001端口 8002端口
第2台机器 192.168.66.109 8001端口 8002端口
第3台机器 192.168.66.110 8001端口 8002端口创建文件夹
mkdir -p /usr/local/redis/redis-cluster/8001 /usr/local/redis/redis-cluster/8002拷贝配置文件
将redis安装目录下的 redis.conf 文件分别拷贝到8001目录下
cp /usr/local/redis/redis.conf /usr/local/redis/redis-cluster/8001修改redis.conf文件以下内容
/usr/local/redis/redis-cluster/8001/redis.conf
port 8001
daemonize yes
pidfile /var/run/redis_8001.pid
#指定数据文件存放位置必须要指定不同的目录位置不然
会丢失数据
dir /usr/local/redis/redis-cluster/8001/
#启动集群模式
cluster-enabled yes
#集群节点信息文件这里800x最好和port对应上
cluster-config-file nodes-8001.conf
# 节点离线的超时时间
cluster-node-timeout 5000
#去掉bind绑定访问ip信息
#bind 127.0.0.1
#关闭保护模式
protected-mode no
#启动AOF文件
appendonly yes
#如果要设置密码需要增加如下配置
#设置redis访问密码
#requirepass baizhan
#设置集群节点间访问密码跟上面一致
#masterauth baizhan文件拷贝到8002文件夹
#将8001修改为8002
cp /usr/local/redis/redis-cluster/8001/redis.conf /usr/local/redis/redis-cluster/8002
# 批量修改字符串
:%s/8001/8002/g将本机机器上的文件拷贝到另外两台机器上
# 第二台机器
scp /usr/local/redis/redis-cluster/8001/redis.conf root192.168.66.109:/usr/local/redis/redis-cluster/8001/
scp /usr/local/redis/redis-cluster/8002/redis.conf root192.168.66.109:/usr/local/redis/redis-cluster/8002/
# 第三台机器
scp /usr/local/redis/redis-cluster/8001/redis.conf root192.168.66.110:/usr/local/redis/redis-cluster/8001/
scp /usr/local/redis/redis-cluster/8002/redis.conf root192.168.66.110:/usr/local/redis/redis-cluster/8002/分别启动这6个redis实例
/usr/local/redis/src/redis-server /usr/local/redis/redis-cluster/8001/redis.conf
/usr/local/redis/src/redis-server /usr/local/redis/redis-cluster/8002/redis.conf检查是否启动成功
ps -ef |grep redis使用redis-cli创建整个redis集群
/usr/local/redis/src/redis-cli --cluster create --cluster-replicas 1 \
192.168.66.108:8001 192.168.66.108:8002 \
192.168.66.109:8001 192.168.66.109:8002 \
192.168.66.110:8001 192.168.66.110:8002 \参数 -a密码 –cluster-replicas 1表示1个master下挂1个slave --cluster-replicas 2表示1个master 下挂2个slave。 注意记得关闭防火墙 查看帮助命令
redis-cli --cluster help参数 create创建一个集群环境host1:port1 … hostN:portNcall可以执行redis命令add-node将一个节点添加到集群里第一个参数为新节点的ip:port第二个参数为集群中任意一个已经存在的节点的ip:portdel-node移除一个节点reshard重新分片check检查集群状态 验证集群
连接任意一个客户端
/usr/local/redis/src/redis-cli -a redis-pw -c -h 192.168.66.108 -p 8001参数 ‐a表示服务端密码‐c表示集群模式-h指定ip地址-p表示端口号 查看集群的信息
cluster info3 Cluster模式原理分析 Redis Cluster将所有数据划分为16384个slots(槽位)每个节点负 责其中一部分槽位。槽位的信息存储于每个节点中。只有master节点会被分配槽位slave节点不会分配槽位。 槽位定位算法 k1 127001 Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整 数值然后用这个整数值对 16384 进行取模来得到具体槽位。 HASH_SLOT CRC16(key) % 16384 命令执行
set k1 v1注意 根据k1计算出的槽值进行切换节点并存入数据。不在一个slot 下的键值是不能使用mget、mset等多建操 可以通过{}来定义组的概念从而是key中{}内相同内容的键值对放 到同一个slot中。
mset k1{test} v1 k2{test} v2 k3{test} v3故障恢复
查看节点
192.168.66.108:8001 cluster nodes杀死Master节点
lsof -i:8001
kill -9 pid观察节点信息 4 Java操作Redis集群
SpringBoot 整合 Redis
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring‐boot‐starter‐data‐redis/artifactId
/dependency配置文件
##单服务器
spring.redis.cluster.nodes192.168.159.129:7
001,192.168.159.129:7002,192.168.159.129:700
3,192.168.159.129:7004,192.168.159.129:7005,
192.168.159.129:7006
## 连接池最大连接数使用负值表示没有限制
spring.redis.pool.max-active300
## Redis数据库索引(默认为0)
spring.redis.database0
## 连接池最大阻塞等待时间使用负值表示没有限制
spring.redis.pool.max-wait-1
## 连接池中的最大空闲连接
spring.redis.pool.max-idle100
## 连接池中的最小空闲连接
spring.redis.pool.min-idle20
## 连接超时时间毫秒
spring.redis.timeout60000java代码 RestController
public class TestController {Autowiredprivate StringRedisTemplate stringRedisTemplate;RequestMapping(/test_cluster)public void testCluster() throwsInterruptedException {stringRedisTemplate.opsForValue().set(user:name, baizhan);System.out.println(stringRedisTemplate.opsForValue().get(user:name));}
}十三 Redis企业级解决方案
1 Redis脑裂 什么是Redis的集群脑裂
Redis的集群脑裂是指因为网络问题导致Redis Master节点跟 Redis slave节点和Sentinel集群处于不同的网络分区此时因为 sentinel集群无法感知到master的存在所以将slave节点提升为 master节点。 注意 此时存在两个不同的master节点就像一个大脑分裂成了两 个。集群脑裂问题中如果客户端还在基于原来的master节点继续写入数据那么新的Master节点将无法同步这些数据当网络问题解决之后sentinel集群将原先的Master节点降为 slave节点此时再从新的master中同步数据将会造成大量的数据丢失。 解决方案
redis.conf配置参数
min-replicas-to-write 1
min-replicas-max-lag 5参数 第一个参数表示最少的slave节点为1个 第二个参数表示数据复制和同步的延迟不能超过5秒 配置了这两个参数如果发生脑裂原Master会在客户端写入操作的时候拒绝请求。这样可以避免大量数据丢失。 2 缓存预热 缓存冷启动
缓存中没有数据由于缓存冷启动一点数据都没有如果直接就对外提供服务了那么并发量上来Mysql就裸奔挂掉了。 缓存冷启动场景
新启动的系统没有任何缓存数据在缓存重建数据的过程中系统性能和数据库负载都不太好所以最好是在系统上线之前就把要缓存的热点数据加载到缓存中这种缓存预加载手段就是缓存预热。 解决思路
提前给redis中灌入部分数据再提供服务如果数据量非常大就不可能将所有数据都写入redis因为数据量太大了第一是因为耗费的时间太长了第二根本redis容纳不下所有的数据需要根据当天的具体访问情况实时统计出访问频率较高的热数据然后将访问频率较高的热数据写入redis中肯定是热数据也比较多我们也得多个服务并行读取数据去写并行的分布式的缓存预热 3 缓存穿透 概念
缓存穿透是指缓存和数据库中都没有的数据而用户不断发起请 求如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者攻击会导致数据库压力过大。 解释 缓存穿透是指用户查询数据在数据库没有自然在缓存中也不会有。这样就导致用户查询的时候在缓存中找不到每次都要去数据库再查询一遍然后返回空相当于进行了两次无用的查询。这样请求就绕过缓存直接查数据库这也是经常提的缓存命中率问题。 解决方案
对空值缓存如果一个查询返回的数据为空不管数据是否存在我们仍然把这个空结果缓存 设置空结果的过期时间会很短最长不超过5分钟。布隆过滤器如果想判断一个元素是不是在一个集合里一般想到的是将集合中所有元素保存起来然后通过比较确定。
布隆过滤器
什么是布隆过滤器
布隆过滤器是一种数据结构比较巧妙的概率型数据结构 probabilistic data structure特点是高效地插入和查询可以用来告诉你 “某样东西一定不存在或者可能存在”。 注意 布隆说不存在一定不存在布隆说存在你要小心了它有可能不存在。 代码实现
引入hutool包
dependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.7.17/version
/dependencyjava代码实现
// 初始化 注意 构造方法的参数大小10 决定了布隆过滤器BitMap的大小BitMapBloomFilter filter new BitMapBloomFilter(10);filter.add(123);filter.add(abc);filter.add(ddd);boolean abc filter.contains(abc);System.out.println(abc);4 缓存击穿 概念
某一个热点 key在缓存过期的一瞬间同时有大量的请求打进来由于此时缓存过期了所以请求最终都会走到数据库造成瞬时数据库请求量大、压力骤增甚至可能打垮数据库。
解决方案
互斥锁在并发的多个请求中只有第一个请求线程能拿到锁并执行数据库查询操作其他的线程拿不到锁就阻塞等着等到第一个线程将数据写入缓存后其他线程直接查询缓存。热点数据不过期直接将缓存设置为不过期然后由定时任务去异步加载数据更新缓存。
代码实现 public String get(String key) throws InterruptedException {String value jedis.get(key);// 缓存过期if (value null){// 设置3分钟超时防止删除操作失败的时候 下一次缓存不能load dbLong setnx jedis.setnx(key mutex, 1);jedis.pexpire(key mutex, 3 * 60);// 代表设置成功if (setnx 1){// 模拟数据库查询value db.get(key);//保存缓存jedis.setex(key,3*60,value);jedis.del(key mutex);return value;}else {// 这个时候代表同时操作的其他线程已经load db并设置缓存了。 需要重新获取缓存Thread.sleep(50);// 重试return get(key);}}else {return value;}}
5 缓存雪崩 概念
缓存雪崩是指在我们设置缓存时采用了相同的过期时间导致缓存在某一时刻同时失效请求全部转发到DBDB瞬时压力过重雪崩。
缓存正常从Redis中获取示意图如下 缓存失效瞬间示意图如下 解决方案
过期时间打散既然是大量缓存集中失效那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间使得每个 key 的过期时间分布开来不会集中在同一时刻失效。热点数据不过期该方式和缓存击穿一样也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。加互斥锁: 该方式和缓存击穿一样按 key 维度加锁对于同一个 key只允许一个线程去计算 其他线程原地阻塞等待第一个线程的计算结果然后直接走缓存即可。
加锁排队代码如下
public Object GetProductListNew(String cacheKey) {int cacheTime 30;String lockKey cacheKey;// 获取key的缓存String cacheValue jedis.get(cacheKey);// 缓存未失效返回缓存if (cacheValue ! null) {return cacheValue;} else {// 枷锁synchronized(lockKey) {// 获取key的value值cacheValue jedis.get(cacheKey);if (cacheValue ! null) {return cacheValue;} else {//这里一般是sql查询数据// db.set(key)// 添加缓存jedis.set(cacheKey,);}}return cacheValue;}} 注意 加锁排队只是为了减轻数据库的压力并没有提高系统吞吐量。 6 Redis开发规范 key设计技巧
把表名转换为key前缀如 tag:把第二段放置用于区分key的字段对应msyql中主键的列名如 user_id第三段放置主键值如 2,3,4第四段写存储的列名
user_idnameage1baizhan182itbaizhan20
示例
# 表名 主键 主键值 存储列名字
set user:user_id:1:name baizhan
set user:user_id:1:age 20
#查询这个用户
keys user:user_id:9*value设计
拒绝bigkey
防止网卡流量、慢查询string类型控制在10KB以内hash、 list、set、zset元素个数不要超过5000。
命令使用
1、禁用命令
禁止线上使用keys、flushall、flushdb等通过redis的rename机 制禁掉命令或者使用scan的方式渐进式处理。
2、合理使用select
redis的多数据库较弱使用数字进行区分很多客户端支持较差 同时多业务用多数据库实际还是单线程处理会有干扰。
3、使用批量操作提高效率
原生命令例如mget、mset。非原生命令可以使用pipeline提高效率。 注意 但要注意控制一次批量操作的元素个数(例如500以内实际也和元素字节数有关)。 4、不建议过多使用Redis事务功能
Redis的事务功能较弱(不支持回滚)而且集群版本(自研和官方)要 求一次事务操作的key必须在一个slot上。
客户端使用
Jedis https://github.com/xetorthio/jedis 重点推荐Spring Data redis https://github.com/spring-projects/spring-data-redis 使用Spring框架时推荐Redisson https://github.com/mrniko/redisson 分布式锁、阻塞队列的时重点推荐
1、避免多个应用使用一个Redis实例
不相干的业务拆分公共数据做服务化。
2、使用连接池
可以有效控制连接同时提高效率标准使用方式
//执行命令如下
Jedis jedis null;
try {jedis jedisPool.getResource();//具体的命令jedis.executeCommand()
} catch (Exception e) {logger.error(op key {} error: e.getMessage(), key, e);
} finally {//注意这里不是关闭连接在JedisPool模式下Jedis会被归还给资源池。if (jedis ! null) {jedis.close();}
}7 数据一致性 缓存已经在项目中被广泛使用在读取缓存方面大家没啥疑问 都是按照下图的流程来进行业务操作。 缓存说明 从理论上来说给缓存设置过期时间是保证最终一致性的解决方案。 三种更新策略 先更新数据库再更新缓存 先删除缓存再更新数据库 先更新数据库再删除缓存
先更新数据库再更新缓存
这套方案大家是普遍反对的。为什么呢
线程安全角度
同时有请求A和请求B进行更新操作那么会出现:
1线程A更新了数据库 2线程B更新了数据库 3线程B更新了缓存 4线程A更新了缓存
这就出现请求A更新缓存应该比请求B更新缓存早才对但是因为网络等原因B却比A更早更新了缓存。这就导致了脏数据因此不考虑。
先删缓存再更新数据库
该方案会导致不一致的原因是。同时有一个请求A进行更新操作另 一个请求B进行查询操作。那么会出现如下情形:
1请求A进行写操作删除缓存 2请求B查询发现缓存不存在 3请求B去数据库查询得到旧值 4请求B将旧值写入缓存 5请求A将新值写入数据库 注意 该数据永远都是脏数据。 先更新数据库再延时删缓存 这种情况存在并发问题吗
1缓存刚好失效 2请求A查询数据库得一个旧值 3请求B将新值写入数据库 4请求B删除缓存 5请求A将查到的旧值写入缓存 发生这种情况的概率又有多少? 发生上述情况有一个先天性条件就是步骤3的写数据库操作比步骤2的读数据库操作耗时更短才有可能使得步骤 4先于步骤5。可是大家想想数据库的读操作的速 度远快于写操作的因此步骤3耗时比步骤2更短这 一情形很难出现。