Redis-基础篇

Redis是一个开源、高性能、内存键值存储数据库,由 Salvatore Sanfilippo(网名antirez)创建,并在BSD许可下发布。它不仅可以用作缓存系统来加速数据访问,还可以作为持久化的主数据存储系统或消息中间件使用。Redis因其数据结构丰富、性能优异和高可用性而被广泛应用在现代分布式架构中。

目录

一、认识Redis

1.1、认识NoSQL

1.2、认识Redis

1.3、安装Redis

二、Redis的常见命令

2.1、5种常见的数据结构

2.2、通用命令

2.3、不同数据结构的操作命令

三、Redis的Java客户端

3.1、Jedis客户端

3.2、SpringDataRedis客户端


一、认识Redis

1.1、认识NoSQL

SQL:结构化、表有关联、支持SQL查询、满足事务的ACID、存储在磁盘、垂直扩展、适用于数据结构固定,相关业务的数据安全性与一致性要求高的场合

NoSQL:非结构、无关联的、非SQL、满足基本一致性、存储在内存、水平扩展、适用于数据结构不固定,对一致性与安全性要求不高,但是对性能要求较高的场合

常见的NoSQL有:键值对类型的Redis、文档类型的MongoDB

1.2、认识Redis

Redis( Remote Dictionary Server)的全程是远程字典服务器,是一个基于内存的非关系型数据库。

redis的特征:
1.键值对类型的数据库,支持丰富的数据类型

2.单线程,每个命令具备原子性

3.低延迟,速度快(基于内存、IO多路复用、良好的编码)

4.支持数据的持久化

5.支持主从集群与分片集群

6.支持多语言客户端

1.3、安装Redis

这个不过多介绍,直接下载安装即可。

安装redis服务器和客户端,并安装redis可视化管理工具Another Redis Desktop Manager。

二、Redis的常见命令

2.1、5种常见的数据结构

redis是一个键值对类型的数据库,key的类型一般是String,value的类型就多种多样:

基本数据类型:

String类型:缓存用户信息、计数器(例如点赞数、浏览量)、简单的键值对存储等。

Hash类型:存储用户属性集合(如用户的姓名、年龄、地址等)、产品详情等多字段数据结构。

List类型:存储用户属性集合(如用户的姓名、年龄、地址等)、产品详情等多字段数据结构。

Set类型:适用于标签系统(给一篇文章打上多个标签)、唯一事件记录等。

SortedSet类型:跳跃表提供O(log N)级别的插入、删除和查找操作,并按分数排序。

特殊数据类型:

GEO类型:Geo数据类型允许用户存储地理位置信息,并执行地理半径查询、邻近点搜索等操作。

BitMap类型:用于用户在线状态跟踪、访问统计(例如用户是否读过某篇文章)。

HyperLog类型:日活用户统计、网站独立访客统计、广告点击去重等需要估算大量唯一元素数量而不需精确存储所有元素的场景。

2.2、通用命令

通用命令常见的有:

KEYS:查看符合模板的所有key,不建议在生产环境上使用

DEL:删除一个指定的key

EXISTS:判断一个key是否存在

EXPIRE:为一个key设置有效期,有效期到了key会自动删除

TTL:查看key的剩余有效期

2.3、不同数据结构的操作命令

String类型:字符串类型,包括普通字符串,整数,浮点数

API如下:

SET:添加或者修改string类型的键值对

GET:根据key获取string类型的value

MSET:批量添加多个string类型的键值对

MGET:根据string类型的key获取多个string类型的值

INCR:让整型的key自增1

INCREBY:整数设置步长的自增

INCREBYFLOAT:按照指定步长的浮点型自增

SETNX:添加一个string类型的键值对,前提是key不存在,否则不执行

SETEX:添加一个string类型的键值对,并指定有效期

redis的key允许有多个单词形成层级结构,多个单词用“:”隔开,如果value是一个Java对象,则可以将对象序列化成JSON字符串后存储:
例如key可以为  项目名:业务名:类型:id      value为{“id”:1,"product":"小米手机","price":"2999"}

这样redis会根据冒号:进行层级划分。

Hash类型:也称为散列,value是一个无序字典,类似于Java中的HashMap结构。之前的string类型的value是将对象序列化成JSON字符串后存储,当需要修改某个字段时很不方便。

Hash结构可以将每个字段独立存储,可以针对每个字段进行操作。

相关的API:

HSET key field value:添加或者修改hash类型的key的field值

HGET key field:获取一个hash类型的key的field值

HMSET:批量添加多个hash类型key的field值

HMGET:批量查询多个hash类型key的field值

