Redis教程

Redis

本文主要是介绍Redis,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Redis

启动:

redis-cli(不使用镜像的时候)
docker run -d --name w-redis redis redis-server
docker exec -it w-redis redis-cli

退出:

shutdown   //退出redis模式
docker stop 容器的ID    //关闭容器

Redis常用建

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        命令切换数据库(一共16个,0~15,默认的是0)
dbsize        查看当前数据库的key的数量
flushdb       清空当前库
flushall      通杀全部库

一、Redis中五大常用数据类型

Redis中String常用指令

内部实现是ArrayList

set   <key><value>		添加键值对
get   <key>				查询对应键值
append  <key><value>	将给定的<value> 追加到原值的末尾
strlen  <key>			获得值的长度
setnx  <key><value>		只有在 key 不存在时    设置 key 的值
incr  <key>
		将 key 中储存的数字值增1
		只能对数字值操作,如果为空,新增值为1
decr  <key>
		将 key 中储存的数字值减1
		只能对数字值操作,如果为空,新增值为-1
incrby / decrby  <key><步长>		将 key 中储存的数字值增减。自定义步长。
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开始)。
setex  <key><过期时间><value>
		设置键值的同时,设置过期时间,单位秒。
getset <key><value>
		以新换旧,设置了新值同时获得旧值。
Redis中的List

内部实现:List的数据结构为快速链表quickList。

首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。

它将所有的元素紧挨着一起存储,分配的是一块连续的内存。

当数据量比较多的时候才会改成quicklist。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwqOUGOP-1620812325959)(C:\Users\Acer\Pictures\Saved Pictures\redis1.png)]

lpush/rpush  <key><value1><value2><value3> ....	 从左边/右边插入一个或多个值。
lpop/rpop  <key>	从左边/右边吐出一个值。值在键在,值光键亡。
rpoplpush  <key1><key2>从<key1>	列表右边吐出一个值,插到<key2>列表左边。
lrange <key><start><stop>	按照索引下标获得元素(从左到右)
lrange mylist 0 -1   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
Redis中的Set集合

Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的****复杂度都是O(1)****。

sadd <key><value1><value2> ..... 
    将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
smembers <key>		取出该集合的所有值。
sismember <key><value>		判断集合<key>是否为含有该<value>值,有1,没有0
scard<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中的)
Redis中的Hash

内部实现:Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。

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集合的所有field
hvals <key>	列出该hash集合的所有value
hincrby <key><field><increment>	为哈希表 key 中的域 field 的值加上增量 1   -1
hsetnx <key><field><value>	将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在 .
Redis中的Zset(有序集合)

内部实现:(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。

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

zadd  <key><score1><value1><score2><value2>…
    将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zrange <key><start><stop>  [WITHSCORES]   
    返回有序集 key 中,下标在<start><stop>之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。
zrangebyscore key minmax [withscores] [limit offset count]
	返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。 
zrevrangebyscore key maxmin [withscores] [limit offset count]               
    同上,改为从大到小排列。 
zincrby <key><increment><value>      为元素的score加上增量
zrem  <key><value>	删除该集合下,指定值的元素 
zcount <key><min><max>	统计该集合,分数区间内的元素个数 
zrank <key><value>	返回该值在集合中的排名,从0开始。

二、Redis的发布和订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

Redis 客户端可以订阅任意数量的频道。

1、打开一个客户端订阅channel1
	SUBSCRIBE channel1
2、打开另一个客户端,给channel1发布消息hello
   	publish channel1 hello

三、Redis中的新数据类型

Bitmaps

Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。

Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量.

setbit<key><offset><value>	设置Bitmaps中某个偏移量的值(0或1)
getbit<key><offset>			获取Bitmaps中某个偏移量的值
bitcount<key>[start end] 	统计字符串从start字节到end字节比特值为1的数量
bitop  and(or/not/xor) <destkey> [key…]
bitop是一个复合操作, 它可以做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中。

Bitmaps和Set对比,数据量大的话使用Bitmaps,比如1亿数据。

数据量小的话用Set比如一万数据。

HyperLogLog

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

pfadd <key>< element> [element ...]   添加指定元素到 HyperLogLog 中
pfcount<key> [key ...] 计算HLL的近似基数,可以计算多个HLL
pfmerge<destkey><sourcekey> [sourcekey ...]  将一个或多个HLL合并后的结果存储在另一个HLL中,比如每月活跃用户可以使用每天的活跃用户来合并计算可得
Geospatial

Redis 3.2 中增加了对GEO类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。

geoadd<key><longitude><latitude><member> [longitude latitude member...]   添加地理位置(经度,纬度,名称)
geopos  <key><member> [member...]  获得指定地区的坐标值
geodist<key><member1><member2>  [m|km|ft|mi ]  获取两个位置之间的直线距
georadius<key>< longitude><latitude>radius  m|km|ft|mi   以给定的经纬度为中心,找出某一半径内的元素

四、Redis和Spring boot整合

依赖
	<!-- 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>
yml
#Redis服务器地址
spring.redis.host=8.142.98.85
#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
配置类
@Configuration
@EnableCaching
public 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;
    }
}
启动方式
在引用的类中注入即可。
@Autowired
private RedisTemplate redisTemplate;

五、Redis事务

