Redis
这个版本是我跟着视频教程做的笔记
教程来源:Redis 6 入门到精通
教程视频地址:https://www.bilibili.com/video/BV1Rv41177Af
特点
- Redis是一个开源的key-value存储系统
- 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)
- 这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。
- Redis支持各种不同方式的排序。
- 为了保证效率,数据都是缓存在内存中。
- Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件
- 实现了master-slave(主从)同步
作用
- 配合关系型数据库做高速缓存
- 多样的数据结构存储持久化数据
不适用场景
- 需要事务支持
- 基于sql的结构化查询存储,处理复杂的关系,需要即席查询。
Redis的安装
官网直接下载 在Linux中安装
- 在linux中检查是否装有C 语言的编译环境
输入命令 gcc --version
如果有:

如果没有:
安装C 语言的编译环境
**yum** **install centos-release-scl scl-utils-build**
**yum install -y devtoolset-8-toolchain**
**scl enable devtoolset-8 bash**
-
将windows中下载好的redis-6.2.1.tar.gz传入到Linux中的 /opt目录

redis-6.2.1.tar.gz压缩包在**/opt**目录 -
进入目录 解压
进入目录 cd /opt
解压命令 tar -zxvf redis-6.2.1.tar.gz
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uRZqLMxM-1636112884663)(E:\真.学习资料\易点\Redis\image-20211031105917057.png)]
-
解压完成后进入目录
cd redis-6.2.1redis-6.2.1 的东西都在这里面
-
执行make命令
在redis-6.2.1目录下再次执行make命令(将rides文件进行编译)
编译完成出现:

-
进行安装
make install安装成功出现

默认安装在 usr/local/bin 目录下

redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何 redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲 redis-check-dump:修复有问题的dump.rdb文件 redis-sentinel:Redis集群使用 redis-server:Redis服务器启动命令 redis-cli:客户端,操作入口 -
备份rides.conf
将 redis-6.2.1 中的 rides.conf 复制到 /etc
cp /opt/redis-3.2.5/redis.conf -
更改redis.conf配置
修改redis.conf(128行)文件将里面的daemonize no 改成 yes,让服务在后台启动
Redis的后台启动

原子性
所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)
-
在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。
-
在多线程中,不能被其它进程(线程)打断的操作就叫原子操作
Redis单命令的原子性主要得益于Redis的单线程
Redis五大数据类型
获得redis常见数据类型操作命令http://www.redis.cn/commands.html
key
keys * 查看当前库所有key (匹配:keys *1)
exists key 判断某个key是否存在
type key 查看你的key是什么类型
del key 删除指定的key数据
unlink key 根据value选择非阻塞删除仅将keys从keyspace元数据中删除真正的删除会在后续异步操作
expire key 10 10秒钟:为给定的key设置过期时间
ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
select 命令切换数据库
dbsize 查看当前数据库的key的数量
flushdb 清空当前库
flushall 通杀全部库
String
String是Redis最基本的类型 ,一个key对应一个value
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。
数据结构
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。
是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配.

如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M。
命令
set <key> <value> 添加键值对get <key> 查询对应键值append <key> <value> 将给定的<value> 追加到原值的末尾strlen <key> 获得值的长度setnx <key> <value> 只有在 key不存在时 设置 key 的值incr <key> 将 key 中储存的数字值加1 只能对数字值操作,如果为空,新增值为1decr <key> 将 key 中储存的数字值减1 只能对数字值操作,如果为空,新增值为-1incrby <key> <步长> 将 key 中储存的数字值增。自定义步长decrby <key> <步长> 将 key 中储存的数字值减。自定义步长getset <key> <value> 以新换旧,设置了新值同时获得旧值。setex <key> <过期时间> <value> 设置键值的同时,设置过期时间,单位秒一下命令满足原子性((原子性,有一个失败则都失败))mset <key1> <value1> <key2> <value2> ..... 同时设置一个或多个 key-value对 mget <key1> <key2> <key3> ..... 同时获取一个或多个 value msetnx <key1> <value1> <key2>< value2> ..... 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在 getrange <key> <起始位置> <结束位置> 获得值的范围,类似java中的substring,前包,后包 (左闭右闭)setrange <key> <起始位置> < value> 用 <value> 覆写<key>所储存的字符串值,从<起始位置>开始(索引从0开始)
说明
incr 将 key 中储存的数字值加1 只能对数字值操作,如果为空,新增值为1

