Redis基础入门(系统化)

152 阅读8分钟

1. 什么是Redis?

Redis(Remote Dictionary Server)本质上是一个Key-Value类型的内存数据库,数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。

2. Redis的特点

  1. 速度快
    • 内存读写每秒110000/81000次
  2. 持久化
    • 数据的更新将异步地保存到硬盘(RDB和 AOF)
  3. 多种数据结构
    • key-value 类型数据,还支持:字符串、hash、列表、集合、有序集合
  4. 简单稳定
    • 源码少、单线程模型
  5. 功能丰富
    • HyperLogLog、GEO、发布订阅、Lua脚本、事务、Pipeline、Bitmaps,key 过期

2. Redis的使用场景

1. 缓存系统

  • 页面缓存:存储频繁访问的网页内容
  • 数据库查询结果缓存:减少 database 压力
  • 会话存储:存储用户登录状态和会话信息
  • API 响应缓存:缓存第三方 API 调用结果

2. 数据库功能

  • 主数据库:存储结构化数据和非结构化数据
  • 实时数据存储:存储实时更新的数据如排行榜
  • 计数器应用:实现访问统计、点赞数等功能

3. 消息队列

  • 任务队列:异步处理后台任务
  • 发布/订阅:实现消息广播和实时通信
  • 事件驱动架构:支持微服务间的消息传递

4. 实时应用

  • 聊天应用:存储聊天记录和在线用户状态
  • 游戏排行榜:维护实时分数和排名
  • 实时分析:处理流式数据和实时统计

5. 分布式系统

  • 分布式锁:实现跨服务的资源锁定
  • 服务发现:存储服务注册信息
  • 配置管理:集中管理应用配置

6. 会话管理

  • 用户认证:存储 token 和认证信息
  • 购物车:电商网站的购物车数据存储
  • 临时数据存储:存储临时会话数据

3.Redis安装

这里分享的是windows用户使用wsl将redis下载安装的教程。

  1. 在终端安装Ubuntu Linux操作系统(如有,忽略)
# 通过 Microsoft Store 安装 Ubuntu
# 打开 Microsoft Store
start ms-windows-store://search/?query=Ubuntu

点击get下载,然后Open打开,install结束
2. 在终端进入Wsl Linux系统

# 启动 Ubuntu
wsl -d Ubuntu
  1. 在Linux系统下安装Redis 并启动
# 更新包管理器
sudo apt update

# 安装 Redis
sudo apt install redis-server

# 启动 Redis 服务
sudo service redis-server start

# 查看状态
sudo service redis-server status

好了,现在redis运行在你的windows里面的linux环境了
4. 连接Redis

# 启动 Redis CLI
redis-cli

# 连接远程 Redis 服务器
redis-cli -h hostname -p port

image.png
现在操作redis数据库的命令行界面cli也配好了,可以对数据库做操作了。

4. Redis 数据结构

String(字符串)

stringredis最基本的类型,一个key对应一个value

string类型是二进制安全的。意思是redisstring可以包含任何数据。比如jpg图片或者序列化的对象 。

string类型是Redis最基本的数据类型,一个键最大能存储512MB。

实例

redis 127.0.0.1:6379> SET name "w3cschool.cn"
OK
redis 127.0.0.1:6379> GET name
"w3cschool.cn"

在以上实例中我们使用了 Redis 的 SET 和 GET 命令。键为 name,对应的值为w3cschool.cn。

注意: 一个键最大能存储512MB。


Hash(哈希)

Redis hash 是一个键值 (key=>value) 对集合。

Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。

实例

redis 127.0.0.1:6379> HMSET user:1 username w3cschool.cn password w3cschool.cn points 200
OK
redis 127.0.0.1:6379> HGETALL user:1
1) "username"
2) "w3cschool.cn"
3) "password"
4) "w3cschool.cn"
5) "points"
6) "200"
redis 127.0.0.1:6379>

以上实例中 hash 数据类型存储了包含用户脚本信息的用户对象。实例中我们使用了 Redis HMSET, HGETALL 命令,user:1 为键值。每个 hash 可以存储 232 - 1键值对(40多亿)。


List(列表)

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

实例

redis 127.0.0.1:6379> lpush w3cschool.cn redis
(integer) 1
redis 127.0.0.1:6379> lpush w3cschool.cn mongodb
(integer) 2
redis 127.0.0.1:6379> lpush w3cschool.cn rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange w3cschool.cn 0 10
1) "rabitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>

列表最多可存储 232 -1元素 (4294967295, 每个列表可存储40多亿)。


Set(集合)

Redis 的 Set是 string 类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

sadd 命令

添加一个 string 元素到key 对应的 set 集合中,成功返回1,如果元素已经在集合中返回0,key 对应的 set 不存在返回错误。

sadd key member

实例

redis 127.0.0.1:6379> sadd w3cschool.cn redis
(integer) 1
redis 127.0.0.1:6379> sadd w3cschool.cn mongodb
(integer) 1
redis 127.0.0.1:6379> sadd w3cschool.cn rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd w3cschool.cn rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers w3cschool.cn