HGETALL:获取一个hash类型key的所有feild的值

HKEYS:获取一个hash类型的key中的所有feild

HVALS:获取一个hash类型的key中的所有value

HINCREBY:让hash类型的key自增并指定步长

HSETNX:添加一个hash类型的field,前提是field不存在,否则不执行

List类型:List类型与Java中的LinkedList类似,可以看作是一个双向链表,支持正向与反向检索。

特征:有序、允许元素重复、插入删除快、查询速度一般

List的常见命令如下:

LPUSH key element...:向列表左侧插入一个或者多个元素

LPOP key :移除并返回列表左侧额第一个元素,没有则返回nil

RPUSH key element...:向列表右侧插入一个或者多个元素

RPOP key:移除并返回列表右侧的第一个元素

LRANGE key  start end:返回一段角标范围内的所有元素

BLPOP与BRPOP:移除指定的元素,没有元素时并设置等待时间,而不是直接返回nil

List模拟栈:lpush与lpop  rpush与rpop

List模拟队列:lpush与rpop

List模拟阻塞队列:blpop与brpop

Set类型:Redis中的set与Java中的HashSet类似,具有如下特征:无序、元素不可重复、查找快

、支持交集、并集差集等操作。

SET类型的常见命令:

SADD key member...:向set中添加一个或者多个元素

SREM key member...:向set中移除指定元素

SCARD key:返回set中的元素个数

SISMEMBER key member:判断一个元素是否在set中

SMEMBERS:获取set中的所有元素

SINTER key1 key2:求两个key的交集

SDIFF:求两个集合的差集

SUNION:求两个集合的并集

SortedSet类型:有序的集合,每个元素都带有一个score属性,可以根据score属性进行排序,底层是一个跳表+hash表的实现。跳跃表提供O(log N)级别的插入、删除和查找操作,并按分数排序;hash表用于快速查找成员的存在性。

跳表是通过随机函数维护平衡性的,当我们在跳表中插入数据的时候,我们通过选择同时将这个数据插入到部分索引层中,如何选择索引层,可以通过一个随机函数来决定这个节点插入到哪几级索引中,比如随机生成了k,那么就将这个索引加入到,第一级到第k级索引中。

SortedSet具有以下特点:

1、可排序 2、元素不重复 3、查询速度快

常见的SortedSort的api如下:默认是升序,如果向降序前缀由Z改成ZREV

三、Redis的Java客户端

3.1、Jedis客户端

常见的Redis的Java客户端有Jedis、Lettuce、Redisson三种,具体如下。

下面我们通过jedis客户端连接redis服务器,并进行单元测试,具体如下:

1.首先添加三方依赖。

    <!--jedis客户端依赖--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version></dependency><!-- junit测试依赖 --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.7.0</version><scope>test</scope></dependency>

2.编写单元测试模块,jedis客户端连接redis服务器,并进行crud基本操作,最后释放连接。

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;import java.util.Map;/*** @author nuist__NJUPT* @ClassName JedisTest* @description: jedis测试类* @date 2024/03/02*/
public class JedisTest {private Jedis jedis ;@BeforeEachvoid setUp(){// 1.建立连接jedis = new Jedis("localhost", 6379) ;// 2.设置密码jedis.auth("123456") ;// 3.选择库jedis.select(0) ;}@Testvoid testString(){// 存入数据String result = jedis.set("name", "mandy");System.out.println(result);// 获取数据String name = jedis.get("name");System.out.println("name : " + name );}@Testvoid testHash(){// 存值jedis.hset("user:1", "name", "jack") ;jedis.hset("user:1", "age", "21") ;// 取值Map<String, String> stringStringMap = jedis.hgetAll("user:1");System.out.println(stringStringMap);}@AfterEachvoid tearDown(){if(jedis != null){jedis.close();}}}

jedis本身是线程不安全的,而且频繁的创建与销毁jedis连接会有性能损耗,因此推荐使用jedis连接池的方式代替直连的方式。

1.定义一个连接池工具类,用于建立jedis连接,并返回jedis对象,jedis使用完放回连接池而不是直接销毁。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;/*** @author nuist__NJUPT* @ClassName JedisConnectionFactory* @description: Jedis连接池* @date 2024/03/02*/
public class JedisConnectionFactory {private static final JedisPool jedisPool ;static {// 配置连接池JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(8);jedisPoolConfig.setMaxIdle(8);jedisPoolConfig.setMinIdle(0);jedisPoolConfig.setMaxWaitMillis(1000);// 创建连接池对象jedisPool= new JedisPool(jedisPoolConfig, "localhost", 6379, 1000, "123456");}public static Jedis getJedis(){return jedisPool.getResource() ;}}

2.客户端直接通过连接池获取jedis对象就可以,不用直接newjedis对象进行直连了。

import com.alibaba.jedis.util.JedisConnectionFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;import java.util.Map;/*** @author nuist__NJUPT* @ClassName JedisTest* @description: jedis测试类* @date 2024/03/02*/
public class JedisTest {private Jedis jedis ;@BeforeEachvoid setUp(){// 1.建立连接// jedis = new Jedis("localhost", 6379) ;jedis = JedisConnectionFactory.getJedis() ;// 2.设置密码jedis.auth("123456") ;// 3.选择库jedis.select(0) ;}@Testvoid testString(){// 存入数据String result = jedis.set("name", "mandy");System.out.println(result);// 获取数据String name = jedis.get("name");System.out.println("name : " + name );}@Testvoid testHash(){// 存值jedis.hset("user:1", "name", "jack") ;jedis.hset("user:1", "age", "21") ;// 取值Map<String, String> stringStringMap = jedis.hgetAll("user:1");System.out.println(stringStringMap);}@AfterEachvoid tearDown(){if(jedis != null){jedis.close();}}}

3.2、SpringDataRedis客户端

SpringData是Spring中数据操作的模块,包含了对多种数据库的集成,其中对redis的集成就是SpringDataRedis。它提供了对不同redis客户端的整合(jedis、Lettuce),提供了RedisTemplate统一API来操作Redis,支持redis的发布订阅模型,支持redis哨兵和redis集群,支持基于Lettuce的响应式编程,支持序列化与反序列化。

下面看一下SpringDataRedis提供的工具类RedisTemplate的应用,首先创建springboot项目并导入redis依赖。