getrange <起始位置> <结束位置>
获得值的范围,类似java中的substring,前包,后包 (左闭右闭)

setrange <起始位置> < value>
用 覆写所储存的字符串值,从<起始位置>开始(索引从0开始)

getset 以新换旧,设置了新值同时获得旧值。

List
单键多值
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
数据结构
List的数据结构为快速链表quickList
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。

-
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。它将所有的元 素紧挨着一起存储,分配的是一块连续的内存。
-
当数据量比较多的时候才会改成quicklist
-
普通的链表需要的附加指针空间太大,会比较浪费空间
-
所以Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。

命令
lpush/rpush <key><value1><value2><value3> 从左边/右边插入一个或多个值。lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值光键亡(里面的值吐完了,键也就没了)rpoplpush <key1> <key2> 从<key1>列表右边吐出一个值,插到<key2>列表左边lrange <key> <start> <stop> 按照索引下标获得元素(从左到右) 0左边第一个,-1右边第一个(0 -1表示获取所有)lindex <key> <index> 按照索引下标获得元素(从左到右)llen <key> 获得列表长度linsert <key> before <value> <newvalue> 在<value>的后面插入<newvalue>插入值 lrem <key> <n> <value> 从左边删除n个value(从左到右)lset<key><index><value> 将列表key下标为index的值替换成value
说明
rpoplpush 从列表右边吐出一个值,插到列表左边

lrem 从左边删除n个value(从左到右)

set
Redis set功能与list类似,特殊之处在于set是可以自动排重的,
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择
并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
方法
sadd <key> <value1> <value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略smembers <key> 取出该集合的所有值sismember <key> < value> 判断集合<key>是否为含有该<value>值,有1,没有0scard <key> 返回该集合的元素个数srem <key> <value1> <value2> .... 删除集合中的某个元素spop <key> 随机从该集合中吐出一个值srandmember <key> <n> 随机从该集合中取出n个值。不会从集合中删除smove <source><destination>value 把集合中一个值从一个集合移动到另一个集合sinter <key1> <key2> 返回两个集合的交集元素sunion <key1> <key2> 返回两个集合的并集元素sdiff <key1> <key2> 返回两个集合的差集元素(key1中的,不包含key2中的)
说明
smove value 把集合中一个值从一个集合移动到另一个集合

smembers 取出该集合的所有值。

数据结构
Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
Set数据结构是dict字典,字典是用哈希表实现的
Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一
样,它的内部也使用hash结构,所有的value都指向同一个内部值
sorted set
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。
不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的
因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。
命令
zadd <key> <score1> <value1> <score2> <value2>… 将一个或多个 member 元素及其 score 值加入到有序集 key 当中zrange <key> <start> <stop> 返回有序集key中,下标在<start><stop>之间的元素zrange <key> <start> <stop> WITHSCORES 返回有序集key中,下标在<start><stop>之间的元素和评分zrangebyscore <key> min max [withscores] 返回有序集key中评分介于min和max之间(包括等于min或max)的成员。 从小到大次序排列。 zrevrangebyscore key max min [withscores] 同上 从大到小排序zincrby <key> <increment> <value> 为元素的score加上增量zrem <key> <value> 删除该集合下,指定值的元素 zcount <key> <min> <max> 统计该集合,分数区间内的元素个数 zrank <key> <value> 返回该值在集合中的排名,从0开始
说明
zadd …
将一个或多个 member 元素及其 score 值加入到有序集 key 当中

zrange
返回有序集 key 中,下标在

zrange
返回有序集 key 中,下标在

数据结构
SortedSet(zset)是Redis提供的一个非常特别的数据结构
一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。
zset底层使用了两个数据结构
- hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。

- 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

从第2层开始,1节点比51节点小,向后比较。21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下在第0层,51节点为要查找的节点,节点被找到,共查找4次。从此可以看出跳跃表比有序链表效率要高
Hash
Redis hash 是一个键值对集合。
**Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。**类似Java里面的Map<String,Object>

命令
hset <key> <field> <value> 给<key>集合中的 <field>键赋值<value>hget <key1> <field> 从<key1>集合<field>取出 value hmset <key1> <field1> <value1> <field2> <value2>... 批量设置hash的值hexists <key1> <field> 查看哈希表 key 中,给定域 field 是否存在hkeys <key> 列出该hash集合的所有fieldhvals <key> 列出该hash集合的所有valuehincrby <key> <field> <increment> 为哈希表 key 中的域 field 的值加上增量 1 -1hsetnx <key> <field> <value> 将哈希表key中的域field的值设置为value,当且仅当域 field不存在
说明
hincrby 为哈希表 key 中的域 field 的值加上增量 1 -1

