高级Redis应用进阶 一站式Redis解决方案-Redis-1 Redis
源代码在https://github.com/629y/food-social-contact-parent
1. Redis
功能强大,持续改进,经久不衰
1.高性能
底层C语言编写,内存数据库,通讯采用epoll非阻塞l/O多路复用机制
2.线程安全
3.功能丰富
数据结构:
基本:String、LIst、HashSet、SortredSet
高阶:GEO、BitMap、HyperLogLog
持久化:RDB持久化、AOF持久化、RDB-AOF混合持久化
主从:Master-Slave解决并发
哨兵:Sentinel
集群:分片
模块化...
2. Redis介绍以及为什么能支撑10W+QPS
2.1.特点
内存数据库,速度快,也支持数据的持久化
Redis不仅仅支持简单的key-value类型的数据,同时还提供Lists、Hashes、Sets 、Sorted Sets等多种数据结构的存储
Redis支持数据的备份(master-slave)与集群(分片存储),以及拥有哨兵监控机制
支持事务
2.2.优势
性能极高– Redis能读的速度是110000次/s,写的速度是81000次/s
丰富的数据类型 - Redis支持 Strings、Lists、Hashes、Sets 、Sorted Sets等数据类型操作
原子操作-Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行(事务)
丰富的特性 - Redis还支持 publish/subscribe,通知, key过期等特性
2.3.Redis、Memcached、Ehcache的区别
这三个中间件都可以应用于缓存,但目前市面上使用Redis的场景会更多,更广泛,其原因是: Redis性能高、原子操作、支持多种数据类型,主从复制与哨兵监控,持久化操作等。
2.4.Redis的高并发
官方的bench-mark数据:测试完成了50个并发执行100000个请求。设置和获取的值是一个256字节字符串。结果:读的速度是110000次/s,写的速度是81000次/s。redis尽量少写多读,符合缓存的适用要求。单机redis支撑万级,如果10万+可以采用主从复制的模式。
2.4.1.原理
1. Redis是纯内存数据库,所以读取速度快。
2. Redis使用的是非阻塞lO,IO多路复用,减少了线程切换时上下文的切换和竞争。
3. Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
4. Redis存储结构多样化,不同的数据结构对数据存储进行了优化加快读取的速度。
5. Redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。
2.4.2. 原因
1.不需要各种锁的性能消耗
2. 单线程多进程集群方案
3. CPU消耗
2.4.3 优势
单进程单线程优势
1. 代码更清晰,处理逻辑更简单
2. 不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
3.不存在多进程或者多线程导致的切换而消耗CPU
单进程单线程弊端
无法发挥多核CPu性能,不过可以通过在单机开多个Redis实例来完善
2.5 IO多路复用技术
redis采用网络lO多路复用技术来保证在多连接的时候,系统的高吞吐量。
3. Redis的安装与多种启动方式详解
下载
安装C语言的依赖
查看版本
升级GCC
编译
创建redis目录,然后把刚才安装完的redis装到这个目录上去
可执行文件
这是前端启动,ctrl+c 退出
以守护进程的方式运行
后端启动
4. Redis配置文件及GUI客户端工具连接
Redis配置文件
关闭防火墙,开启redis
修改配置文件,开放对应的权限( 实际开发中一般不能这么做)
密码
再次启动服务
5. Redis自带的客户端的基本命令操作
Redis支持多种语言的客户端,这也是Redis受欢迎的原因之一。https://redis.io/clients
Java客户端
./redis-cli -a *******
-h ip地址,如果是本机,就可以省略
-p 6379 端口号
-a 密码
./redis-cli -h r-uf6qppd4tmlvzgig1ypd.redis.rds.aliyuncs.com -p 6379 -a *******
select 0 切换
info 查看所有
info CPU 查看cpu的使用率
info repilication 查看主从
info cluster 查看集群
flushall 清除全部数据(谨慎使用)
6. Redis的Java客户端及性能优化
新建项目jedis-demo-01
源代码:https://github.com/629y/jedis
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>5.1.0</version>
</dependency>
package com.imooc;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class JedisTest {
Jedis jedis = null;
//建立连接
@Before
public void init() {
//初始化 Jedis 客户端,声明主机和端口
jedis = new Jedis
("r-uf6qppd4tmlvzgig1ypd.redis.rds.aliyuncs.com",6379);
//身份认证
jedis.auth("Courseimooc1");
//PING PONG 心跳机制检测是否连接成功
String pong = jedis.ping();
System.out.println("pong = " + pong);
}
@Test
public void testString(){
//插入一条数据
String result = jedis.set("username", "zhangsan");
System.out.println("result = " + result);
}
//释放资源
@After
public void close() {
if (null != jedis){
jedis.close();
}
}
}
package com.imooc;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.Set;
public class JedisTest {
Jedis jedis = null;
//建立连接
@Before
public void init() {
//初始化 Jedis 客户端,声明主机和端口
jedis = new Jedis
("r-uf6qppd4tmlvzgig1ypd.redis.rds.aliyuncs.com",6379);
//身份认证
jedis.auth("Courseimooc1");
//PING PONG 心跳机制检测是否连接成功
String pong = jedis.ping();
System.out.println("pong = " + pong);
}
@Test
public void testString(){
//选择数据库
String selResult = jedis.select(2);
System.out.println("selResult = " + selResult);
//插入一条数据
String result = jedis.set("username", "zhangsan");
System.out.println("result = " + result);
//获取一条数据
String username = jedis.get("username");
System.out.println("username = " + username);
//层级也是可以的
//插入一条数据
jedis.set("imooc:users:1","zhangsan");
//获取一条数据
String users1 = jedis.get("imooc:users:1");
System.out.println("users1 = " + users1);
}
@Test
public void testKeys() {
jedis.select(2);
Set<String> keys = jedis.keys("*");
System.out.println(keys);
}
//释放资源
@After
public void close() {
if (null != jedis){
jedis.close();
}
}
}
JedisPoolConnectRedis.java
package com.imooc;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* 连接池工具类
*/
public class JedisPoolConnectRedis {
private static JedisPool jedisPool;
static {
//创建连接池配置对象
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//设置最大连接数,默认8
jedisPoolConfig.setMaxTotal(5);
//设置最大空闲数量,默认8
jedisPoolConfig.setMaxIdle(5);
//设置最少空闲数量,默认0
jedisPoolConfig.setMinIdle(0);
//设置等待时间 ms
jedisPoolConfig.setMaxWaitMillis(100);
//初始化 JedisPool 对象
jedisPool = new JedisPool(jedisPoolConfig,"r-uf6qppd4tmlvzgig1ypd.redis.rds.aliyuncs.com",6379,100,"Courseimooc1");
}
/**
* 获取Jedis对象
* @return
*/
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
复制JedisTest.java重命名JedisPoolTest.java
JedisPoolTest.java
7. SpringBoot集成Redis
源代码:GitHub - 629y/springdata-demo: jedis,lettuce
SpringDataDemoApplication.java
package com.imooc.springdatademo;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
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;
@SpringBootApplication
public class SpringDataDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDataDemoApplication.class, args);
}
@Bean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
//JsonAutoDetect.Visibility.ANY 代表所有属性或字段都可以序列化
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//新版用法
//以数组的方式存放到Redis,Class Type 全类名作为为第一个元素,Json字符串为第二个元素。
objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
//老版用法,已弃用
//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(serializer);
template.setHashKeySerializer(RedisSerializer.string());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
application.yml
spring:
# redis 配置
redis:
host: r-uf6qppd4tmlvzgig1ypd.redis.rds.aliyuncs.com
port: 6379
password: *****
database: 5
# jedis 连接池配置
# jedis:
# pool:
# max-active: 8
# max-idle: 8
# min-idle: 0
# max-wait: 1000
# lettuce 连接池配置
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 1000
SpringDataDemoApplicationTests.java
package com.imooc.springdatademo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.Resource;
@SpringBootTest
class SpringDataDemoApplicationTests {
@Resource
private RedisTemplate redisTemplate;
@Resource
private ObjectMapper objectMapper;
@Test
void testInit(){
//PING PONG 心跳机制是否连接成功
String pong = redisTemplate.getConnectionFactory().getConnection().ping();
System.out.println("pong = " + pong);
}
@Test
public void testString(){
//插入一条数据
redisTemplate.opsForValue().set("username","zhangsan");
//获取一条数据
Object username = redisTemplate.opsForValue().get("username");
System.out.println("username = " + username);
}
@Test
void contextLoads() throws JsonProcessingException {
Person person = new Person("john",23);
String string = objectMapper.writeValueAsString(person);
redisTemplate.opsForValue().set("name",string);
Object o = redisTemplate.opsForValue().get("name");
System.out.println(o);
}
}
8. 美食社交APP需求分析与数据库表结构设计
需求分析
以美食社交APP后台API接口设计为例。涉及APP中用户、好友、订单为基础的相关业务,分为用户、好友、排行榜、优惠券/秒杀、订单、附近的人、Feed等微服务。完成用户登录、交友、发朋友圈以及购买优惠券、下单整个业务流程,并实现积分排行榜以及附近的人等相关功能。
数据库表结构设计
9. 项目架构与微服务搭建
源代码:GitHub - 629y/food-social-contact-parent: redis项目-美食社交APP
1.删除src,添加pom依赖
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.imooc</groupId>
<artifactId>food-social-contact-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 可以集中定义依赖资源的版本信息 -->
<properties>
<spring-boot-version>2.3.5.RELEASE</spring-boot-version>
<spring-cloud-version>Hoxton.SR8</spring-cloud-version>
<lombok-version>1.18.16</lombok-version>
<commons-lang-version>3.11</commons-lang-version>
<mybatis-starter-version>2.1.3</mybatis-starter-version>
<mysql-version>8.0.22</mysql-version>
<swagger-starter-version>2.1.5-RELEASE</swagger-starter-version>
<hutool-version>5.4.7</hutool-version>
<guava-version>20.0</guava-version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 集中定义依赖,不引入 -->
<dependencyManagement>
<dependencies>
<!-- spring boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- lombok 依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
</dependency>
<!-- common-lang3 依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang-version}</version>
</dependency>
<!-- mybatis 依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter-version}</version>
</dependency>
<!-- swagger 依赖 -->
<dependency>
<groupId>com.battcn</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>${swagger-starter-version}</version>
</dependency>
<!-- mysql 依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-version}</version>
</dependency>
<!-- hutool 依赖 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-version}</version>
</dependency>
<!-- guava 依赖 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava-version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 集中定义项目所需插件 -->
<build>
<pluginManagement>
<plugins>
<!-- spring boot maven 项目打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
2.创建ms-registry模块
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.imooc</groupId>
<artifactId>food-social-contact-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>ms-registry</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
RegistryApplication.java
application.yml
server:
port: 8080
spring:
application:
name: ms-registry
# 配置 Eureka Server 注册中心
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8080/eureka/
3.创建ms-gateway模块
application.yml
server:
port: 80
spring:
application:
name: ms-gateway
# 配置 Eureka Server 注册中心
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:8080/eureka/
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>food-social-contact-parent</artifactId>
<groupId>com.imooc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-gateway</artifactId>
<dependencies>
<!-- spring cloud gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
启动RegistryApplication、GatewayApplication
4.创建ms-diners模块
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>food-social-contact-parent</artifactId>
<groupId>com.imooc</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ms-diners</artifactId>
<dependencies>
<!-- eureka client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8081
spring:
application:
name: ms-diners
# 配置 Eureka Server 注册中心
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:8080/eureka/
DinersApplication.java
package com.imooc.diners;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DinersApplication {
public static void main(String[] args) {
SpringApplication.run(DinersApplication.class, args);
}
}
为了方便测试
HelloController.java
如果想要从网关那里访问的话,需要改一些网关的规则
application.yml(gateway)
http://localhost:8081/hello?name=redis
重启 RegistryApplication、GatewayApplication
http://localhost/hello?name=redis