  <!--	jackson依赖	--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.5</version></dependency><!--redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 连接池依赖 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency>

然后在yml文件中进行配值,配值redis的数据源信息。

spring:redis:host: 127.0.0.1port: 6379password: 123456lettuce:pool:max-active: 8max-idle: 8min-idle: 0max-wait: 100

编写redisTemplate的配值类,防止在redis在接收Object类型时,把Object对象序列化成字节形式,变成一串乱码,可读性差,占用内存。


/*** @author nuist__NJUPT* @ClassName RedisConfig* @description: redis配置类* @date 2024/03/02*/import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;@Configuration
public class RedisConfig {/*** RedisTemplate可以接收任意Object作为值写入Redis,* 只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的一串很长的值* 缺点:可读性查、浪费存储空间*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){// 1.创建 redisTemplateRedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 2.设置连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);// 3.设置序列化工具GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();// key 采用 String 序列化redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setHashKeySerializer(RedisSerializer.string());// value 采用 json 序列化redisTemplate.setValueSerializer(jsonRedisSerializer);redisTemplate.setHashValueSerializer(jsonRedisSerializer);return  redisTemplate;}
}

定义一个实体类。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author nuist__NJUPT* @ClassName User* @description: 实体类* @date 2024/03/02*/@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private String name ;private Integer age ;}

最后编写单元测试模块,利用redisTemplate进行测试。

import com.alibaba.redisdemo.pojo.User;
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.RedisTemplate;@SpringBootTest
class RedisDemoApplicationTests {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Testvoid testString(){// 添加一个元素redisTemplate.opsForValue().set("name", "Jack");// 获取元素Object object = redisTemplate.opsForValue().get("name");System.out.println(object);}@Testvoid testSaveUser(){//写入数据redisTemplate.opsForValue().set("user:2", new User("wang", 18));// 读取数据User user = (User) redisTemplate.opsForValue().get("user:2");System.out.println(user);}@Testvoid testHash(){stringRedisTemplate.opsForHash().put("user:3","name", "liu");stringRedisTemplate.opsForHash().put("user:3","age","18") ;Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:3");System.out.println("entries = " + entries);}}

上述尽管Json序列化的方式满足要求,但是会发现仍然存在一些问题,比如JSON序列化器将类型class写入了json中,存入redis会导致额外的内存开销。

为了节省内存空间,不使用JSON序列化来处理value,而是处理String序列化器,要求只能存储String类型的key与value,当存储Java对象的时候需要手动的序列化与反序列化。

可以采用如下写法:
 

