- 简介
- 背景
- Redis概述
- 版本及参考说明
- Redis初始 *** 作
- 启动Redis服务
- 进入redis容器
- 登录redis服务
- 查看Redis信息
- 清空redis屏幕
- 退出redis服务
- 关闭redis服务
- 系统帮助
- Redis数据存储 *** 作
- 简易数据存取
- Key有效时间设计
- Redis常用数据类型
- Java中 *** 作redis
- 购物车实现
- 分布式ID实现
- RedisPool连接池
- 基于redis的单点登录设计及实现
- Redis实现投票系统(简易)
- 重点: 定制序列化与反序列化方式
- 指定序列化方式前
- 指定序列化方式后
- 自定义序列化方式1
- 自定义序列化方式2
- 通过AOP实现自定义序列化
- Redis事务处理
- 基本指令
- Redis架构设计应用实践
我们现在的项目架构中,基本上是Web服务器(Tomcat)和数据库独立部署,独占服务器资源,随着用户数的增长,并发读写数据库,会加大数据库访问压力,导致性能的下降,严重时直接导致系统宕机,例如:
此时,我们可以在Tomcat同服务器上中增加本地缓存,并在外部增加分布式缓存,缓存热门数据。也就是通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。例如:
基于这样的一种架构设计,于是类似redis的一些分布式数据库就诞生了。
Redis是一个key-value存储系统(官网:http://redis.io),是一个分布式缓存数据库。在DB-Engines.com的数据库排行中, Redis上升排行第七,如图所示:
Redis的次版本号(第一个小数点后的数字)为偶数的版本是稳定版本(2.4、2.6等),奇数为非稳定版本(2.5、2.7),一般推荐在生产环境使用稳定版本。最新版本6.2.2,新增了stream的处理方式,性能更高。Redis官方是不支持windows平台的,windows版本是由微软自己建立的分支,基于官方的Redis源码上进行编译、发布、维护的,所以windows平台的Redis版本要略低于官方版本。
Redis 相关参考网址如下所示:
Bootnb 相关:https://www.runoob.com/redis/redis-tutorial.html Redis 官网:https://redis.io/ 源码地址:https://github.com/redis/redis Redis 在线测试:http://try.redis.io/ Redis 命令参考:http://doc.redisfans.com/Redis初始 *** 作 启动Redis服务
Docker环境下的启动(docker环境启动需要运行多个容器):
docker start redis01 #底层也是通过redis-server启动,start单词后的redis01为容器名
docker中查看redis服务
docker ps
查看启动的redis进程信息
ps -ef|grep redis root 3511 1 0 16:29 ? 00:00:01 redis-server *:6379 root 3515 1 0 16:29 ? 00:00:01 redis-server 127.0.0.1:6380进入redis容器
docker exec -it redis01 bash #redis01为容器名登录redis服务
登录本地redis
redis-cli 或者 redis-cli -p 6379 或者 redis-cli -p 6379 -a password #-a后面为password,此 *** 作需要开启redis.conf文件中的 requirepass选项
登录远程redis
redis-cli -h ip -p 6379 -a passworld查看Redis信息
首先登录redis,然后输入info指令
127.0.0.1:6379> info #查看当前redis节点的详细配置信息清空redis屏幕
清除redis屏幕内容
127.0.0.1:6379> clear退出redis服务
127.0.0.1:6379> exit关闭redis服务
127.0.0.1:6379> shutdown
系统帮助shotdown 受保护模式的关闭(关闭前会将数据持久化)
可以基于help指令查看相关指令帮助,例如:
127.0.0.1:6379> help redis-cli 2.8.19 Type: "help @" to get a list of commands in "help " for help on "help " to get a list of possible help topics "quit" to exit
127.0.0.1:6379> help type TYPE key summary: Determine the type stored at key since: 1.0.0 group: genericRedis数据存储 *** 作 简易数据存取
基于查看redis中的key
127.0.0.1:6379> keys * (empty list or set)
基于key/value形式存储数据
127.0.0.1:6379> set test1 123 OK 127.0.0.1:6379> set test2 ab OK 127.0.0.1:6379> keys * 1) "test1" 2) "test2"
基于key获取redis中存储的数据
127.0.0.1:6379> get test1 "123" 127.0.0.1:6379> get test2 "ab" 127.0.0.1:6379> get test3 (nil) 127.0.0.1:6379>
清除redis中的数据
清除当前数据库的数据
127.0.0.1:6379> flushdb OK
清除所有数据库的数据(redis一共有15个数据库)
127.0.0.1:6379> flushall OKKey有效时间设计
实际工作中我们经常要控制redis中key的有效时长,例如秒杀 *** 作的计时,缓存数据的有效时长等。
Expire (设置生效时长-单位秒)
语法:EXPIRE key seconds
127.0.0.1:6379> set bomb tnt OK 127.0.0.1:6379> expire bomb 10 (integer) 1 127.0.0.1:6379> ttl bomb (integer) 5 127.0.0.1:6379> ttl bomb (integer) 3 127.0.0.1:6379> ttl bomb (integer) 3 127.0.0.1:6379> ttl bomb (integer) 2 127.0.0.1:6379> ttl bomb (integer) 1 127.0.0.1:6379> ttl bomb (integer) -2 127.0.0.1:6379> ttl bomb (integer) -2 127.0.0.1:6379>
其中,TTL查看key的剩余时间,当返回值为-2时,表示键被删除。
当 key 不存在时,返回 -2 。 当 key 存在但没有设置剩余生存时间时,返回 -1 。
Persist (取消时长设置)
通过persist让对特定key设置的生效时长失效。
语法:PERSIST key
127.0.0.1:6379> set bomb tnt OK 127.0.0.1:6379> expire bomb 60 (integer) 1 127.0.0.1:6379> ttl bomb (integer) 49 127.0.0.1:6379> persist bomb (integer) 1 127.0.0.1:6379> ttl bomb (integer) -1 127.0.0.1:6379>
其中,设置新的数据时需要重新设置该key的生存时间,重新设置值也会清除生存时间。
pexpire (单位毫秒)
pexpire 让key的生效时长以毫秒作为计量单位,这样可以做到更精确的时间控制。例如,可应用于秒杀场景。
语法:PEXPIRE key milliseconds
127.0.0.1:6379> set bomb tnt OK 127.0.0.1:6379> pexpire bomb 10000 (integer) 1 127.0.0.1:6379> ttl bomb (integer) 6 127.0.0.1:6379> ttl bomb (integer) 3 127.0.0.1:6379> ttl bomb (integer) -2 127.0.0.1:6379>Redis常用数据类型
父工程中添加依赖
org.apache.maven.plugins maven-compiler-plugin3.8.1 8
子工程中添加依赖
redis.clients jedis3.5.2 junit junit4.12 test com.google.code.gson gson2.8.6
Redis常用数据类型
Java中 *** 作redisJava中 *** 作redis
购物车实现package com.jt.redis; import redis.clients.jedis.Jedis; import java.util.Map; public class CartDemo01 { private static final String IP = "192.168.81.128"; private static final int PORT = 6379; private static final String PREFIX = "cart:"; static void addCart(String userId,Long productId,int num){ //1.建立redis连接 Jedis jedis = new Jedis(IP, PORT); //2.添加商品 jedis.hincrBy(PREFIX+userId, String.valueOf(productId), num); //3.释放资源 jedis.close(); } static Map分布式ID实现listCart(String userId){ //1.建立redis连接 Jedis jedis = new Jedis(IP, PORT); //2.添加商品 Map stringStringMap = jedis.hgetAll(PREFIX+userId); //3.释放资源 jedis.close(); return stringStringMap; } static void updateCart(String userId,Long productId,int num){ //1.建立redis连接 Jedis jedis = new Jedis(IP, PORT); //2.添加商品 jedis.hincrBy(PREFIX+userId, String.valueOf(productId), num); //3.释放资源 jedis.close(); } static void deleteCart(String userId,String... productId){ //1.建立redis连接 Jedis jedis = new Jedis(IP, PORT); //2.删除商品 //批量删除 // List longs = Arrays.asList(productId); // jedis.hdel(PREFIX+userId, longs.toArray(new String[]{})); jedis.hdel(PREFIX+userId, productId); //3.释放资源 jedis.close(); } public static void main(String[] args) { //1.向购物车添加商品 updateCart("101", 2001L, 1); updateCart("101", 2002L, 3); updateCart("101", 2003L, 5); //2.查看购物车商品 Map stringStringMap = listCart("101"); System.out.println(stringStringMap); //3.修改购物车商品数量 updateCart("101", 2003L, -10); //4.删除购物车商品 deleteCart("101", "2001"); //5.清空购物车 deleteCart("101","2001","2002","2003"); Map stringStringMap1 = listCart( "101"); System.out.println(stringStringMap1); } }
package com.jt.redis; import redis.clients.jedis.Jedis; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class IdGeneratorDemo01 { public static Long getId(){ //Jedis jedis = new Jedis("192.168.81.128"); Jedis jedis = JedisDataSource.getConnection(); //假如redis设置了访问密码123456 //jedis.auth("123456"); Long id = jedis.incr("id"); jedis.close(); return id; } public static void main(String[] args) { //构建一个最多只有3个线程的线程池(启动一个线程需要1兆内存) ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i <= 10; i++) { //从线程池中获取线程 //这个任务会存储到阻塞式任务队列中 executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "->" + getId()); } }); } } }RedisPool连接池
package com.jt.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisDataSource { public static final String IP = "192.168.81.128"; public static final int PORT = 6379; private static volatile JedisPool jedisPool; //方案一1: 饿汉式对象的创建 // static { // //1.1 构建连接池配置(可选,不写有默认的) // JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); // //最大连接数 // jedisPoolConfig.setMaxTotal(16); // //最大空闲数 // jedisPoolConfig.setMaxIdle(8); // jedisPool = new JedisPool(IP,PORT); // // } // // public static Jedis getConnection(){ // return jedisPool.getResource(); // } public static Jedis getConnection(){ if (jedisPool == null) { synchronized (JedisDataSource.class) { if (jedisPool == null) { //1.1 构建连接池配置(可选,不写有默认的) JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); //最大连接数 jedisPoolConfig.setMaxTotal(16); //最大空闲数 jedisPoolConfig.setMaxIdle(8); jedisPool = new JedisPool(IP, PORT); //创建对象分析 //1. 开辟内存 //2. 执行属性的默认初始化 //3. 执行构造方法 //4. 将创建的对象的内存地址赋值给jedisPool变量 //假如使用了volatile修饰jedisPool变量,可以保证如上几个步骤是顺序执行的 } } } return jedisPool.getResource(); } public static void close(){ jedisPool.close(); } }基于redis的单点登录设计及实现
package com.jt.redis; import redis.clients.jedis.Jedis; import java.util.UUID; public class SsoDemo01 { static String token; static String doLogin(String username, String password){ //1.检验数据的合法性(判断用户名,密码是否为空,密码的长度,是否有数字字母特殊符号) if (username==null || "".equals(username)){ throw new IllegalArgumentException("用户名不能为空"); } //2.基于基于用户名查询用户信息,并判定密码是否正确 if (!"jack".equals(username)){ throw new RuntimeException("此用户不存在"); } if (!"123456".equals(password)){ throw new RuntimeException("密码不正确"); } //3.用户存在且面膜正确,将用户信息写入到redis Jedis jedis = new Jedis(JedisDataSource.IP, JedisDataSource.PORT); token = UUID.randomUUID().toString(); jedis.hset(token,"username",username); jedis.hset(token,"permission","sys:resource:create"); jedis.expire(token,10); jedis.close(); //4.将token返回给客户端(将来使用response对象响应到客户端) return token; } static Object doGetResource(String token){ //1. 校验token是否为空 if (token == null){ throw new IllegalArgumentException("请先登录"); } //2. 基于token查询redis数据,假如有对应数据说明用户登陆了 Jedis jedis = new Jedis(JedisDataSource.IP, JedisDataSource.PORT); String username = jedis.hget(token, "username"); if (username==null){ throw new IllegalArgumentException("登录超时,请重新登录"); } String permission = jedis.hget(token, "permission"); if (!"sys:resource:create".equals(permission)){ throw new RuntimeException("你没有权限访问这个资源"); } //3. 检查用户是否有访问资源的权限,假如有则允许访问 //4. 返回要访问的资源 return "your resource"; } public static void main(String[] args) { //1.登陆 *** 作(用户身份认证) token = doLogin("jack", "123456"); //2.携带token访问资源服务器 System.out.println(token); Object o = doGetResource(token); System.out.println(o); } }Redis实现投票系统(简易)
package com.jt.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.Set; public class VoteDemo01 { public static volatile JedisPool jedisPool; public static Jedis getJedis(){ if (jedisPool == null) { synchronized (VoteDemo01.class) { if (jedisPool == null) { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(16); jedisPoolConfig.setMaxIdle(8); jedisPool = new JedisPool(jedisPoolConfig, JedisDataSource.IP, JedisDataSource.PORT); } } } return jedisPool.getResource(); } static void doVote(String activityId,String userId){ //1.建立连接 Jedis jedis = getJedis(); //2.执行投票 Boolean flag = jedis.sismember(activityId, userId); if (flag){ //假如已经投过票,再投票就取消投票 jedis.srem(activityId, userId); }else { //没有投过票则执行投票 jedis.sadd(activityId,userId); } //3.释放资源 jedis.close(); } static Long doCount(String activityId){ //1.建立连接 Jedis jedis = getJedis(); //2.获取当前活动的总票数 Long scard = jedis.scard(activityId); return scard; } static Set重点: 定制序列化与反序列化方式 指定序列化方式前 指定序列化方式后 自定义序列化方式1doGetMembers(String activityId){ //1.建立连接 Jedis jedis = getJedis(); Set smembers = jedis.smembers(activityId); return smembers; } public static void main(String[] args) { String activityId = "101"; String userId1 = "1"; String userId2 = "2"; String userId3 = "3"; //执行投票动作 doVote(activityId,userId1); doVote(activityId,userId2); doVote(activityId,userId3); //获取总票数 Long aLong = doCount(activityId); System.out.println(aLong); //获取哪些人参与了投票 Set strings = doGetMembers(activityId); System.out.println(strings); } }
package com.jt; import com.jt.pojo.Blog; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import java.time.Duration; import java.util.Map; @SpringBootTest public class RedisTemplateTests { @Autowired private RedisTemplate redisTemplate; @Test void testString01(){ //修改1序列化方式 redisTemplate.setKeySerializer(RedisSerializer.string()); //1.获取字符串 *** 作对象 ValueOperations自定义序列化方式2vo = redisTemplate.opsForValue(); //2.读写数据 vo.set("name", "redis"); // Duration.ofSeconds(10) 表示有效时间 vo.set("author", "tony", Duration.ofSeconds(10)); String name = vo.get("name"); System.out.println(name); String author = vo.get("author"); System.out.println(author); } @Test void testHash(){ HashOperations vo = redisTemplate.opsForHash(); vo.put("blog", "name", "tony"); vo.put("blog", "id", "100"); Map
package com.jt; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.RedisSerializer; import java.net.UnknownHostException; @Configuration public class RedisConfig { public RedisSerializer jsonSerializer(){ //1.定义Redis序列化,反序列化规范对象(此对象底层通过ObjectMapper完成对象序列化和反序列化) // new Jackson2JsonRedisSerializer(Blog.class); 指定序列化的对象 Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class); //2.创建ObjectMapper(由jackson api提供)对象,基于此对象进行序列化和反序列化 //2.1 创建ObjectMapper对象 ObjectMapper objectMapper = new ObjectMapper(); //2.2 设置按哪些方法规则进行序列化 PropertyAccessor.GETTER get方法 objectMapper.setVisibility(PropertyAccessor.GETTER, //JsonAutoDetect.Visibility.ANY any表示任意访问修饰符 JsonAutoDetect.Visibility.ANY); //2.3 激活序列化类型存储,对象序列化时还回将对象的类型存储到redis数据库 //假如没有这个配置,redis存储数据时不存储类型,反序列化时会默认将其数据存储到map objectMapper.activateDefaultTyping( //多态校验分析 objectMapper.getPolymorphicTypevalidator(), //激活序列化形式存储 ObjectMapper.DefaultTyping.NON_FINAL, //PROPERTY 表示类型会以json对象属性形式存储 JsonTypeInfo.As.PROPERTY); //对象属性值为null时,不进行序列化存储 //objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); serializer.setObjectMapper(objectMapper); return serializer; } @Bean @ConditionalOnMissingBean( name = {"redisTemplate"} ) public RedisTemplate通过AOP实现自定义序列化redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory); //设置key的序列化方式 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); //设置value的序列化方式 template.setValueSerializer(jsonSerializer()); template.setHashValueSerializer(jsonSerializer()); //更新一下RedisTemplate对象对的默认配置 template.afterPropertiesSet(); return template; } // // @Bean // @ConditionalOnMissingBean( // name = {"redisTemplate"} // ) // public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { // RedisTemplate template = new RedisTemplate(); // template.setConnectionFactory(redisConnectionFactory); // //设置key的序列化方式 // template.setKeySerializer(RedisSerializer.string()); // template.setHashKeySerializer(RedisSerializer.string()); // //设置value的序列化方式 // template.setValueSerializer(RedisSerializer.json()); // template.setHashValueSerializer(RedisSerializer.json()); // return template; // } }
- 启动类上添加此注解
- 在Service层添加注解
package com.jt.service; import com.jt.dao.MenuMapper; import com.jt.pojo.Menu; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class DefaultMenuService implements MenuService { @Autowired private MenuMapper menuMapper; @Cacheable(value = "menuCache",key = "#id") @Override public Menu selectById(Long id) { return menuMapper.selectById(id); } @Override @CachePut(value = "menuCache", key = "#menu.id") public Menu insertMenu(Menu menu) { menuMapper.insert(menu); return menu; } @CachePut(value = "menuCache",key = "#menu.id") @Override public Menu updateMenu(Menu menu) { menuMapper.updateById(menu); return menu; } }
- 添加配置类
package com.jt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; 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.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; @Configuration public class CacheManagerConfig { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public CacheManager cacheManager() { RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() //定义key的序列化方式 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) //定义value的序列化方式 .serializevaluesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); return RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(cacheConfiguration) //建造者模式(复杂对象的创建,建议使用这种方式,封装了对象的创建细节) .build(); } }Redis事务处理 基本指令
multi 开启事务
exec 提交事务
discard 取消事务
watch 监控,如果监控的值发生变化,则提交事务时会失败
unwatch 去掉监控
Redsi 事务处理实践
Redis架构设计应用实践Redis主从,哨兵,集群
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)