hsetnx 将哈希表key中的域field的值设置为value,当且仅当域 field不存在
(就相当于于追加一个属性 )

数据结构
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
Redis新数据类型
Bitmaps

Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。
Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量
setbit
setbit <key> <offset> <value> 设置Bitmaps中某个偏移量的值(0或1)(offset:偏移量从0开始)
例如:
每个独立用户是否访问过网站存放在Bitmaps中, 将访问的用户记做1, 没有访问的用户记做0, 用偏移量作为用户的id
设置键的第offset个位的值(从0算起) , 假设现在有20个用户,userid=1, 6, 11, 15, 19的用户对网站进行了访问, 那么当前Bitmaps初始化结果如图


getbit
getbit <key> <offset> 获取Bitmaps中某个偏移量的值(获取键的第offset位的值(从0开始算))
例如:
获取id=8的用户是否在2020-11-06这天访问过, 返回0说明没有访问过:

因为100根本不存在,所以也是返回0
bitcount
统计字符串被设置为1的bit数 通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行

bitop
bitop是一个复合操作, 它可以做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中
bitop and(or/not/xor) <destkey> [key…]

HyperLogLog
基数
不重复元素
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)就为5。
基数估计就是在误差可接受的范围内,快速计算基数。
pfadd
将所有元素添加到指定HyperLogLog数据结构中。如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0
pfadd <key> < element> [element ...] 添加指定元素到 HyperLogLog 中

pfcount
pfcount <key> [key ...] 计算HLL的近似基数

pfmerge
pfmerge <destkey> <sourcekey> [sourcekey ...] 将一个或多个HLL合并后的结果存储在另一个HLL中
比如每月活跃用户可以使用每天的活跃用户来合并计算可得

Redis配置文件
打开配置文件
根目录下 vi /etc/redis.conf
Redis的发布和订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式
发送者 (pub) 发送消息,订阅者 (sub) 接收消息
Redis 客户端可以订阅任意数量的频道
redis订阅
客户端可以订阅频道

当给这个频道发布消息后,消息就会发送给订阅的客户端

发布订阅命令行实现
1、 打开一个客户端订阅channel1
SUBSCRIBE channel1

2、打开另一个客户端,给channel1发布消息hello (返回的1是订阅者数量)
publish channel1 hello

3、打开第一个客户端可以看到发送的消息

Jedsi
idea连接redis
导入maven
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version></dependency>
配置Redis.conf
首先将linux中的redis配置文件redis.conf更改三个地方
- 注释掉bind
在第70行左右 (命令模式直接输入70G就直接跳转)

- 更改protected-mode为no
在第88行

- 添加登录密码
在第789行的位置(更改为自己的密码我这里是123456)
这三个修改完成后Esc然后:wq保存退出
- 登录redis
正常登录只不过需要输入密码auth 密码

- 需要关闭防火墙
在一个新的终端输入
#关闭防火墙;systemctl stop firewalld.service#禁止防火墙启动;systemctl disable firewalld.service
连接idea
//创建jedis Jedis jedis = new Jedis("192.168.10.100",6379); //测试 String ping = jedis.ping(); System.out.println(ping);
案例
手机验证码
1、输入手机号,点击发送后随机生成6位数字码,2分钟有效
2、输入验证码,点击验证,返回成功或失败
3、每个手机号每天只能输入3次

