文章目录
前言
Redis是一种开源的内存数据结构存储系统,可以用于缓存、消息队列、实时数据处理等多个领域,学习Redis能帮助我们更好地提高系统的性能和可靠性,同时也可以拓宽我们的技术视野和解决问题的思路。
1. redis简介
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis是一个开源的高性能键值对存储数据库,具有以下特点:
- 高性能:Redis是基于内存的,因此读写速度非常快,可以达到每秒百万级别的操作。
- 数据结构丰富:Redis支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等。
- 安全性:Redis支持密码验证,并且可以通过配置文件限制对数据库的访问。
- 可扩展性:Redis支持多个节点的集群,可以通过增加节点来扩充存储容量和吞吐量。
- 简单易用:Redis提供简单的命令行接口和各种语言的客户端库,非常容易使用。
- 持久化:Redis支持数据持久化,可以将数据写入磁盘,以便在重启后恢复数据。
- 发布/订阅:Redis支持发布/订阅模式,可以实现异步消息传递和事件通知。
总之,Redis是一款功能强大、易于使用、高性能的键值对存储数据库,被广泛用于缓存、消息队列、实时统计等场景。
2. redis安装与常用配置
2.1 linux安装示例
redis官网提供了不同系统的安装方式,可参考以下地址:redis官网
这里以在linux centos7安装redis为例:
- 更新系统软件包列表:
sudo yum update
- 安装Redis依赖:
sudo yum install epel-release yum-utils
sudo yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
sudo yum-config-manager --enable remi
sudo yum install redis
- 更改Redis配置文件以允许远程连接:
sudo nano /etc/redis.conf
将bind项更改为服务器的IP地址:
bind SERVER_IP
- 启动Redis服务:
sudo systemctl start redis
- 设置Redis开机自启:
sudo systemctl enable redis
- 验证Redis是否已安装并正在运行:
redis-cli ping
返回“PONG”,则表明成功启动了Redis。
2.2 常用基本配置
配置项 | 示例 | 说明 |
---|---|---|
daemonize | daemonize yes | 是否启用后台运行,默认no |
port | port 6379 | 设置端口号,默认6379 |
logfile | logfile 日志文件 | 设置日志文件 |
databases | databases 255 | 设置redis数据库总量 |
dir | dir 数据文件目录 | 设置数据文件存储目录 |
requirepass | requirepass 123456 | 设置使用密码 |
3. redis基本数据结构类型
3.1 基本数据结构
Redis 有以下5种基本数据类型:
- 字符串(string):最基本的数据类型,支持任何格式的数据,如数字、文本、二进制数据等。适用于缓存、计数、限流等场景。
- 列表(list):链表结构,支持从两端进行插入和删除等操作。适用于任务队列、消息队列等场景。
- 集合(set):无序的字符串集合,支持集合间的交集、并集、差集等操作。适用于去重、标签、好友关系等场景。
- 有序集合(sorted set):在集合的基础上增加了一个权重值,支持按照权重值进行排序和范围查找等操作。适用于排行榜、计数器、排他性锁等场景。
- 哈希表(hash):类似于字典,存储键值对。适用于存储对象或者结构化数据。
不同的数据结构适用于不同的场景,需要根据具体的业务需求进行选择,合理使用每种数据结构可以提高 Redis 的性能和效率。
3.2 使用命令
- 通用命令
命令 | 示例 | 说明 |
---|---|---|
select | select 0 | 选择0号数据库 |
set | set name lily | 设置key=name,value=lily |
get | get hello | 获得key=hello结果 |
keys | keys he* | 根据Pattern表达式查询符合条件的Key |
dbsize | dbsize | 返回key的总数 |
exists | exists a | 检查key=a是否存在 |
del | del a | 删除key=a的数据 |
expire | expire hello 20 | 设置key=hello 20秒后过期 |
ttl | ttl hello | 查看key=a的过期剩余时间 |
- 字符串命令
命令 | 示例 | 说明 |
---|---|---|
get | get hello | 获得key=hello结果 |
set | set hello world | 设置key=hello,value=hello一次性设置 |
mset | mset hello world java best | 一次性设置多个值 |
mget | mget hello java | 一次性获取多个值 |
del | del hello | 删除key=hello |
incr/decr | incr/decr count | key值自增/自减1 |
incrby/decrby | incrby/decrby count 99 | 自增/自减指定步长 |
- Hash命令
命令 | 示例 | 说明 |
---|---|---|
hget | hget emp:1 age | 获取hash中key=age的值 |
hset | hset emp:1 age 23 | 设置hash中age=23 |
hmset | hmset emp:1 age 30 name kaka | 设置hash多个值 |
hmget | hmget emp:1 age name | 获取hash多个值 |
hgetall | hgetall emp:1 | 获取hash所有值 |
hdel | hdel emp:1 age | 删除emp:1的age |
hexists | hexists emp:1 name | 检查是否存在 |
hlen | hlen emp:1 | 获取指定长度 |
- List命令
- rpush listkey c b a - 右侧插入
- lpush listkey f e d - 左侧插入
- rpop listkey - 右侧弹出
- lpop listkey- 左侧弹出
- llen listkey- 获取长度
- lrange listkey 0 2
- lrange listkey 1-1 获取子集
- set/zset命令
- zadd zrem zrange zrangebyscore
- sadd srem srange sinter sdiff sunion
4. java中使用redis
- 在 pom.xml 文件中添加 jedis 依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
- 首先需要创建一个 Jedis 连接池,用于从连接池中获取 Jedis 实例。以下是创建连接池的代码:
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(30);
config.setMaxWaitMillis(10000);
config.setTestOnBorrow(true);
JedisPool jedisPool = new JedisPool(config, "localhost", 6379);
- 编写实现页面缓存的代码。以下是一个简单的实现:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisPageCache {
private JedisPool jedisPool;
public RedisPageCache(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 从缓存中获取页面内容
*
* @param pageUrl 页面 URL
* @return 页面内容
*/
public String getPage(String pageUrl) {
Jedis jedis = jedisPool.getResource();
String pageContent = jedis.get(pageUrl);
jedis.close();
return pageContent;
}
/**
* 将页面内容存入缓存
*
* @param pageUrl 页面 URL
* @param pageContent 页面内容
*/
public void savePage(String pageUrl, String pageContent) {
Jedis jedis = jedisPool.getResource();
jedis.set(pageUrl, pageContent);
jedis.close();
}
}
- 在需要缓存的页面中调用上述方法,如果缓存中有页面内容,则直接返回缓存内容,否则生成页面并存入缓存中:
public class HomePageServlet extends HttpServlet {
private RedisPageCache redisPageCache;
public void init(ServletConfig config) throws ServletException {
super.init(config);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(100);
jedisPoolConfig.setMaxIdle(30);
jedisPoolConfig.setMaxWaitMillis(10000);
jedisPoolConfig.setTestOnBorrow(true);
JedisPool jedisPool = new JedisPool(jedisPoolConfig, "localhost", 6379);
redisPageCache = new RedisPageCache(jedisPool);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String pageUrl = request.getRequestURI();
// 从缓存中获取页面内容
String pageContent = redisPageCache.getPage(pageUrl);
if (pageContent != null) {
// 如果缓存中有页面内容,则直接返回
response.getWriter().write(pageContent);
} else {
// 如果缓存中没有页面内容,则生成页面,并存入缓存中
pageContent = generatePageContent();
redisPageCache.savePage(pageUrl, pageContent);
response.getWriter().write(pageContent);
}
}
private String generatePageContent() {
// 生成页面内容的代码
}
public void destroy() {
// 关闭连接池
redisPageCache.getJedisPool().close();
}
}
注意:
- 在 servlet 中使用时,需要在 init() 方法中初始化 RedisPageCache 实例,并在 destroy() 方法中关闭连接池。
- 可以根据页面内容的变化来判断是否需要重新生成页面并存入缓存中。
- 在缓存页面时,最好设置一个过期时间,以防缓存中的内容过时。
5. 常见问题和应用
5.1 常见问题
- Redis 如何实现分布式缓存?
- 主从复制(Master-Slave Replication):Redis 可以设置一个主节点和多个从节点,主节点负责写入和读取数据,从节点只负责读取数据,当主节点宕机时,从节点可以自动接管成为新的主节点,从而保证数据不会丢失。
- Sentinel 哨兵模式:Redis 可以配置多个主节点,并且在主节点故障时自动切换到备用节点上,以保证系统的高可用性。
- Cluster 集群模式:Redis 可以将数据分布到多个节点上,以实现数据的自动分片和负载均衡。在集群模式下,Redis 会在多个节点之间自动进行数据迁移和复制,以确保数据的一致性和高可用性。
以上三种方式各有优缺点,选择哪种方式取决于实际业务需求和场景。
- Redis 如何实现限流?
- 令牌桶算法
令牌桶算法的核心是令牌桶,系统会以一定的频率往桶里面放入令牌,每次请求需要消耗一个令牌,如果桶里面令牌不足,则请求被限流。在 Redis 中实现令牌桶算法,可以使用 Redis 的 sorted set 数据类型,将每个请求的时间戳作为 score,请求的唯一标识作为 member,然后使用 Redis 的 zrangebyscore 命令获取当前时间窗口内的请求数量,进而判断是否需要限流。 - 计数器算法
计数器算法的核心是对请求次数进行计数,如果请求数超过了设定的阈值,则请求被限流。在 Redis 中实现计数器算法,可以使用 Redis 的 string 类型,每个请求的唯一标识作为 key,请求次数作为 value,然后使用 Redis 的 incrby 命令对请求次数进行累加,进而判断是否需要限流。
- 令牌桶算法
需要注意的是,令牌桶算法和计数器算法都只是 Redis 实现限流的方式之一,具体的实现方式还需要根据业务场景和需求来选择。
-
Redis 持久化策略是什么?
- RDB持久化:将Redis在内存中的数据定期保存到硬盘上,即将Redis中的数据快照(snapshot)持久化到硬盘上。可以通过配置Redis来定期执行RDB快照,或者手动执行SAVE和BGSAVE命令进行快照操作。RDB持久化的优点是可以节约空间和速度快,但是可能会造成数据的丢失。
- AOF持久化:将Redis接收到的每一条写命令以追加的方式写入到硬盘上的文件中,即将Redis的操作日志持久化到硬盘上。可以通过配置Redis来定期执行AOF重写,或者手动执行BGREWRITEAOF命令来进行AOF重写操作。AOF持久化的优点是数据更加可靠,但是速度和空间消耗相对较高。
-
Redis 的过期数据如何处理?有哪些策略可供选择?
- 惰性删除:只有在有读取请求时才检查 key 是否过期并删除。这种方式的优点是可以避免在过期时立即删除 key 所带来的性能开销,但缺点是过期 key 不会被及时删除,会一直占用空间。
- 定期删除:每隔一段时间,Redis 会对数据库进行一次检查,删除过期的 key。这种方式的优点是可以在一定程度上保证过期 key 在一定时间内会被及时删除,但缺点是可能会有一些 key 过期但还没有被检查到。
- 定时删除:在 Redis 中使用一个单独的线程轮询过期 key,并及时删除。这种方式的优点是可以实时删除过期 key,但缺点是相对于其他策略,需要更多的系统资源。
可以根据具体的业务需求和系统资源情况选择适合的过期数据处理策略。
5.2 应用场景
-
缓存
Redis 可以作为缓存来存储频繁请求的数据。它可以在内存中存储数据,速度非常快,可以大大缩短数据访问的响应时间。 -
队列
Redis 支持多种数据结构,包括列表、哈希表、集合和有序集合等。这些数据结构可以用来实现队列、栈等数据结构。 -
计数器
Redis 的原子性操作可以用来实现计数器,比如统计网站访问量、在线人数等。 -
分布式锁
Redis 支持分布式锁,可以用来解决多台服务器共享操作时的并发问题。 -
发布/订阅
Redis 支持发布订阅模式,可以用来实现消息队列等功能。 -
排名
Redis 支持有序集合,可以用来实现排名功能,比如排名前十的热门文章、排名前十的用户等。
以上便是本文全部内容啦,希望能够带来帮助!