Multi、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。
组队的过程中可以通过discard来放弃组队
监控:WATCH key [key …]

1、监控某个Key的值或多个

2、执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断

取消:UNWATCH
1、取消 WATCH 命令对所有 key 的监视。
2、如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
事务的三大特征
1、单独的隔离操作 
	事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 
2、没有隔离级别的概念 
	队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
3、不保证原子性 
	事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚 
事务秒杀活动一个案列
1、在虚拟机里面可以使用工具ab模拟高访问量和高并发的测试。
2、安装    yum install httpd-tools
3、遇见的三个问题和解决方案
	1)连接Redis超时的问题:使用连接池
	2)超卖的问题:使用乐观锁,把库存监视一下,然后把库存减一和添加用户按顺序加到事务里面,在执行。
	3)库存遗留问题(乐观锁造成的):LUA脚本

六、Redis的持久化

RDB
1、将需要持久化的数据先写到一个临时文件,然后在放到持久化文件。
2、RDB的缺点是最后一次持久化后的数据可能丢失。
AOF
1、以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件。
如果两个都开启,默认使用AOF
官方推荐两个都启用。
如果对数据不敏感,可以选单独用RDB。
不建议单独用 AOF,因为可能会出现Bug。
如果只是做纯内存缓存,可以都不用

七、主从复制

一主多仆
1、从机选择主机的时候用到:slaveof  <ip><port>
	例如:slaveof 127.0.0.1 6379
2、从机挂掉之后,需要重新连接到主机上面,执行第一步。
3、当主机活了之后,还是主机。
薪火相传
1、一个从机还可以有另外的从机。
反客为主
1、主机挂掉之后,其他从机有当主机的机会。
	可以使用  slaveof  no one  将从机变成主机。
	不过还需要手动,此方式不可以。下面使用哨兵
2、哨兵选举主机
	启动哨兵监控主机,当主机死掉之后,会在从机里面选举出来一个新的主机。
	不同与一主多仆,之前死掉的主机活了之后会变成从机。
原理
	master=》主机,Slave=》从机。
1、Slave启动成功连接到master后会发送一个sync命令
2、Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
3、全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
4、增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
5、但是只要是重新连接master,一次完全同步(全量复制)将被自动执行

八、集群

常用命令
1、cluster nodes 命令查看集群信息。
2、slots(插槽), Redis 集群包含 16384 个插槽。
3、集群状态下插入多个值,需要用到组的概念。例如:mset name{user} wxs age{user} 20,把它们放到USER组里 
故障恢复
1、如果主节点下线?从节点能否自动升为主节点?注意:15秒超时
2、主节点恢复后,主从关系会如何?主节点回来变成从机
3、如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?
	 如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么 ,整个集群都挂掉
		如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,该插槽数据全都不能使		   用,也无法存储。
4、redis.conf中的参数  cluster-require-full-coverage

九、Redis三大重要问题

客户访问大量空数据造成服务器压力属于缓存穿透,访问过期的热点key属于缓存击穿,大量key过期属于缓存雪崩。
缓存穿透
现象:
	1、应用服务器压力增加
	2、redis命中率降低
	3、一直查询数据库
原因:
	1、redis中查找不到Key的值,请求就会访问数据源,而数据源也没有对应的值,最终会压垮数据源。
	2、一般这种现象是黑客入侵(非法的url访问)。
解决:
	1、对空值缓存:如果访问这个值不存在时,仍然把该值(BULL结果)缓存,设置过期时间短一点,不超过五分钟。
	2、采用布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个			 bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
	3、实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名			单限制服务。(或者可以报网警)
缓存击穿
现象:
	1、数据库访问量忽然增加
	2、redis中没有大量的Key过期
	3、redis正常运行
原因:
	1、热点访问key过期。
解决:
	1、预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长
	2、适时调整:现场监控那些key数据热门,增加存活时间
	3、使用锁:效率太低
缓存雪崩
现象:
	1、数据库压力大,服务器崩溃
原因:
	1、大量的key值过期。
解决:
	1、将缓存失效时间分散开来:在原来失效时间上面加一个随机值,这样每一个缓存失效时间的重复率会降低,避免大量		key失效。
	2、设置过期标志更新缓存:如果一个key触发了过期,会通知另外一个进程在后台去更新key的缓存。
	3、构建多级缓存架构:使用多级缓存。(比较麻烦)
	4、使用队列或者锁:效率低。
单限制服务。(或者可以报网警)
###### 缓存击穿

```tex
现象:
	1、数据库访问量忽然增加
	2、redis中没有大量的Key过期
	3、redis正常运行
原因:
	1、热点访问key过期。
解决:
	1、预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长
	2、适时调整:现场监控那些key数据热门,增加存活时间
	3、使用锁:效率太低
缓存雪崩
现象:
	1、数据库压力大,服务器崩溃
原因:
	1、大量的key值过期。
解决:
	1、将缓存失效时间分散开来:在原来失效时间上面加一个随机值,这样每一个缓存失效时间的重复率会降低,避免大量		key失效。
	2、设置过期标志更新缓存:如果一个key触发了过期,会通知另外一个进程在后台去更新key的缓存。
	3、构建多级缓存架构:使用多级缓存。(比较麻烦)
	4、使用队列或者锁:效率低。
这篇关于Redis的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!