Redis 学习笔记总结
Posted IT_Holmes
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 学习笔记总结相关的知识,希望对你有一定的参考价值。
文章目录
- 1. Redis 事务
- 2. Redis 事务冲突的问题
- 3. Redis 事务 秒杀案例
- 4. Redis 持久化之 RDB(Redis DataBase)
- 5. Redis 持久化之 AOF(Append Only File)
- 6. Redis 主从复制
- 7. Redis 主从复制 搭建一主多从
- 8. Redis 主从复制的原理
- 9. Redis 主从复制之 一主二仆
- 10. Redis 主从复制之 薪火相传 , 反客为主
- 11. Redis 主从复制之 哨兵模式(sentinel)
1. Redis 事务
1.1 Redis事务的作用
Redis 事务是一个单独的隔离操作,事务中的所有命令都会序列化,按顺序地执行(因为,单线程+IO多路复用)。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis 事务的主要作用就是串联多个命令防止别的命令插队
。
1.2 Multi , Exec , discard 命令(事务常用的三个命令)
multi 和 exec 命令执行效果:
而discard命令是放弃组队,就类似mysql的回滚。
1.3 事务的错误处理
在组队阶段,如果我们某个命令出现了错误,整个队列中的命令都会被取消。
下面这段英文要知道!!
如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他正常的命令都会执行,不会回滚。
2. Redis 事务冲突的问题
2.1 什么是事务冲突?
像下面这种多个事务,同时对一个数据进行操作的情况,就是事务冲突。
2.2 如何解决事务冲突? 乐观锁和悲观锁
解决事务冲突就必须要用到锁。
锁一般分为悲观锁和乐观锁。
悲观锁:无论对数据进行什么操作的时候,都先会上锁,这样别人就不能对该数据进行增删改查操作了。除非解锁。
(这个锁的缺点就是效率很低。)
乐观锁:与悲观锁相反。但是版本号的机制要注意!!
版本号机制要知道!!!
2.3 watch 命令 和 unwatch命令
通过watch命令来监视一个或多个键。
unwatch命令就是取消watch命令对所有key的监视。
如果在执行watch命令之后,执行了exec命令或discard命令的话,那就不需要再执行unwatch了。
2.4 Redis 事务三个特性
首先,redis是不支持ACID四特性的。redis对于事务有专门自己的三个特性。
3. Redis 事务 秒杀案例
3.1 秒杀案例的 基本实现
首先,确定库存和成功用户的要使用redis的数据类型,如下:
package com.itholmes.jedis;
import java.util.Random;
import redis.clients.jedis.Jedis;
public class SecKill_redis
public static void main(String[] args)
Random random = new Random();
//模拟随机用户
String uid = "";
for(int i=0;i<4;i++)
//随机生成10以内的一个数字
int rand = random.nextInt(10);
uid += rand;
//商品标签为0101
String prodid = "0101";
//假设多个用户调用这个方法进行秒杀,那么数据库中的对应key-value就行操作的!
doSecKill(uid, prodid);
//秒杀过程,uid就是用户id,prodid就是库存id
public static boolean doSecKill(String uid,String prodid)
//1.uid和prodid非空判断
if(uid == null || prodid == null)
return false;
//2.连接redis
Jedis jedis = new Jedis("39.103.163.156",6379);
//3.拼接key
//3.1 库存key
String kuncunKey = "sk:"+prodid+":qt";
//3.2 秒杀成功用户key
String userKey = "sk:"+prodid+":user";
//4.获取库存,如果库存本身等于null,表示秒杀还没有开始
String kuncun = jedis.get(kuncunKey);
if(kuncun == null)
System.out.println("秒杀还没有开始,请等待!");
jedis.close();
return false;
//5.判断用户是否重复秒杀操作(每个用户秒杀只能秒杀一次)
Boolean sismember = jedis.sismember(userKey, uid);
if(sismember)
System.out.println("已经秒杀成功了,不能重复秒杀!");
jedis.close();
return false;
//6.判断如果商品数量,库存数量 小于1了,说明秒杀结束了
int i = Integer.parseInt(kuncun);
if(i<1)
System.out.println("秒杀已经结束了");
jedis.close();
return false;
//7. 秒杀过程
//7.1 库存要减1
jedis.decr(kuncunKey);
//7.2 把秒杀成功用户添加到清单里面
jedis.sadd(userKey, uid);
System.out.println("秒杀成功了");
jedis.close();
return true;
3.2 使用ab 来模拟并发秒杀
yum install httpd-tools 或 apt-get install httpd-tools 安装httpd-tools工具。
我们要使用它的ab,ab是apache性能测试工具。
ab 的参数。
- -n 表示当前请求次数。
- -c 表示当前的并发次数。
- -p 表示post方法发送过来的参数存放位置。
- -T 表示post方法设置类型。
通过这个方式来测试并发效果很好!!
就3.1的秒杀代码而言,有严重的并发问题!例如:库存成为了负数(超卖问题),再者就是太多的请求,会有超时问题!
3.3 通过连接池解决 连接超时的问题
redis不能同时处理多个请求,就有了连接超时的问题。
连接池:节省每次连接redis服务带来的消耗,把连接好的实例反复利用。
模拟一个jedis数据库池的效果:
package com.itholmes.jedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolUtil
private static volatile JedisPool jedisPool = null;
private JedisPoolUtil()
public static JedisPool getJedisPoolInstance()
if(null == jedisPool)
//加锁
synchronized (JedisPoolUtil.class)
if(null == jedisPool)
JedisPoolConfig poolConfig = new JedisPoolConfig();
//设置一堆参数
poolConfig.setMaxTotal(200);
poolConfig.setMaxIdle(32);
poolConfig.setMaxWaitMillis(100*1000);
poolConfig.setBlockWhenExhausted(true);
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig,"39.103.163.156",6379,60000);
return jedisPool;
public static void release(JedisPool jedisPool, Jedis jedis)
if(null != jedis)
//关闭归还连接。
jedis.close();
3.4 如何解决并发出现的超卖现象(库存为负数)
首先,可以通过watch乐观锁来解决!(记住乐观锁的版本号对应机制)
注意下面是如何开启redis事务(组或队列)的!!
package com.itholmes.jedis;
import java.util.List;
import java.util.Random;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
public class SecKill_redis
public static void main(String[] args)
Random random = new Random();
//模拟随机用户
String uid = "";
for(int i=0;i<4;i++)
//随机生成10以内的一个数字
int rand = random.nextInt(10);
uid += rand;
//商品标签为0101
String prodid = "0101";
//假设多个用户调用这个方法进行秒杀,那么数据库中的对应key-value就行操作的!
doSecKill(uid, prodid);
//秒杀过程,uid就是用户id,prodid就是库存id
public static boolean doSecKill(String uid,String prodid)
//1.uid和prodid非空判断
if(uid == null || prodid == null)
return false;
//2.连接redis
//Jedis jedis = new Jedis("39.103.163.156",6379);
//这样我们就可以通过连接池得到jedis对象,通过连接池解决超时问题。
JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = jedisPool.getResource();
//3.拼接key
//3.1 库存key
String kuncunKey = "sk:"+prodid+":qt";
//3.2 秒杀成功用户key
String userKey = "sk:"+prodid+":user";
//监视库存,相当于开启了乐观锁(记住乐观锁的版本号对应机制)
jedis.watch(kuncunKey);
//4.获取库存,如果库存本身等于null,表示秒杀还没有开始
String kuncun = jedis.get(kuncunKey);
if(kuncun == null)
System.out.println("秒杀还没有开始,请等待!");
jedis.close();
return false;
//5.判断用户是否重复秒杀操作(每个用户秒杀只能秒杀一次)
Boolean sismember = jedis.sismember(userKey, uid);
if(sismember)
System.out.println("已经秒杀成功了,不能重复秒杀!");
jedis.close();
return false;
//6.判断如果商品数量,库存数量 小于1了,说明秒杀结束了
int i = Integer.parseInt(kuncun);
if(i<1)
System.out.println("秒杀已经结束了");
jedis.close();
return false;
//7. 秒杀过程
//开始一个队列(组或者事务,都可以理解)
Transaction multi = jedis.multi();
//组队操作
//7.1 库存要减1
multi.decr(kuncunKey);
//7.2 把秒杀成功用户添加到清单里面
multi.sadd(userKey, uid);
//开启事务后,就使用事务对应的方法!!来进行组队等等操作。就不单独来操作。
//jedis.decr(kuncunKey);
//jedis.sadd(userKey, uid);
//执行队列命令。exec()方法会给我们返回一个list集合结果内容。
List<Object> results = multi.exec();
if(results == null || results.size() == 0)
System.out.println("秒杀失败!!");
jedis.close();
return false;
System.out.println("秒杀成功了");
jedis.close();
return true;
3.5 库存的 遗留问题
乐观锁会造成库存遗留的问题!
解决办法,最先想到的是悲观锁,但是悲观锁在redis中,不行!!
redis中默认不能直接使用悲观锁(不支持悲观锁),只能使用乐观锁。
- 原因:Redis 作为缓存服务器使用时,以读操作为主,很少写操作,相应的操作被打断的几率较少。不采用悲观锁是为了防止降低性能。
用嵌入式脚本语言Lua来,来解决库存遗留问题。
3.6 LUA脚本在Redis中的优势
就是将复杂的或者多步的redis操作,写为一个LUA脚本,一次提交给redis执行。
下面就是一个LUA脚本(了解):
4. Redis 持久化之 RDB(Redis DataBase)
4.1 持久化
什么是持久化?
- 就是将数据写到硬盘中,能长时间存储!这个过程就叫做持久化。
Redis有两种不同形式的持久化方式:
- RDB(Redis DataBase),redis默认持久化方式。
- AOF(Append Of File)
4.2 RDB 持久化(备份)是如何进行的?
什么是RDB?
- 在指定的
时间间隔
内将内存中的数据集快照
写入磁盘,也就是我们将Snapshot快照,它恢复时是将快照文件在直接读到内存里。这种方式就叫做RDB。
RDB执行过程:
RDB创建fork,以及RDB的缺点(最后一次持久化后的数据可能丢失) 要牢记。
4.3 Fork
RDB有一个非常重要的底层原理叫做写时复制技术。
fork的介绍:
4.4 redis配置文件 配置RDB相关信息
首先,我们找到redis.conf中的SNAPSHOTTING,这一块就是配置快照相关的信息。
配置dbfilename:配置持久化后的文件名称。
配置dir:默认是再当前启动redis服务的命令下生成持久化文件。
配置stop-writes-on-bgsave-error:当redis无法写入磁盘的话,直接关掉Redis的写操作。推荐yes。
配置 rdbcompression:压缩文件,推荐yes,压缩么节约空间。
配置rdbchecksum:检查完整性,就是一个数据效验的功能。
配置save [秒钟] [写操作次数]:就是在多少秒内,有多少写操作,就进行持久化操作。
解释一下上面英文内容内容:
- 在1个小时内,有1个key发生了变化,就进行持久化操作同步。
- 在5分钟内,有100个key发生了变化,就进行持久化操作同步。
- 在60秒内,有10000个key发生了变化,就进行持久化操作同步。
对应的两个持久化的redis命令: save 和 bgsave 命令
(在加上一个lastsave命令。)
4.5 RDB 优劣比较
优点:周期性的进行持久化操作。
缺点:最后一次的快照信息可能丢失!
4.6 rdb的 备份和恢复
如果我们存储的dump.rdb出现了问题!我们就可以通过备份的文件来恢复。
下面的cp一份持久化的数据就是redis备份操作。将cp的数据改个成redis默认名字放到相同目录,重启redis服务就是redis恢复操作。
首先,我们cp一份dump.rdb数据,这样dump.rdb出现问题的时候,我们只需要将cp的备份数据,改个名字,放过来就好了。这就算是一个备份效果。
5. Redis 持久化之 AOF(Append Only File)
5.1 什么是AOF?
Append Only File的意思可以理解为仅仅向文件中追加信息。
AOF持久化的原理:
5.2 AOF的 开启
AOF持久化默认是不开启的。
配置AOF的相关信息,就在append only node模块下。
配置appendonly : 配置为yes表示开启AOF模式。
配置appendfilename:持久化后生成的文件名字。
当我们配置上面的信息,重启redis服务后,就开启了AOF持久化!
注意一个问题:
- 其实这个时间段AOF和RDB是同时开启的,我们并没有在配置文件关闭RDB之类的操作。
当AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)
当我们开启了AOF,进行一个写操作就往aof文件中追加一个写操作命令。
5.3 AOF 备份和恢复
AOF 备份和恢复的效果和rdb一样的:
AOF多了一个异常恢复的效果。
异常恢复:
- 通过/usr/local/bin/redis-check-aof --fix 可以将坏的aof文件恢复成为正常的。
例如:我们将appendonly.aof,随便改了改。
这样我们在进行redis-cli连接时,就会拒接连接。
然后,我们就可以通过rdis-check-aof --fix xxx.aof命令来修复aof文件了。
5.4AOF 同步频率设置
配置appendfsync:同步频率设置。
5.5 AOF rewrite重写操作(压缩)
rewrite重写 压缩操作:
说的简单一点就是 多个指令给他重写成为一个执行进行操作。
什么时候出发重写操作?
AOF 重写对应两个相关配置:
案例:
5.6 AOF执行流程 和 优缺点
AOF执行流程:
AOF的优点:
AOF的缺点:
6. Redis 主从复制
主机数据更新后根据配置和策越,自动同步到备份机的master/slaver机制。
其中,master以写为主,slave以读为主。
就像下面这张图,这就是主从复制,读写分离
。
主从复制的优点:
- 实现读写分离,性能扩展。
- 容灾快速恢复:就是当我们在
从数据库
进行读操作,如果这个从数据库
出现问题,宕机了,就快速切换到另一个从数据库
进行读操作。这个操作就叫做容灾快速恢复。
容灾快速恢复是用来解决从数据库
发生问题宕机后,切换另一个台从数据库
。那么如果主数据库
出现问题怎么办呢?
那么对应解决主数据库
如果出现问题宕机了该怎么办?就用到了Redis集群。
虽说,每个主从复制要求是一主多从
的效果,就是一台主数据库
,多台从数据库
。而Redis集群就把多个一主多从
进行了联系起来。当用户访问的这台主数据库
出现问题,就通过集群切换到另一个个一主多从的主数据库
中。
7. Redis 主从复制 搭建一主多从
这里实现一个一主两从的效果。
第一步:创建一个文件夹用来放置三个redis数据库的配置文件,持久化文件等等。这里我创建的是myredis文件夹。
第二步:复制cp我们的redis.conf配置文件到文件夹中,作为一个公共配置文件部分(配置文件可以去安装redis的目录下获取)。
第三步:配置一主两从,创建三个配置文件。
三个配置文件修改的信息分别是:
- include导入redis.conf的公共部分。
- 修改pidfile的路径和名称。
- 修改port端口
- 修改dbfilename持久化文件名称。
include的作用是引入公共部分。这里我们引入了之前copy的redis.conf配置文件。就是分别对三个redis数据库进行配置。
(一般我们命名就是在后面添加上对应的数字就行,我这里用的6379,6380,6381作为三个redis服务器,整好设置不同的端口。)
第四步:分别创建并配置好三个配置文件。
第五步:redis-server [对应的.conf文件] 启动三台redis服务器。并且使用slaveof命令来配置从服务器
。
如果我们想要连接其中一台redis服务器的话,就使用redis-cli -p [端口号] 就可以了。通过端口号来打开redis服务器的客户端。
我们可以使用info replication 命令来查看主从复制的相关信息。
REDIS04_主从复制概述及搭建反客为主薪火相传原理哨兵模式集群搭建