Redis学习

  1. 1. 一、概述
  2. 2. 二、安装
  3. 3. 三、启动
  4. 4. 四、访问
  5. 5. 五、Redis_Jedis 测试
  6. 6. 六、分布式事务锁
  7. 7. 七、redis事务的三大特性
  8. 8. 八、持久化
  9. 9. 九、分布式
  10. 10. 十、应用问题

一、概述

  • 存储方式:key-value
  • 支持的数据类型:string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。
  • 新数据类型:
    • Bitmaps–使用二进制单位,实现对位的操作,通过数据偏移量来计算
    • HyPerLogLog–适用于需要去重和计数的基数问题
    • Geospatial–适用于元素的二维坐标(经纬度)操作
  • Redis支持各种不同方式的排序。
  • 操作支持原子性
  • 数据缓存在内存中
  • 支持主从同步
  • 端口号默认:6379
  • 默认16个数据库,类似数组下标从0开始,初始默认使用0号库
  • Redis是单线程+多路IO复用技术

二、安装

自行到官网:http://redis.io/下载当前系统支持版本:Windows/Linux

三、启动

前台(窗口)启动:

  • 在 /bin下,redis-server (缺点:当关闭当前服务窗口后redis服务将关闭,优点:可以实时查看redis服务状态日志信息)

后台启动:

  • 拷贝配置文件到指定目录:cp /opt/redis-3.2.5/redis.conf /myredis
  • 修改配置文件 开启后台启动 vi redis.conf –>daemonize no 改为daemonize yes
  • 使用修改后的配置文件启动 redis-server myredis/redis.conf
  • 查看redis线程是否存在:ps -ef |grep redis

四、访问

#redis-cli 即可进入客户端

#ping 若出现pong 则通

#shutdown 关闭服务

#flushdb 清空当前库

#flushall 通杀全部库

#dbsize查看当前数据库的key的数量

#select dbid来切换数据库。如: select 8

五、Redis_Jedis 测试

引入依赖

1
2
3
4
5
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>

连接Redis注意事项

禁用Linux的防火墙:Linux(CentOS7)里执行命令

systemctl stop/disable firewalld.service

redis.conf中注释掉bind 127.0.0.1 ,然后 protected-mode no

测试程序:demo.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

import redis.clients.jedis.Jedis;

public class Demo01 {

public static void main(String[] args) {

Jedis jedis = new Jedis("192.168.137.3",6379);

String pong = jedis.ping();

System.out.println("连接成功:"+pong);

jedis.close();

}

}

可以完成手机验证码功能

1、输入手机号,点击发送后随机生成6位数字码,2分钟有效

2、输入验证码,点击验证,返回成功或失败

3、每个手机号每天只能输入3次(设置数据过期时间)

整合Springboot

引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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

添加配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@EnableCaching
@Configuration
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;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/redisTest")
public class RedisTestController {
@Autowired
private RedisTemplate redisTemplate;

@GetMapping
public String testRedis() {
//设置值到redis
redisTemplate.opsForValue().set("name","lucy");
//从redis获取值
String name = (String)redisTemplate.opsForValue().get("name");
return name;
}
}

六、分布式事务锁

  • 乐观锁

    • 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
  • 悲观锁

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

七、redis事务的三大特性

①单独的隔离操作

  • 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

②没有隔离级别的概念

  • 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行

③不保证原子性

  • 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

八、持久化

redis提供两种持久化方式:RDB、AOF

如何执行备份:

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式(实时<可以控制时间间隔>进行持久化操作)更加的高效。RDB****的缺点是最后一次持久化后的数据可能丢失

小结:官方推荐两个都启用。如果对数据不敏感,可以选单独用RDB。不建议单独用 AOF,因为可能会出现Bug。如果只是做纯内存缓存,可以都不用。

九、分布式

主从复制

哨兵模式:能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

redis集群:实现扩容、分摊压力、无中心配置相对简单;缺点:不支持多键操作、不支持Lua脚本

十、应用问题

  • 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。

    • 解决:对空值进行缓存、设置可访问的白名单、采用布隆过滤器、实时监控
  • 缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

    • 解决:预先设置热门数据、实时调整、使用锁
  • 缓存雪崩:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

    • 解决:创建多级缓存架构、使用锁或队列、设置过期标志更新缓存、将缓存失效时间分散开