1) "rabitmq"
2) "mongodb"
3) "redis"

注意: 以上实例中 rabitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。

集合中最大的成员数为 232-1 (4294967295, 每个集合可存储40多亿个成员)。


zset(sorted set:有序集合)

Redis zset 和 set 一样也是 string 类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

zset 的成员是唯一的,但分数(score)却可以重复。

zadd 命令

添加元素到集合,元素在集合中存在则更新对应 score

zadd key score member 

实例

redis 127.0.0.1:6379> zadd w3cschool.cn 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd w3cschool.cn 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd w3cschool.cn 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd w3cschool.cn 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE w3cschool.cn 0 1000

1) "redis"
2) "mongodb"
3) "rabitmq"

5. Redis 常用命令

www.w3cschool.cn/redis/redis…

1. 字符串操作命令

  • SET key value - 设置键值对
  • GET key - 获取键的值
  • INCR key - 将键的值递增1
  • DECR key - 将键的值递减1
  • INCRBY key increment - 将键的值增加指定数值
  • APPEND key value - 将值追加到键的现有值末尾

2. 哈希操作命令

  • HSET key field value - 设置哈希表键的字段值
  • HGET key field - 获取哈希表键的字段值
  • HMSET key field1 value1 field2 value2 - 同时设置多个哈希字段
  • HGETALL key - 获取哈希表中所有的字段和值
  • HDEL key field - 删除哈希表中的一个或多个字段

3. 列表操作命令

  • LPUSH key value - 将值插入到列表头部
  • RPUSH key value - 将值插入到列表尾部
  • LPOP key - 移除并返回列表的第一个元素
  • RPOP key - 移除并返回列表的最后一个元素
  • LRANGE key start stop - 获取列表中指定范围的元素

4. 集合操作命令

  • SADD key member - 向集合添加成员
  • SMEMBERS key - 获取集合中的所有成员
  • SISMEMBER key member - 判断成员是否是集合的成员
  • SREM key member - 移除集合中的指定成员
  • SINTER key1 key2 - 返回多个集合的交集

5. 有序集合操作命令

  • ZADD key score member - 向有序集合添加成员
  • ZRANGE key start stop - 返回有序集合指定范围的成员
  • ZSCORE key member - 返回有序集合中指定成员的分数
  • ZREM key member - 移除有序集合中的一个或多个成员
  • ZRANK key member - 返回有序集合中指定成员的排名

6. 键管理命令

  • KEYS pattern - 查找所有符合给定模式的键
  • EXISTS key - 检查键是否存在
  • DEL key - 删除键
  • TTL key - 获取键的剩余生存时间
  • EXPIRE key seconds - 设置键的过期时间
  • TYPE key - 返回键所存储值的类型

7. 服务器管理命令

  • PING - 测试连接是否存活
  • INFO - 获取服务器信息和统计
  • FLUSHDB - 清空当前数据库
  • FLUSHALL - 清空所有数据库
  • SAVE - 同步保存数据到磁盘
  • BGSAVE - 异步保存数据到磁盘

六、Redis+Javascript应用

String的应用:为个人博客文章内容添加文章缓存

连接配置

// redis.js
const redis = require('redis');

const client = redis.createClient({
  host: 'localhost',
  port: 6379,
  // password: 'your_password', // 如果有密码
  retry_strategy: (options) => {
    if (options.error && options.error.code === 'ECONNREFUSED') {
      return new Error('Redis server refused the connection');
    }
    if (options.total_retry_time > 1000 * 60 * 60) {
      return new Error('Retry time exhausted');
    }
    if (options.attempt > 10) {
      return undefined;
    }
    return Math.min(options.attempt * 100, 3000);
  }
});

client.on('connect', () => {
  console.log('Redis client connected');
});

client.on('error', (err) => {
  console.error('Redis error:', err);
});

module.exports = client;

方法参考

// blogService.js
const redis = require('./redis');
const blogsData = require('../assets/blogs.json');

class BlogService {
  // 获取单篇文章(带缓存)
  async getPostById(postId) {
    try {
      // 先尝试从 Redis 获取缓存
      const cachedPost = await redis.get(`post:${postId}`);
      
      if (cachedPost) {
        console.log(`Cache hit for post ${postId}`);
        return JSON.parse(cachedPost);
      }
      
      // 缓存未命中,从原始数据源获取
      const post = blogsData.posts.find(p => p.id === parseInt(postId));
      
      if (post) {
        // 将文章存入 Redis 缓存,设置1小时过期时间
        await redis.setex(`post:${postId}`, 3600, JSON.stringify(post));
        console.log(`Cache set for post ${postId}`);
      }
      
      return post;
    } catch (error) {
      console.error('Error getting post:', error);
      // 出错时直接从数据源获取,不使用缓存
      return blogsData.posts.find(p => p.id === parseInt(postId));
    }
  }
  
