Redis 学习笔记
1. Redis 简介
什么是 Redis?
- Redis 是一个基于内存的 NoSQL 数据库,专门用于处理高并发和分布式项目中常见的性能瓶颈问题。
- NoSQL:全称为 “Not Only SQL”。与传统关系型数据库不同,NoSQL 数据库通常无需表结构、外键关系等复杂设计,数据以键值对形式存储,适合大规模、高性能的分布式环境。
Redis 的由来
- Redis 的开发者是意大利人 Salvatore Sanfilippo。他在创建网站过程中,发现传统数据库(如 MySQL 和 Oracle)的硬盘存储带来性能瓶颈,决定开发基于内存的数据库来解决这个问题,进而诞生了 Redis。
- Redis Labs:Salvatore 在开发 Redis 后成立了 Redis Labs,目前为全球超过 8000 家公司提供 Redis 解决方案。
Redis 的核心特性
- 基于内存:Redis 将数据存储在内存中,极大提升了读取速度和性能,特别适用于高并发场景。
- 高性能:相比传统的关系型数据库,Redis 的数据检索速度可以提升 10 倍甚至更多。
- 持久化:虽然 Redis 是基于内存存储,但它也支持将数据持久化到磁盘,确保在重启或当机后数据不丢失。
什么是 NoSQL?
- Not Only SQL:NoSQL 数据库不局限于 SQL 的关系型数据结构,通常采用键值对(Key-Value)形式存储数据。
-
与关系型数据库的区别:
- 关系型数据库的典型特征是表结构、主键、外键以及复杂的表关系(如一对一、一对多、多对多)。
- 关系型数据库在处理大规模数据或分布式数据扩展时效率较低,而 NoSQL 数据库可以有效应对这些挑战。
- NoSQL 数据库适合处理非结构化数据,具有高可扩展性和灵活性。
Redis 的数据存储方式
- Redis 的数据是以 键值对(Key-Value) 形式存储的,这类似于编程语言中的
Map
或HashMap
,每个键(Key)对应一个值(Value)。 - 键值对的存储方式使得 Redis 在查询时具有极高的效率,特别是在需要快速数据读取和写入的场景。
内存数据库的优势
- 速度快:由于数据存储在内存中,Redis 的数据处理速度比传统的硬盘存储数据库快得多。适用于处理大量并发请求的场景,如电商网站、社交媒体等。
- 高并发处理:在电商场景中,通过将商品数据从传统数据库如 MySQL 中提取并缓存在 Redis 中,可以大大提升数据的检索速度,避免传统硬盘存储的瓶颈。
Redis 的持久化功能
- 尽管 Redis 是基于内存的数据库,但它支持将内存数据定期或实时持久化到磁盘中,防止数据丢失。
- RDB(快照)和 AOF(追加日志)是 Redis 的两种持久化机制,分别用于定期存储和记录每个操作。
Redis 在现代互联网中的作用
- 随着硬件价格的下降,内存的普及,Redis 等基于内存的数据库开始被广泛应用。特别是近年来,互联网行业对高并发处理的需求越来越大,Redis 作为缓存解决方案备受青睐。
2.Redis 核心特性
2. 1. 速度快
- Redis 最显著的特点是极高的速度。在峰值情况下,Redis 每秒可以处理 十万次操作。即使在常规场景下,Redis 每秒处理 两三万次操作 也是可以保证的。这种速度远远超过传统的关系型数据库(如 MySQL、Oracle),因为 Redis 是基于内存操作,读写速度非常快。
2. 2. 多语言支持
- Redis 拥有广泛的开发语言支持。无论是常见的 Java、C#、Python 等开发语言,还是其他主流开发工具,Redis 都有良好的兼容性和交互工具。同时,由于 Redis 是开源的,开发者甚至可以为特定需求自定义 Redis 交互工具。
2. 3. 持久化机制
- Redis 提供了两种持久化机制,确保数据在 Redis 失去服务后不丢失:
- RDB (Redis DataBase):基于全量备份的持久化方案。
- AOF (Append Only File):基于日志更新的持久化方案,通过记录每次操作来持久化数据。
2. 4. 多种数据结构支持
-
Redis 支持丰富的数据类型和结构,如:
- 字符串 (String):最基本的数据类型。
- 列表 (List):类似于数组的数据结构。
- 哈希 (Hash):键值对的映射集合。
- 集合 (Set):无序且唯一的集合。
- 有序集合 (Sorted Set):带有排序的集合结构。
这些数据结构帮助开发者灵活地处理各种类型的数据需求。
2. 5. 主从复制
- 主从复制是指多个 Redis 服务器之间保持数据同步的机制。通过主从复制,数据可以在多台 Redis 实例之间保持一致,这样即使某台 Redis 出现问题,其他服务器也能继续提供服务。
2. 6. 分布式与高可用
- 分布式:Redis 支持将数据分布到多台服务器上,这些服务器可以位于不同地域。例如,一台服务器位于广州,另一台服务器位于北京,程序可以根据地理位置就近访问 Redis,减少网络延迟。
- 高可用性:Redis 提供了 Sentinel 哨兵机制,用于监控集群中的 Redis 实例,自动发现出现故障的节点并进行切换,确保 Redis 服务的持续可用性,避免单点故障。
2. 7. 哨兵机制 (Sentinel)
- Redis 的哨兵机制 (Sentinel) 提供了一种自动化的高可用解决方案,可以监控 Redis 实例并在出现问题时自动完成故障转移。哨兵可以监测主从关系中的主节点状态,如果主节点出现故障,它会自动将从节点提升为主节点,从而确保系统的稳定运行。
3.Redis Linux安装
参考官网
sudo apt-get install lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis
连接到 Redis
一旦 Redis 运行,您可以通过运行以下命令对其进行测试redis-cli:
redis-cli
使用以下命令测试连接ping:
127.0.0.1:6379> ping
PONG
sudo vim /etc/redis/redis.conf #找到6379更改默认端口
#找到databases可以更改数据库个数
#找到requirepass可以修改密码
#找到dir可以指定数据保存路径
redis-cli -p 6380 #找到更改的端口号
4. Redis 通用命令课程笔记
课程简介
本节课介绍了 Redis 中一些最常用的通用命令,这些命令涵盖了 Redis 数据库的选择、数据的设置和获取、数据库中的键管理、过期时间设置等方面。这些命令将在后续学习中反复用到。
Redis 通用命令
-
select
- 用于选择不同编号的数据库。
- Redis 默认有 16 个数据库(0-15)。
- 使用
select
命令选择指定编号的数据库:select 0 # 选择0号数据库 select 1 # 选择1号数据库
-
set
- 设置键值对数据,格式为
set key value
。 - 例如:
set name lily # 设置 key=name, value=lily
- 设置键值对数据,格式为
-
get
- 获取指定键的值,格式为
get key
。 - 例如:
get name # 获取 key=name 对应的值
- 获取指定键的值,格式为
-
keys
- 列举当前数据库中所有匹配的键,使用通配符查询:
keys * # 列出所有键 keys he* # 列出以 he 开头的键
- 列举当前数据库中所有匹配的键,使用通配符查询:
-
dbsize
- 返回当前数据库中键的总量:
dbsize # 返回当前数据库中所有键的数量
- 返回当前数据库中键的总量:
-
exists
- 检查某个键是否存在,存在返回 1,不存在返回 0:
exists name # 检查 key=name 是否存在
- 检查某个键是否存在,存在返回 1,不存在返回 0:
-
del
- 删除指定的键:
del name # 删除 key=name
- 删除指定的键:
-
expire
- 设置键的过期时间(秒),过期后该键会被自动删除:
expire name 20 # 设置 key=name 的过期时间为20秒
- 设置键的过期时间(秒),过期后该键会被自动删除:
-
ttl
- 查询某个键的剩余存活时间(秒),如果该键没有设置过期时间,则返回 -1:
ttl name # 查询 key=name 的剩余过期时间
- 查询某个键的剩余存活时间(秒),如果该键没有设置过期时间,则返回 -1:
Redis 服务操作
- 启动 Redis 服务:
redis-server
- 连接 Redis 客户端:
redis-cli -p 6379 # 默认端口是6379
- 如果 Redis 设置了密码,连接后需要认证:
auth 12345 # 使用 auth 命令进行认证
注意事项
- 不同编号的数据库之间的数据互不影响。
- 使用
set
命令对同一个键多次赋值时,后面的值会覆盖前面的值。 -
keys *
虽然可以列出所有键,但如果数据库中的数据量非常大,不建议使用,因为效率较低。 - 使用
dbsize
查询数据总量是非常高效的,底层通过计数器实现,不需要遍历所有数据。
5. Redis 五种常用数据类型及字符串类型课程笔记
课程简介
本节课介绍了 Redis 中五种常用的数据类型,包括字符串、哈希、列表、集合和有序集合。我们重点学习了字符串类型,它是最常用、最基础的数据类型。
Redis 五种数据类型
-
字符串类型(String)
- Redis 中最常用的数据类型。
- 可以存储字符串和数字,支持最大 512MB 的数据长度。
-
哈希类型(Hash)
- 类似于字典,键值对的 value 本身是一个键值对结构。
-
列表类型(List)
- 类似于数组,可以存储多个有序的值。
-
集合类型(Set)
- 无序集合,元素不允许重复。
-
有序集合类型(ZSet)
- 有序集合,集合中的每个元素关联一个分数,元素按照分数排序。
字符串类型(String)
-
特点:
- 支持存储字符串或数字。
- 字符串是 Redis 中最常用的数据类型。
- 常用于存储简单的数据,如用户姓名、年龄等。
常用命令
-
SET:设置键值对
set key value
例如:
set name lily # 设置 key=name, value=lily
-
GET:获取键的值
get key
例如:
get name # 获取 key=name 的值
-
MSET:一次设置多个键值对
mset key1 value1 key2 value2 ...
例如:
mset name1 Kitty age1 20 birthday1 1999-01-12
-
MGET:一次获取多个键的值
mget key1 key2 ...
例如:
mget name1 age1 birthday1
-
INCR:将键对应的值自增1
incr key
例如:
incr age # 将 age 的值自增1
-
DECR:将键对应的值自减1
decr key
-
DEL:删除键
del key
例如:
del name # 删除 key=name
特殊用法
-
计数器:
- 可以通过
INCR
和DECR
实现自增或自减操作,适用于计数场景。 - 例如:
incr age
将年龄自增1。
- 可以通过
-
批量操作:
- 通过
MSET
和MGET
可以一次操作多个键值对,提高效率。
- 通过
Redis 哈希键值类型课程笔记
哈希键值类型的特点
- 适用于存储结构化数据,例如用户信息、员工信息等。
- 可以通过一个 key 存储多个属性及其对应的值,类似于 Java 中的
Map
。 - 适用于保存包含多个属性的对象,简化 Redis 的数据存储结构,提高可用性。
常用命令
-
HSET:设置哈希键的字段和值
hset key field value
例如:
hset emp:1 name "张三" hset emp:1 age 35 hset emp:1 birthday "1983-04-03" hset emp:1 height 178
-
HGET:获取哈希键指定字段的值
hget key field
例如:
hget emp:1 name # 获取name字段的值
-
HGETALL:获取哈希键的所有字段和值
hgetall key
例如:
hgetall emp:1 # 获取一号员工所有属性和值
-
HMSET:一次设置多个字段和值
hmset key field1 value1 field2 value2 ...
例如:
hmset emp:2 name "Lisa" age 23 birthday "1990-05-03" height 165
-
HDEL:删除哈希键的某个字段
hdel key field
例如:
hdel emp:2 age # 删除emp:2中的age字段
-
HLEN:获取哈希键的字段数量
hlen key
例如:
hlen emp:1 # 返回一号员工的属性数量
-
HEXISTS:判断哈希键的字段是否存在
hexists key field
例如:
hexists emp:1 name # 判断name字段是否存在
Redis 列表类型课程笔记
列表类型特点
- 元素类型:列表中的每个元素必须是字符串。
- 有序性:按照插入顺序排序。
- 长度:列表的最大长度为 2^32 - 1,约 40 亿个元素。
- 常用场景:存储同一类型的数据集合,如学生信息列表、电脑设备列表等。
列表常用命令
-
RPUSH:从列表右侧插入元素
rpush key element1 element2 ...
例如:
rpush list_key C B A # 在列表右侧依次插入 C、B、A
-
LPUSH:从列表左侧插入元素
lpush key element1 element2 ...
例如:
lpush list_key F E D # 在列表左侧依次插入 F、E、D
-
RPOP:从列表右侧弹出元素
rpop key
例如:
rpop list_key # 从列表右侧弹出一个元素
-
LPOP:从列表左侧弹出元素
lpop key
例如:
lpop list_key # 从列表左侧弹出一个元素
-
LRANGE:获取列表中指定范围的元素
lrange key start stop
例如:
lrange list_key 0 -1 # 获取列表中的所有元素,-1 表示最后一个元素
Redis 集合类型(Set 和 ZSet)课程笔记
Set 和 ZSet 的特点
Set 集合特点
- 无序:Set 集合中的元素没有顺序。
- 唯一性:Set 集合中的元素都是唯一的,不能重复。
- 常见场景:Set 常用于需要快速判断元素是否存在的场景,例如标签管理、用户角色管理等。
ZSet 集合特点
- 有序:ZSet 集合中的元素通过设置的分数进行排序,默认按分数升序排列。
- 唯一性:和 Set 一样,ZSet 中的元素也是唯一的,但可以为每个元素设置一个分数。
- 常见场景:ZSet 常用于排行榜、带权重的元素存储等需要排序的场景。
Set 常用命令
-
SADD:向集合添加元素
sadd key element1 element2 ...
例如:
sadd set1 A B C
-
SMEMBERS:获取集合中的所有元素
smembers key
例如:
smembers set1 # 获取集合 set1 的所有元素
-
SINTER:获取两个集合的交集
sinter key1 key2
例如:
sinter set1 set2 # 获取 set1 和 set2 的交集
-
SUNION:获取两个集合的并集
sunion key1 key2
例如:
sunion set1 set2 # 获取 set1 和 set2 的并集
-
SDIFF:获取两个集合的差集
sdiff key1 key2
例如:
sdiff set1 set2 # 获取 set1 中有但 set2 中没有的元素
ZSet 常用命令
-
ZADD:向有序集合添加元素及其分数
zadd key score1 element1 score2 element2 ...
例如:
zadd zset1 100 A 101 B 99 C
-
ZRANGE:按分数顺序获取有序集合中的元素
zrange key start stop [WITHSCORES]
例如:
zrange zset1 0 -1 WITHSCORES # 获取 zset1 中的所有元素及其分数
-
ZRANGEBYSCORE:按分数范围获取有序集合中的元素
zrangebyscore key min max
例如:
zrangebyscore zset1 100 103 # 获取分数在100到103之间的元素
package com.imooc.jedis;
import com.alibaba.fastjson.JSON;
import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CacheSample {
public CacheSample(){
Jedis jedis = new Jedis("47.98.151.127");
try {
List<Goods> goodsList = new ArrayList<Goods>();
goodsList.add(new Goods(8818, "红富士苹果", "", 3.5f));
goodsList.add(new Goods(8819, "进口脐橙", "", 5f));
goodsList.add(new Goods(8820, "进口香蕉", "", 25f));
jedis.select(3);
for (Goods goods : goodsList) {
String json = JSON.toJSONString(goods);
System.out.println(json);
String key = "goods:" + goods.getGoodsId();
jedis.set(key , json);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
jedis.close();
}
}
public static void main(String[] args) {
new CacheSample();
System.out.printf("请输入要查询的商品编号:");
String goodsId = new Scanner(System.in).next();
Jedis jedis = new Jedis("47.98.151.127");
try{
jedis.select(3);
String key = "goods:" + goodsId;
if(jedis.exists(key)){
String json = jedis.get(key);
System.out.println(json);
Goods g = JSON.parseObject(json, Goods.class);
System.out.println(g.getGoodsName());
System.out.println(g.getPrice());
}else{
System.out.println("您输入的商品编号不存在,请重新输入!");
}
}catch(Exception e){
e.printStackTrace();
}finally {
jedis.close();
}
}
}