springboot整合redis
导入mevan
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- spring2.X集成redis所需common-pool2--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.6.0</version> </dependency>
配置redis配置
application.properties配置redis配置
#Redis服务器地址spring.redis.host=192.168.140.136#Redis服务器连接端口spring.redis.port=6379#Redis数据库索引(默认为0)spring.redis.database= 0#连接超时时间(毫秒)spring.redis.timeout=1800000#连接池最大连接数(使用负值表示没有限制)spring.redis.lettuce.pool.max-active=20#最大阻塞等待时间(负数表示没限制)spring.redis.lettuce.pool.max-wait=-1#连接池中的最大空闲连接spring.redis.lettuce.pool.max-idle=5#连接池中的最小空闲连接spring.redis.lettuce.pool.min-idle=0
添加redis配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CachingConfigurerSupport;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheConfiguration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializationContext;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;@EnableCaching@Configurationpublic class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setConnectionFactory(factory);//key序列化方式 template.setKeySerializer(redisSerializer);//value序列化 template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化 template.setHashValueSerializer(jackson2JsonRedisSerializer); return template; } @Bean public CacheManager cacheManager(RedisConnectionFactory factory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//解决查询缓存转换异常的问题 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om);// 配置序列化(解决乱码的问题),过期时间600秒 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(600)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; }}
测试
使用redisTemplate(模板)的opsForValue调用对应的方法
@RestController@RequestMapping(value = "/redisTest")public class RedisTestController { @Autowired private RedisTemplate redisTemplate; @GetMapping public String testRedis(){ //设置值到redis redisTemplate.opsForValue().set("k1","123"); //从redis取出值 String k1 = (String) redisTemplate.opsForValue().get("k1"); return k1; }}
Redis事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队
Multi、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行 (组队)
直到输入Exec后,Redis会将之前的命令队列中的命令依次执行 (执行)
组队的过程中可以通过discard来放弃组队 (中断)

事务错误处理
正常情况
组队成功,提交成功

组队出错
组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消

组队阶段报错,提交失败(全部失败)

执行出错
如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚

组队成功,提交有成功有失败情况(部分成功)

锁
悲观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
WATCH key [key …]
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,
如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
持久化操作
RDB
基于内存快照,
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里
RDB持久化流程
redis 不会自己直接将保存为 dump.rdb会单独创建(fork)一个子进程来进行持久化会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件

整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
RDB的缺点是最后一次持久化后的数据可能丢失
Fork
Redis运用fork()来完成RDB的持久化操作。
Fork的作用是复制一个与当前进程一样的进程
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”
fork()用于创建一个子进程,注意是子进程,不是子线程。fork()出来的进程共享其父类的内存数据。一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。仅仅是共享fork()出子进程的那一刻的内存数据,后期主进程修改数据对子进程不可见,同理,子进程修改的数据对主进程也不可见。
比如:A进程fork()了一个子进程B,那么A进程就称之为主进程,这时候主进程子进程所指向的内存空间是同一个,所以他们的数据一致。但是A修改了内存上的一条数据,这时候B是看不到的,A新增一条数据,删除一条数据,B都是看不到的。而且子进程B出问题了,对我主进程A完全没影响,我依然可以对外提供服务,但是主进程挂了,子进程也必须跟随一起挂。
dump.rdb文件
dump.rdb(redis持久化后的内容)(rdb文件都是二进制,很小)
在redis.conf中配置文件名称,默认为dump.rdb

配置
rdb保存位置
rdb文件的保存路径,也可以修改。默认为Redis启动时命令行所在的目录下 dir “/myredis/”

Save
格式:save <秒钟> < 写操作次数>
默认是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。
也可以选择禁用:不设置save指令,或者给save传入空字符串

rdb的备份
先通过config get dir 查询rdb文件的目录 再将*.rdb的文件拷贝到别的地方
rdb的恢复
关闭Redis
先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb
启动Redis, 备份数据会直接加载
命令
save :save时只管保存,其它不管,全部阻塞。手动保存。不建议。
bgsave:Redis会在后台异步进行快照操作, 快照同时还可以响应客户端请求。
lastsave :获取最后一次成功执行快照的时间
优缺点
优点
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
- 节省磁盘空间
- 恢复速度快
缺点
- Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
- 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改
停止RDB
动态停止RDB:redis-cli config set save “”#save后给空值,表示禁用保存策略
AOF
基于操作日志
以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
AOF持久化流程
客户端的请求写命令会被append追加到AOF缓冲区内
AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;

AOF默认不开启
可以在redis.conf中配置文件名称,默认为 appendonly.aof
AOF文件的保存路径,同RDB的路径一致。为Redis启动时命令行所在的目录下 dir “/myredis/”
(AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失))
AOF启动/修复/恢复
AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。
正常恢复
修改配置文件中默认的appendonly no,改为yes
将有数据的aof文件复制一份保存到对应目录
恢复:重启redis然后重新加载
异常恢复
修改配置文件中默认的appendonly no,改为yes
如遇到AOF文件损坏,通过/usr/local/bin目录下 使用redis-check-aof–fix appendonly.aof进行恢复
备份被写坏的AOF文件
恢复:重启redis,然后重新加载
AOF同步频率设置
appendfsync always
始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好
appendfsync everysec
每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
appendfsync no
redis不主动进行同步,把同步时机交给操作系统。
Rewrite压缩
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof
优缺点
优点
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
缺点
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复不能。
总结
官方推荐两个都启用。
如果对数据不敏感,可以选单独用RDB。
不建议单独用 AOF,因为可能会出现Bug。
如果只是做纯内存缓存,可以都不用
l RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储
l AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.
l Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大
l 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.
l 同时开启两种持久化方式
l 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据, 因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
l RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
l 建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份), 快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
Redis主从复制
主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主