 @Autowiredprivate StringRedisTemplate stringRedisTemplate ;private static final ObjectMapper mapper = new ObjectMapper() ;@Testvoid testSave() throws JsonProcessingException {// 创建对象User user = new User("wang", 18);// 手动序列化String s = mapper.writeValueAsString(user);//写入数据stringRedisTemplate.opsForValue().set("user:2",s);// 读取数据String user1 = stringRedisTemplate.opsForValue().get("user:2");// s手动反序列化User user2 = mapper.readValue(user1, User.class);System.out.println(user2);}

使用fastJson进行序列化与反序列化也可以,需要添加依赖。

    @Autowiredprivate StringRedisTemplate stringRedisTemplate ;@Testvoid testSave() throws JsonProcessingException {// 创建对象User user = new User("wang", 19);// 手动序列化String s = JSON.toJSONString(user) ;//写入数据stringRedisTemplate.opsForValue().set("user:2",s);// 读取数据String user1 = stringRedisTemplate.opsForValue().get("user:2");// s手动反序列化User user2 = JSON.parseObject(user1, User.class) ;System.out.println(user2);}

添加fastJson依赖。

<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.32</version>
</dependency>

总结:redis中有两种序列化方式,推荐使用第二种。

1.第一种是自定义RedisTemplate,修改其序列化器,相对方便,但是写入redis会存class对象,占用额外的内存空间。

2.使用StringRedisTemplate,默认使用String序列化器,写入redis需要将Java对象手动序列化为json,读取redis需要将读取到的json反序列化为Java对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/716121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode:37.解数独

题目理解&#xff1a;本题中棋盘的每一个位置都要放一个数字&#xff08;而N皇后是一行只放一个皇后&#xff09;&#xff0c;并检查数字是否合法&#xff0c;解数独的树形结构要比N皇后更宽更深。 代码实现&#xff1a;

SpringBoot+Redis 解决海量重复提交问题,yyds!

在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求&#xff0c;我们来解释一下幂等的概念&#xff1a;任意多次执行所产生的影响均与一次执行的影响相同。按照这个含义&#xff0c;最终的含义就是 对数据库的影响只能是一次性的&#xff0c;不能重复处理。如何保证其…

JetBrains Gateway Github Copilot 客户端插件和主机插件

JetBrains Gateway可以通过插件支持Github Copilot&#xff08;需另行注册&#xff09;。 需要安装插件 客户端&#xff0c;而非插件 主机&#xff0c;如图所示&#xff1a; 大概是因为代码显示在客户端&#xff08;运行在本地的IDE&#xff09;&#xff1f;

NOC2023软件创意编程(学而思赛道)python初中组复赛真题

目录 下载打印原文档做题: 软件创意编程 一、参赛范围 1.参赛组别:小学低年级组(1-3 年级)、小学高年级组(4-6 年级)、初中组。 2.参赛人数:1 人。 3.指导教师:1 人(可空缺)。 4.每人限参加 1 个赛项。 组别确定:以地方教育行政主管部门(教委、教育厅、教育局) 认…

Python 潮流周刊#40:白宫建议使用 Python 等内存安全的语言

△△请给“Python猫”加星标 &#xff0c;以免错过文章推送 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯&#xff0c;…

三层靶机靶场之环境搭建

下载&#xff1a; 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;f4as 简介 2019某CTF线下赛真题内网结合WEB攻防题库&#xff0c;涉 及WEB攻击&#xff0c;内网代理路由等技术&#xff0c;每台服务器存在一个 Flag&#xff0c;获取每一 个Flag对应一个积分&…

在docker中搭建selenium 爬虫环境(3分钟快速搭建)

1、安装docker 省略 2、拉取镜像 docker pull selenium/standalone-chrome-debug 3、运行容器 docker run -d -p 4444:4444 -p 5900:5900 -v C:\Users\Public\VNC_Donwnloads:/home/seluser/Downloads --memory6g --name selenium_chrome selenium/standalone-chrome-debu…

大数据核心技术概论

大数据核心技术概述 大数据基石三大论文&#xff1a;GFS&#xff08;Hadoop HDFS&#xff09;、BigTable&#xff08;Apache HBase&#xff09;、MapReduce&#xff08;Hadoop MapReduce&#xff09;。 搜索引擎的核心任务&#xff1a;一是数据采集&#xff0c;也就是网页的爬…

matlab 感知器算法

1. 原理 两类线性可分的模式类&#xff1a;&#xff0c;设判别函数为&#xff1a;。 对样本进行规范化处理&#xff0c;即类样本全部乘以(-1)&#xff0c;则有&#xff1a; 感知器算法通过对已知类别的训练样本集的学习&#xff0c;寻找一个满足上式的权向量。 2. 过程 2.1 …

力扣 分割回文串

输出的是不同的分割方案 class Solution { public:vector<vector<bool>>flag;vector<string>ans;vector<vector<string>>nums;void dfs(string &s,int i){int ns.size();if(in){i表示s长度&#xff0c;等于即全部分割完毕nums.push_back(ans…

第十三篇【传奇开心果系列】Python的文本和语音相互转换库技术点案例示例:Microsoft Azure的Face API开发人脸识别门禁系统经典案例

传奇开心果博文系列 系列博文目录Python的文本和语音相互转换库技术点案例示例系列 博文目录前言一、实现步骤和雏形示例代码二、扩展思路介绍三、活体检测深度解读和示例代码四、人脸注册和管理示例代码五、实时监控和报警示例代码六、多因素认证示例代码七、访客管理示例代码…

03-JNI 类型和数据结构

上一篇&#xff1a; 02-设计概述 本章讨论 JNI 如何将 Java 类型映射为本地 C 类型。 3.1 原始类型 下表描述了 Java 原始类型及其与机器相关的本地等价类型。 为方便起见&#xff0c;定义如下&#xff1a; #define JNI_FALSE 0 #define JNI_TRUE 1 jsize 整数类型用于描述…

逻辑回归与决策边界解析

目录 前言1 逻辑回归基础1.1 Sigmoid函数&#xff1a;打开分类之门1.2 决策函数&#xff1a;划定分类界限1.3 逻辑回归详解 2 决策边界2.1 线性决策边界2.2 非线性决策边界2.3 决策边界的优化 3 应用与实例3.1 垃圾邮件分类&#xff1a;精准过滤3.2 金融欺诈检测&#xff1a;保…

.idea文件详解

.idea文件的作用&#xff1a; .idea文件夹是存储IntelliJ IDEA项目的配置信息&#xff0c;主要内容有IntelliJ IDEA项目本身的一些编译配置、文件编码信息、jar包的数据源和相关的插件配置信息。一般用git做版本控制的时候会把.idea文件夹排除&#xff0c;因为这个文件下保存的…

ISO_IEC_18598-2016自动化基础设施管理(AIM)系统国际标准解读(一)

██ ISO_IEC_18598-2016是什么标准&#xff1f; ISO/IEC 18598国际标准是由ISO&#xff08;国际标准化组织&#xff09;/IEC&#xff08;国际电工委员会&#xff09;联合技术委员会1-信息技术的第25分委员会-信息技术设备互连小组制定的关于信息基础设施自动化管理的国际标准&…

【AI Agent系列】【MetaGPT多智能体学习】6. 多智能体实战 - 基于MetaGPT实现游戏【你说我猜】(附完整代码)

本系列文章跟随《MetaGPT多智能体课程》&#xff08;https://github.com/datawhalechina/hugging-multi-agent&#xff09;&#xff0c;深入理解并实践多智能体系统的开发。 本文为该课程的第四章&#xff08;多智能体开发&#xff09;的第四篇笔记。今天我们来完成第四章的作…

java垃圾回收

垃圾回收 一个对象如果不再使用&#xff0c;需要手动释放&#xff0c;否则就会出现内存泄漏。我们称这种释放对象的过程为垃圾回收&#xff0c;而需要程序员编写代码进行回收的方式为手动回收。 内存泄漏指的是不再使用的对象在系统中未被回收&#xff0c;内存泄漏的积累可能…

【ArcGIS Pro二次开发】(83):ProWindow和WPF的一些技巧

在ArcGIS Pro二次开发中&#xff0c;SDK提供了一种工具界面【ArcGIS Pro ProWindow】。 关于ProWindow的用法&#xff0c;之前写过一篇基础的教程&#xff1a; 【ArcGIS Pro二次开发】(13)&#xff1a;ProWindow的用法_arcgispro二次开发教程-CSDN博客 主要是对几个常用控件…

【嵌入式实践】【芝麻】【设计篇-2】从0到1给电动车添加指纹锁:项目可行性分析

0. 前言 该项目是基于stm32F103和指纹模块做了一个通过指纹锁控制电动车的小工具。支持添加指纹、删除指纹&#xff0c;电动车进入P档等待时计时&#xff0c;计时超过5min则自动锁车&#xff0c;计时过程中按刹车可中断P档状态&#xff0c;同时中断锁车计时。改项目我称之为“芝…

EMR StarRocks实战——猿辅导的OLAP演进之路

目录 一、数据需求产生 二、OLAP选型 2.1 需求 2.2 调研 2.3 对比 三、StarRocks的优势 四、业务场景和技术方案 4.1 整体的数据架构 4.2 BI自助/报表/多维分析 4.3 实时事件分析 4.5 直播教室引擎性能监控 4.4 B端业务后台—斑马 4.5 学校端数据产品—飞象星球 4…