  // 获取所有文章列表(带缓存)
  async getAllPosts() {
    try {
      const cachedPosts = await redis.get('posts:all');
      
      if (cachedPosts) {
        console.log('Cache hit for all posts');
        return JSON.parse(cachedPosts);
      }
      
      const posts = blogsData.posts;
      
      // 缓存所有文章列表,设置30分钟过期时间
      await redis.setex('posts:all', 1800, JSON.stringify(posts));
      console.log('Cache set for all posts');
      
      return posts;
    } catch (error) {
      console.error('Error getting all posts:', error);
      return blogsData.posts;
    }
  }
  
  // 搜索文章(带缓存)
  async searchPosts(query) {
    try {
      const cacheKey = `search:${query.toLowerCase()}`;
      const cachedResults = await redis.get(cacheKey);
      
      if (cachedResults) {
        console.log(`Cache hit for search query: ${query}`);
        return JSON.parse(cachedResults);
      }
      
      // 执行搜索逻辑
      const results = blogsData.posts.filter(post => 
        post.title.toLowerCase().includes(query.toLowerCase()) ||
        post.content.toLowerCase().includes(query.toLowerCase()) ||
        post.tags.some(tag => tag.toLowerCase().includes(query.toLowerCase())) ||
        post.author.toLowerCase().includes(query.toLowerCase())
      );
      
      // 缓存搜索结果,设置15分钟过期时间
      await redis.setex(cacheKey, 900, JSON.stringify(results));
      console.log(`Cache set for search query: ${query}`);
      
      return results;
    } catch (error) {
      console.error('Error searching posts:', error);
      // 出错时直接执行搜索,不使用缓存
      return blogsData.posts.filter(post => 
        post.title.toLowerCase().includes(query.toLowerCase()) ||
        post.content.toLowerCase().includes(query.toLowerCase()) ||
        post.tags.some(tag => tag.toLowerCase().includes(query.toLowerCase())) ||
        post.author.toLowerCase().includes(query.toLowerCase())
      );
    }
  }
  
  // 更新文章时清除相关缓存
  async clearPostCache(postId) {
    try {
      // 清除单篇文章缓存
      await redis.del(`post:${postId}`);
      
      // 清除文章列表缓存
      await redis.del('posts:all');
      
      // 清除可能的相关搜索缓存(这里简化处理)
      console.log(`Cache cleared for post ${postId}`);
    } catch (error) {
      console.error('Error clearing cache:', error);
    }
  }
  
  // 更新文章浏览量
  async incrementViewCount(postId) {
    try {
      const viewKey = `post:views:${postId}`;
      const currentViews = await redis.incr(viewKey);
      
      // 设置过期时间,防止键永久存在
      if (currentViews === 1) {
        await redis.expire(viewKey, 86400); // 24小时过期
      }
      
      return currentViews;
    } catch (error) {
      console.error('Error incrementing view count:', error);
      return 0;
    }
  }
  
  // 获取文章浏览量
  async getViewCount(postId) {
    try {
      const viewKey = `post:views:${postId}`;
      const views = await redis.get(viewKey);
      return views ? parseInt(views) : 0;
    } catch (error) {
      console.error('Error getting view count:', error);
      return 0;
    }
  }
}

module.exports = new BlogService();

Redis 哈希(Hash)数据结构在博客网站中的应用

1. 存储文章元数据

// 使用 Redis Hash 存储文章基本信息
await redis.hset(`post:${postId}`, {
  'title': post.title,
  'author': post.author,
  'created_at': post.created_at,
  'excerpt': getExcerpt(post.content),
  'view_count': 0
});

// 获取文章信息
const postInfo = await redis.hgetall(`post:${postId}`);

2. 用户会话管理

// 存储用户会话信息
await redis.hset(`session:${sessionId}`, {
  'user_id': userId,
  'username': username,
  'login_time': new Date().toISOString(),
  'last_activity': new Date().toISOString()
});

// 更新用户最后活动时间
await redis.hset(`session:${sessionId}`, 'last_activity', new Date().toISOString());

3. 文章统计信息

// 存储文章的各种统计数据
await redis.hset(`post:stats:${postId}`, {
  'views': viewCount,
  'likes': likeCount,
  'comments': commentCount,
  'shares': shareCount
});

// 增加文章浏览量
await redis.hincrby(`post:stats:${postId}`, 'views', 1);

4. 博客配置信息

// 存储博客网站配置
await redis.hset('blog:config', {
  'site_title': 'Harry的主页',
  'site_subtitle': '个人展示/心得分享/技术笔记',
  'posts_per_page': 10,
  'enable_comments': true
});

5. 搜索索引数据

// 为文章创建搜索索引
await redis.hset(`search:index:${postId}`, {
  'title': post.title.toLowerCase(),
  'content': getExcerpt(post.content).toLowerCase(),
  'tags': post.tags.join(',').toLowerCase(),
  'author': post.author.toLowerCase()
});