读写分离,性能扩展
容灾快速恢复(其中一个从挂了可以切换到其他从)
创建主从复制
创建主
1.创建/myredis文件夹,复制redis.conf配置文件到文件夹中
[root@localhost ~]# mkdir /myredis[root@localhost ~]# cd /myredis/[root@localhost myredis]# ls[root@localhost myredis]# cp /etc/redis.conf /myredis/redis.conf[root@localhost myredis]# ll总用量 84-rw-r--r--. 1 root root 83376 11月 2 08:59 redis.conf
创建从
2.在新建/myredis文件夹下创建3个不同的 redisXxxx.conf,填写以下内容
[root@localhost myredis]# vi redis6379.conf(创建并进入redis6379.conf中)
然后填写
include /myredis/redis.conf (include 复制redis.conf文件进来)pidfile /var/run/redis_6379.pid (相关配置)port 6379dbfilename dump6379.rdb

将从复制成三份
[root@localhost myredis]# cp redis6379.conf redis6380.conf[root@localhost myredis]# vi redis6380.conf [root@localhost myredis]# cp redis6379.conf redis6381.conf[root@localhost myredis]# ll总用量 96-rw-r--r--. 1 root root 94 11月 2 09:08 redis6379.conf-rw-r--r--. 1 root root 94 11月 2 09:14 redis6380.conf-rw-r--r--. 1 root root 94 11月 2 09:14 redis6381.conf-rw-r--r--. 1 root root 83376 11月 2 08:59 redis.conf[root@localhost myredis]# vi redis6381.conf [root@localhost myredis]# lsredis6379.conf redis6380.conf redis6381.conf redis.conf
启动所有
[root@localhost myredis]# redis-server redis6379.conf [root@localhost myredis]# redis-server redis6380.conf [root@localhost myredis]# redis-server redis6381.conf [root@localhost myredis]# ps -ef | grep redisroot 2409 1 0 11月01 ? 00:01:10 redis-server *:6379root 10738 1 0 09:27 ? 00:00:00 redis-server *:6380root 10744 1 0 09:27 ? 00:00:00 redis-server *:6381root 10758 10194 0 09:27 pts/1 00:00:00 grep --color=auto redis
分别连接
配置从机命令 slaveof <ip> <port>
主
[root@localhost myredis]# redis-cli -p 6379127.0.0.1:6379>
从
[root@localhost ~]# cd /myredis[root@localhost myredis]# redis-cli -p 6380127.0.0.1:6380> ----------------配置从机127.0.0.1:6380> slaveof 127.0.0.1 6379OK
从
[root@localhost ~]# cd /myredis[root@localhost myredis]# redis-cli -p 6381127.0.0.1:6381> ----------------配置从机127.0.0.1:6380> slaveof 127.0.0.1 6379OK
查看运行状况
主机
127.0.0.1:6379> info replication# Replicationrole:masterconnected_slaves:0master_replid:28a0df4ffd76d7eca565b8cfbbf59d88c66c4384master_replid2:0000000000000000000000000000000000000000master_repl_offset:0second_repl_offset:-1repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0
从机
127.0.0.1:6380> info replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:6master_sync_in_progress:0slave_repl_offset:28slave_priority:100slave_read_only:1connected_slaves:0master_replid:796e7d3a4cd24ffe401f1fa41a735f60ce4aca26master_replid2:0000000000000000000000000000000000000000master_repl_offset:28second_repl_offset:-1repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:28
测试
主机中
127.0.0.1:6379> set k1 v1OK
从机中
127.0.0.1:6380> get k1"v1"
如果在从机中进行写操作 会报错
127.0.0.1:6380> set k2 v2(error) READONLY You can't write against a read only replica.
情况分析
当一台从机挂掉后 ,再重启,该服务器不会直接变成从机,需要 手动配置 加入主从配置 中去。在加入的过程中,会将主机的数据从头配置到从机中去
当主服务器挂掉之后,从服务器依旧是从,当主服务器重新连接时,仍然是主服务器
复制原理
- 当从连接上主服务器之后,从服务器向主服务发送进行数据同步消息
- 主服务器接到从服务器发送过来同步消息,把主服务 器数据进行持久化rdb文件,把rdb文件发送从服务器,从服务器拿到rdb进行读取
- 每次主服务器进行写操作之后,和从服务器进行数据同步
哨兵模式
反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

