1. Redis概述 基于内存存储的,NoSQL数据库(非关系型数据库),存储结构:key-value,Redis是一个开放源代码(BSD许可)内存中的数据结构存储用作数据库丶缓存和消息代理。对于数据量多,数据交互效率要求高的场景,可以考虑使用Redis 。
Redis:开源、免费、高性能、K-V数据库、内存数据库、非关系型数据库,支持持久化、集群和事务
1.1 什么是Redis Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
字符串类型 string
哈希类型 hash
列表类型 list
集合类型 set
有序集合类型 sortedset
1.2 redis的应用场景
缓存(数据查询、短连接、新闻内容、商品内容等等)
聊天室的在线好友列表
任务队列。(秒杀、抢购、12306等等)
应用排行榜
网站访问统计
数据过期处理(可以精确到毫秒
分布式集群架构中的session分离
2. Redis安装 2.1 在线安装 环境:Ubunto 16.04
1 2 3 4 输入命令 apt-get install redis-server 输入y 确认安装并使用空间 安装完成后,使用service redis status 可以查看redis服务的状态为active(running),说明安装完成系统自动启动了服务 使用ps -aux|grep redis命令可以看到服务器系统进程默认端口6379
2.2 安装包安装 环境:已安装GCC,Ubunto 16.04,redis-4.0.11.tar.gz,使用Xshell工具上传Redis的tar包文件
2.2.1 在Ubuntu上安装GCC GNU编译器集合(GCC)是C,C ++,Objective-C,Fortran,Ada,Go和D编程语言的编译器和库的集合。许多开源项目包括GNU工具和Linux内核都是用GCC编译的。默认的Ubuntu存储库包含一个名为build-essential的元包,它包含GCC编译器以及编译软件所需的许多库和其他实用程序。
1 2 3 首先更新包列表:apt update 安装build-essential软件包: apt install build-essential,该命令将安装一堆新包,包括gcc,g ++和make 使用gcc --version命令打印GCC版本:gcc --version
2.2.2 tar安装 1 2 3 4 5 解压:tar -zxvf redis-4.0.11.tar.gz 复制:mv redis-4.0.11 /usr/local/redis 进入redis目录:cd /usr/local/redis/ 编译,并使用标准的libc中的内存管理函数,必须进入Redis的安装目录才能执行该命令:make MALLOC=libc 安装,并选择安装目录:make PREFIX=/usr/local/redis install
查看安装目录下的文件,cd /usr/local/redis/bin/ 开启Redis的服务端,./redis-server 开启Redis的客户端./redis-cli
##docker安装
####拉取镜像
####启动镜像
1 docker run -d -p 6379:6379 --name myredis
3. 命令操作 3.1 redis的数据结构: redis存储的是:key,value格式的数据,其中key都是字符串,value有5种不同的数据结构
value的数据结构:
字符串类型 string
哈希类型 hash : map格式
列表类型 list : linkedlist格式。支持重复元素
集合类型 set : 不允许重复元素
有序集合类型 sortedset : 不允许重复元素,且元素有顺序
3.1.1 字符串类型 String 存储 : set key value
1 2 127.0.0.1:6379> set username zhangsan OK
获取 : get key
1 2 127.0.0.1:6379> get username "zhangsan"
删除 : del key
1 2 127.0.0.1:6379> del age (integer) 1
3.1.2 哈希类型 hash 存储 : hset key field value
1 2 3 4 127.0.0.1:6379> hset myhash username lisi (integer) 1 127.0.0.1:6379> hset myhash password 123 (integer) 1
获取 : hget key field : 获取指定的field对应的值
1 2 127.0.0.1:6379> hget myhash username "lisi"
hgetall key: 获取所有的field和value
1 2 3 4 5 127.0.0.1:6379> hgetall myhash 1) "username" 2) "lisi" 3) "password" 4) "123"
删除 : hdel key field
1 2 127.0.0.1:6379> hdel myhash username (integer) 1
3.1.3 列表类型 list 可以添加一个元素到列表的头部(左边)或者尾部(右边)
添加:
lpush key value: 将元素加入列表左表
rpush key value:将元素加入列表右边
1 2 3 4 5 6 127.0.0.1:6379> lpush myList a (integer) 1 127.0.0.1:6379> lpush myList b (integer) 2 127.0.0.1:6379> rpush myList c (integer) 3
获取:
lrange key start end : 范围获取
1 2 3 4 127.0.0.1:6379> lrange myList 0 -1 1) "b" 2) "a" 3) "c"
删除:
lpop key: 删除列表最左边的元素,并将元素返回
rpop key: 删除列表最右边的元素,并将元素返回
3.1.4 集合类型 set 不允许重复元素
存储 : sadd key value
1 2 3 4 127.0.0.1:6379> sadd myset a (integer) 1 127.0.0.1:6379> sadd myset a (integer) 0
获取 : smembers key : 获取set集合中所有元素
1 2 127.0.0.1:6379> smembers myset 1) "a"
删除 : srem key value : 删除set集合中的某个元素
1 2 127.0.0.1:6379> srem myset a (integer) 1
3.1.5有序集合类型 sortedset 不允许重复元素,且元素有顺序.每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
存储 : zadd key score value
1 2 3 4 5 6 127.0.0.1:6379> zadd mysort 60 zhangsan (integer) 1 127.0.0.1:6379> zadd mysort 50 lisi (integer) 1 127.0.0.1:6379> zadd mysort 80 wangwu (integer) 1
获取 : zrange key start end [withscores]
1 2 3 4 5 6 7 8 9 10 11 127.0.0.1:6379> zrange mysort 0 -1 1) "lisi" 2) "zhangsan" 3) "wangwu" 127.0.0.1:6379> zrange mysort 0 -1 withscores 1) "zhangsan" 2) "60" 3) "wangwu" 4) "80" 5) "lisi" 6) "500"
删除 : zrem key value
1 2 127.0.0.1:6379> zrem mysort lisi (integer) 1
3.1.6 通用命令
keys * : 查询所有的键
type key : 获取键对应的value的类型
del key:删除指定的key value
4. 持久化 redis是一个内存数据库,当redis服务器重启,获取电脑重启,数据会丢失,我们可以将redis内存中的数据持久化保存到硬盘的文件中。
##4.1 redis持久化机制: RDB : 默认方式,不需要进行配置,默认就使用这种机制,在一定的间隔时间中,检测key的变化情况,然后持久化数据
编辑redis.conf文件
1 2 3 4 5 6 # after 900 sec (15 min) if at least 1 key changed save 900 1 # after 300 sec (5 min) if at least 10 keys changed save 300 10 # after 60 sec if at least 10000 keys changed save 60 10000
重新启动redis服务器,并指定配置文件名称
1 ./redis-server redis.conf
AOF: 日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据
编辑redis.conf文件
1 2 3 4 appendonly no(关闭aof) --> appendonly yes (开启aof) # appendfsync always : 每一次操作都进行持久化 appendfsync everysec : 每隔一秒进行一次持久化 # appendfsync no : 不进行持久化
5. Java客户端 Jedis Jedis: 一款java操作redis数据库的工具. 使用步骤:
下载jedis的jar包
使用
1 2 3 4 5 6 //1. 获取连接 Jedis jedis = new Jedis("localhost",6379); //2. 操作 jedis.set("username","zhangsan"); //3. 关闭连接 jedis.close();
注意哦 :
如果需要连接其他服务器的redis需要修改配置文件redis.conf,注释# bind 127.0.0.1,并一配置文件启动,否则后台连接超时。
如果需要添加认证,在配置文件中添加requirepass “zysheep”
1 2 3 4 5 6 7 String host="192.168.40.137"; int prot=6379; Jedis jedis = new Jedis(host,prot); jedis.auth("zysheep"); jedis.set("strName","李四"); System.out.println("strName的key:"+jedis.get("strName")); System.out.println(jedis.ping()); //连接测试
5.1 Jedis操作各种redis中的数据结构 5.1.1 字符串类型 string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 //存储 jedis.set("username","zhangsan"); //获取 String username = jedis.get("username"); System.out.println(username); //可以使用setex()方法存储可以指定过期时间的 key value jedis.setex("activecode",20,"hehe");//将activecode:hehe键值对存入redis,并且20秒后自动删除该键值对 //3. 关闭连接 jedis.close();
5.1.2 哈希类型 hash : map格式
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 //1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // 存储hash jedis.hset("user","name","lisi"); jedis.hset("user","age","23"); jedis.hset("user","gender","female"); // 获取hash String name = jedis.hget("user", "name"); System.out.println(name); // 获取hash的所有map中的数据 Map<String, String> user = jedis.hgetAll("user"); // keyset Set<String> keySet = user.keySet(); for (String key : keySet) { //获取value String value = user.get(key); System.out.println(key + ":" + value); } //3. 关闭连接 jedis.close();
5.1.3 列表类型 list : linkedlist格式。支持重复元素
lpush / rpush
lpop / rpop
lrange start end : 范围获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // list 存储 jedis.lpush("mylist","a","b","c");//从左边存 jedis.rpush("mylist","a","b","c");//从右边存 // list 范围获取 List<String> mylist = jedis.lrange("mylist", 0, -1); System.out.println(mylist); // list 弹出 String element1 = jedis.lpop("mylist");//c System.out.println(element1); String element2 = jedis.rpop("mylist");//c System.out.println(element2); // list 范围获取 List<String> mylist2 = jedis.lrange("mylist", 0, -1); System.out.println(mylist2); //3. 关闭连接 jedis.close();
5.1.4 集合类型 set : 不允许重复元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 //1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // set 存储 jedis.sadd("myset","java","php","c++"); // set 获取 Set<String> myset = jedis.smembers("myset"); System.out.println(myset); //3. 关闭连接 jedis.close();
5.1.5 有序集合类型 sortedset:不允许重复元素,且元素有顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //1. 获取连接 Jedis jedis = new Jedis();//如果使用空参构造,默认值 "localhost",6379端口 //2. 操作 // sortedset 存储 jedis.zadd("mysortedset",3,"亚瑟"); jedis.zadd("mysortedset",30,"后裔"); jedis.zadd("mysortedset",55,"孙悟空"); // sortedset 获取 Set<String> mysortedset = jedis.zrange("mysortedset", 0, -1); System.out.println(mysortedset); //3. 关闭连接 jedis.close();
5.2 jedis连接池: JedisPool 使用:
1.创建JedisPool连接池对象
2.调用方法 getResource()方法获取Jedis连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //0.创建一个配置对象 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(50); config.setMaxIdle(10); //1.创建Jedis连接池对象 JedisPool jedisPool = new JedisPool(config,"localhost",6379); //2.获取连接 Jedis jedis = jedisPool.getResource(); //3. 使用 jedis.set("hehe","heihei"); //4. 关闭 归还到连接池中 jedis.close();
5.2.1 连接池工具类 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 jedis.properties host=127.0.0.1 # redis服务器ip地址 port=6379 #端口 maxTotal=50 # 最大连接数 maxIdle=10 # 空闲连接数 public class JedisPoolUtils { private static JedisPool jedisPool; static{ //读取配置文件 InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties"); //创建Properties对象 Properties pro = new Properties(); //关联文件 try { pro.load(is); } catch (IOException e) { e.printStackTrace(); } //获取数据,设置到JedisPoolConfig中 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal"))); config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle"))); //初始化JedisPool jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port"))); } /** * 获取连接方法 */ public static Jedis getJedis(){ return jedisPool.getResource(); } }
SpringBoot整合redis Pom 引入redis的starter依赖
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 <!--redis start--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--redis end--> <!--cache start--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!--cache end--> <!--mysql-connector start--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mysql-connector end--> <!--mybatis start--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!--mybatis end--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
yml 配置redis连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 spring: datasource: url: jdbc:mysql://172.16.0.192:3306/springboot_cache username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: 172.16.0.192 # 主机地址 cache: redis: time-to-live: -1 #毫秒 #开启驼峰命名 mybatis: configuration: map-underscore-to-camel-case: true logging: level: cn: panyucbale: springboot: mapper: debug
redisTemplate 测试redisTemplate常用api
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 @SpringBootTest class Springboot08RedisApplicationTests { @Autowired StringRedisTemplate stringRedisTemplate; //操作k-v都是字符串的 @Autowired private RedisTemplate redisTemplate; //k-v都是对象的 /** * Redis常见的五大数据类型 * String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合) * stringRedisTemplate.opsForValue()[String(字符串)] * stringRedisTemplate.opsForList()[List(列表)] * stringRedisTemplate.opsForSet()[Set(集合)] * stringRedisTemplate.opsForHash()[Hash(散列)] * stringRedisTemplate.opsForZSet()[ZSet(有序集合)] */ @Test public void test01() { //给redis中保存数据 //stringRedisTemplate.opsForValue().set("string_msg","hello"); String msg = stringRedisTemplate.opsForValue().get("string_msg"); System.out.println(msg); // stringRedisTemplate.opsForList().leftPush("mylist","1"); // stringRedisTemplate.opsForList().leftPush("mylist","2"); } }
测试缓存 ###原理
CacheManager===ache 缓存组件来实际给缓存中存取数据
引入redis的starter,容器中保存的是RedisCacheManager;
RedisCacheManager帮我们创建RedisCache来作为缓存组件;RedisCache通过操作redis缓存数据的
默认保存数据k-v 都是Object;利用序列化保存;如何保存为json
引入了redis的starter,cacheManager变为 RedisCacheManager;
默认创建的 RedisCacheManager 操作redis的时候使用的是 RedisTemplate<Object, Object>
RedisTemplate<Object, Object>是 默认使用jdk的序列化机制
自定义CacheManager;
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 45 46 47 48 49 50 51 52 53 54 55 56 @Configuration public class MyRedisConfig { @Value("${spring.cache.redis.time-to-live}") private Duration timeToLive = Duration.ZERO; @Bean(name = "redisTemplate") public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(mapper); template.setValueSerializer(jackson2JsonRedisSerializer); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); //使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(stringRedisSerializer); template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); 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); // 配置序列化(解决乱码的问题) RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(timeToLive) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); return cacheManager; } }
##环境准备
###Department
实体类Department
1 2 3 4 5 6 7 @AllArgsConstructor @NoArgsConstructor @Data public class Department implements Serializable { private Integer id; private String departmentName; }
###Mapper
1 2 3 4 5 6 @Mapper public interface DepartmentMapper { @Select("SELECT * FROM department WHERE id = #{id}") Department getDeptById(Integer id); }
###DeptService
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 @Service public class DeptService { @Autowired DepartmentMapper departmentMapper; @Autowired CacheManager cacheManager; /** * 缓存的数据能存入redis; * 第二次从缓存中查询就不能反序列化回来; * 存的是dept的json数据;CacheManager默认使用RedisTemplate<Object, Employee>操作Redis * * @param id * @return */ @Cacheable(cacheNames = "dept") public Department getDeptById(Integer id) { System.out.println("查询部门" + id); Department department = departmentMapper.getDeptById(id); return department; } // 使用缓存管理器得到缓存,进行api调用 public Department getDeptByIdManager(Integer id) { System.out.println("查询部门" + id); Department department = departmentMapper.getDeptById(id); //获取某个缓存 Cache cache = cacheManager.getCache("dept"); cache.put("dept::" + id, department); return department; } }
###DeptController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @RestController public class DeptController { @Autowired DeptService deptService; @GetMapping("/dept/{id}") public Department getDept(@PathVariable("id") Integer id) { return deptService.getDeptById(id); } @GetMapping("/depts/{id}") public Department getDepts(@PathVariable("id") Integer id) { return deptService.getDeptByIdManager(id); } }
记得在启动类中开启注解缓存,否则不会生效
问题
缓存的数据能存入redis;第二次从缓存中查询就不能反序列化回来, 存的是dept的json数据;CacheManager默认使用RedisTemplate<Object, Employee>操作Redis