配置哨兵
sentinel monitor mymaster 127.0.0.1 6379 1
其中mymaster为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量
启动哨兵
执行redis-sentinel /myredis/sentinel.conf
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZybBpPvU-1636112884751)(E:\真.学习资料\易点\Redis!在这里插入图片描述
)]
当主机挂掉,在从机选举产生新的主机,并且原主机重启后会变为从机
故障恢复

- 优先级在redis.conf中默认:slave-priority 100,值越小优先级越高
- 偏移量是指获得原主机数据最全的
- 每个redis实例启动后都会随机生成一个40位的runid
Redis集群
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求
创建6个redis服务器
[root@localhost myredis]# ll总用量 108-rw-r--r--. 1 root root 177 11月 2 13:20 redis6379.conf-rw-r--r--. 1 root root 177 11月 2 13:29 redis6380.conf-rw-r--r--. 1 root root 177 11月 2 13:29 redis6381.conf-rw-r--r--. 1 root root 177 11月 2 13:30 redis6389.conf-rw-r--r--. 1 root root 177 11月 2 13:31 redis6390.conf-rw-r--r--. 1 root root 177 11月 2 13:32 redis6391.conf-rw-r--r--. 1 root root 83376 11月 2 08:59 redis.conf
配置各个redis.conf文件
将各个redis.conf文件中的内容进行更改对应的符号
vi redisxxxx.conf
include /myredis/redis.confpidfile /var/run/redis_6379.pidport 6379dbfilename dump6379.rdbcluster-enabled yescluster-config-file nodes-6379.confcluster-node-timeout 15000
启动6个redis服务器
[root@localhost myredis]# redis-server redis6379.conf[root@localhost myredis]# redis-server redis6380.conf[root@localhost myredis]# redis-server redis6381.conf[root@localhost myredis]# redis-server redis6389.conf[root@localhost myredis]# redis-server redis6390.conf[root@localhost myredis]# redis-server redis6391.conf[root@localhost myredis]# ps -ef | grep redisroot 12891 1 0 13:38 ? 00:00:00 redis-server *:6389 [cluster]root 12897 1 0 13:38 ? 00:00:00 redis-server *:6390 [cluster]root 12903 1 0 13:38 ? 00:00:00 redis-server *:6391 [cluster]root 12992 1 0 13:45 ? 00:00:00 redis-server *:6379 [cluster]root 12998 1 0 13:45 ? 00:00:00 redis-server *:6380 [cluster]root 13004 1 0 13:45 ? 00:00:00 redis-server *:6381 [cluster]root 13010 12592 0 13:45 pts/4 00:00:00 grep --color=auto redis
合成一个集群
[root@localhost myredis]# redis-cli --cluster create --cluster-replicas 1 192.168.10.100:6379 192.168.10.100:6380 192.168.10.100:6381 192.168.10.100:6389 192.168.10.100:6390 192.168.10.100:6391>>> Performing hash slots allocation on 6 nodes...Master[0] -> Slots 0 - 5460Master[1] -> Slots 5461 - 10922Master[2] -> Slots 10923 - 16383Adding replica 192.168.10.100:6390 to 192.168.10.100:6379Adding replica 192.168.10.100:6391 to 192.168.10.100:6380Adding replica 192.168.10.100:6389 to 192.168.10.100:6381>>> Trying to optimize slaves allocation for anti-affinity[WARNING] Some slaves are in the same host as their masterM: 286b05469c7564304acfb54d72bcf058fc1d39c0 192.168.10.100:6379 slots:[0-5460] (5461 slots) masterM: 2f54173ca63c53e337cee8fc099caac3e6b59d34 192.168.10.100:6380 slots:[5461-10922],[12706] (5462 slots) masterM: a6c3fc2002ff5ce0ee4a1a8800e6f501287f6f4a 192.168.10.100:6381 slots:[10923-16383] (5461 slots) masterS: 4b9b75a3d45153af9ecb3a0a01a7882a674f327e 192.168.10.100:6389 replicates 286b05469c7564304acfb54d72bcf058fc1d39c0S: bc0704aefd2fa8c42f50a483f981002b1093d617 192.168.10.100:6390 replicates 2f54173ca63c53e337cee8fc099caac3e6b59d34S: 16146f7e0a0e20479f2366e842a6f80dbb7870d5 192.168.10.100:6391 replicates a6c3fc2002ff5ce0ee4a1a8800e6f501287f6f4aCan I set the above configuration? (type 'yes' to accept): yes>>> Nodes configuration updated>>> Assign a different config epoch to each node>>> Sending CLUSTER MEET messages to join the clusterWaiting for the cluster to join>>> Performing Cluster Check (using node 192.168.10.100:6379)M: 286b05469c7564304acfb54d72bcf058fc1d39c0 192.168.10.100:6379 slots:[0-5460] (5461 slots) master 1 additional replica(s)S: 4b9b75a3d45153af9ecb3a0a01a7882a674f327e 192.168.10.100:6389 slots: (0 slots) slave replicates 286b05469c7564304acfb54d72bcf058fc1d39c0S: 16146f7e0a0e20479f2366e842a6f80dbb7870d5 192.168.10.100:6391 slots: (0 slots) slave replicates a6c3fc2002ff5ce0ee4a1a8800e6f501287f6f4aM: a6c3fc2002ff5ce0ee4a1a8800e6f501287f6f4a 192.168.10.100:6381 slots:[10923-16383] (5461 slots) master 1 additional replica(s)M: 2f54173ca63c53e337cee8fc099caac3e6b59d34 192.168.10.100:6380 slots:[5461-10922] (5462 slots) master 1 additional replica(s)S: bc0704aefd2fa8c42f50a483f981002b1093d617 192.168.10.100:6390 slots: (0 slots) slave replicates 2f54173ca63c53e337cee8fc099caac3e6b59d34[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered.
登陆集群中的一个
可能直接进入读主机,存储数据时,会出现MOVED重定向操作。所以,应该以集群方式登录
[root@localhost myredis]# redis-cli -c -p 6379
查看集群信息
通过 cluster nodes 命令

集群插槽
一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,
集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽。
在集群中录入值
在redis-cli每次录入、查询键值,redis都会计算出该key应该送往的插槽,如果不是该客户端对应服务器的插槽,redis会报错,并告知应前往的redis实例地址和端口。
redis-cli客户端提供了 –c 参数实现自动重定向。如 redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向
127.0.0.1:6379> set k1 v1-> Redirected to slot [12706] located at 192.168.10.100:6381OK192.168.10.100:6381> set k2 v2-> Redirected to slot [449] located at 192.168.10.100:6379OK
不同的key,不同的slot,导致不同服务器
在集群中录入多个值
不在一个slot下的键值,是不能使用mget,mset等多键操作
192.168.10.100:6379> mset a1 1 a2 2 a3 3(error) CROSSSLOT Keys in request don't hash to the same slot
可以通过{}来定义组的概念,从而使key中{}内相同内容的键值对放到一个slot中去
192.168.10.100:6379> mset name{student} zjl age{student} 20OK
NoSQL
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。
NoSQL 不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力。
特点
- 不遵循SQL标准。
- 不支持ACID。
- 远超于SQL的性能。
适用场景
- l 对数据高并发的读写
- l 海量数据的读写
- l 对数据高可扩展性的
引入
技术的分类
1、解决功能性的问题:Java、Jsp、RDBMS、Tomcat、HTML、Linux、JDBC、SVN
2、解决扩展性的问题:Struts、Spring、SpringMVC、Hibernate、Mybatis
3、解决性能的问题:NoSQL、Java线程、Hadoop、Nginx、MQ、ElasticSearch
解决CPU及内存压力

解决IO压力

memcached
memcached是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但被许多网站使用。这是一套开放源代码软件,以BSD license授权发布。
2021.11.02
Liunx
clear 清屏
vi
查找单词
首先要进入命令模式(按ESC退出INSERT模式)然后输入:/单词
将文档中的原字符全部被代替为新的字符
%s/原字符/像改为的字符
查看当前进程
ps -ef | grep redis
173万+

被折叠的 条评论
为什么被折叠?



