Jedis操作Redis6
即用java实现在linux命令行中的操作
引入依赖
测试代码
禁用Linux的防火墙:Linux(CentOS7)里执行命令
systemctl stop/disable firewalld.service
redis.conf中注释掉bind 127.0.0.1 ,然后 protected-mode no
总而言之:注意一是否修改配置注意二是否关闭防火墙
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.44.168",6379);
//ip地址和端口号创建Jedis对象
//连接成功就返回值,不成功就报错
String value = jedis.ping();
System.out.println("连接成功:"+pong);
jedis.**set**("k1", "v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
Set<String> keys = jedis.**keys**("*");
System.out.println(keys.size());
for (String key : keys) {
System.out.println(key);
}
System.out.println(jedis.**exists**("k1"));
System.out.println(jedis.**ttl**("k1"));
System.out.println(jedis.**get**("k1"));
jedis.close();
}
实例:完成一个手机验证码功能
要求:
1、输入手机号,点击发送后随机生成6位数字码,2分钟有效
2、输入验证码,点击验证,返回成功或失败
3、每个手机号每天只能输入3次
/***********
** 1.生成一个随机的验证码
** Random
** 2.验证码两分钟内有效
** 设置过期时间
** 3.验证码是否正确
** redis获取验证码和输入的比较
** 4.每个手机只能尝试三次
** incr+1,大于2不能提交
**********/
package com.atguigu.jedis;
public class PhoneCode {
public static void main(String[] args) {
//模拟发送验证码,发送三次后会失败的
verifyCode("13678765435");
getRedisCode("13678765435","982422");
}
//3.校验
public static void getRedisCode(String phone, String code) {
Jedis jedis = new Jedis("192.168.44.168",6379);
String codeKey = "VerifyCode" + phone + ":code";
String redisCode = jedis.get(codeKey);
//判断
if(code.equals(redisCode)) {
System.out.println("成功");
} else {
System.out.println("失败");
}
jedis.close();
}
//2.验证
public static void verifyCode(String phone) {
//连接redis
Jedis jedis = new Jedis("192.168.44.168",6379);
//拼接key
//手机发送次数
String countKey = "VerifyCode" + phone + ":count";
//验证码key
String codeKey = "VerifyCode" + phone + ":code";
//只发送三次
String count = jedis.get(countKey);
if(count == null) {
//未发送过,设置发送次数为1
jedis.setex(countKey, 24*60*60, "1");
} else if (Integer.parseInt(count) <= 2) {
jedis.incr(countKey);
} else if (Integer.parseInt(count) > 2) {
System.out.println("今天已经发送三次了");
jedis.close();
return;
}
//验证码要放到redis中
String vcode = getCode();
jedis.setex(codeKey, 120, vcode);
jedis.close();
}
//1.生成验证码
public static String getCode() {
Random random = new Random();
//建议直接使用StringBuffer
String code ="";
for(int i = 0; i < 6; i++) {
int rand = random.nextInt(10);
code += rand;
}
return code;
}
}
整合
SpringBoot,一个Spring的脚手架、简化工具。
引入依赖
1、 在pom.xml文件中引入redis相关依赖
配置
2、application.properties配置redis配置
类
3、 添加redis配置类 固定写法
测试
4、RedisTestController中添加测试方法
事物操作
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
multi
exec
discard
watch
unwatch
秒杀案例
使用工具ab模拟测试:CentOS6 默认安装;CentOS7 需要手动安装。
public static boolean doSecKill(String uid, String prodid) throws IOException {
//1.uid和prodid非空判断
if(uid == null || prodid == null) {
return false;
}
//2.连接redis
Jedis jedis = new Jedis("192.168.44.168", 6379);
//3.拼接key
//3.1库存key
String keKey = "sk:"+prodid+":qt";
//3.2描述成功用户id
String userKey = "sk:"+uid+":user";
//4.获取库存,若null则秒杀未开始需等待
String kc = jedis.get(kcKey);
if(kc == null) {
System.out.println("秒杀还未开始");
jedis.close();
return false;
}
//5.开始秒杀判断用户是否重复下单
if(jedis.sismember(userKey,uid)) {
System.out.println("已秒杀成功,不能重复秒杀");
jedis.close();
return false;
}
//6.判断如果商品数量,库存数量小于1,表示秒杀结束
if(Integer.parseInt(kc) <= 0) {
System.out.println("秒杀已结束");
jedis.close();
return false;
}
//7.秒杀过程
//7.1库存-1
jedis.decr(kcKey);
//7.2把秒杀成功用户添加到清单中
jedis.sadd(userKey,uid);
System.out.println("秒杀成功");
jedis.close();
return true;
}
flushdb
set sk:0101:qt 10
keys *
(返回"sk:0101:user"
keys *
(返回"sk:0101:user"
"sk:0101:qt"
get sk:0101:qt
(返回“9”
smembers sk:0101:user
(返回“5421”
//修改为连接池
JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = jedisPoolInstance.getResource();
超卖问题
乐观锁
public static boolean doSecKill(String uid, String prodid) throws IOException {
//1.uid和prodid非空判断
if(uid == null || prodid == null) {
return false;
}
//2.连接redis
//Jedis jedis = new Jedis("192.168.44.168", 6379);
//修改为连接池
JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = jedisPoolInstance.getResource();
//3.拼接key
//3.1库存key
String keKey = "sk:"+prodid+":qt";
//3.2描述成功用户id
String userKey = "sk:"+uid+":user";
//监视库存
jedis.watch(kcKey);
//4.获取库存,若null则秒杀未开始需等待
String kc = jedis.get(kcKey);
if(kc == null) {
System.out.println("秒杀还未开始");
jedis.close();
return false;
}
//5.开始秒杀判断用户是否重复下单
if(jedis.sismember(userKey,uid)) {
System.out.println("已秒杀成功,不能重复秒杀");
jedis.close();
return false;
}
//6.判断如果商品数量,库存数量小于1,表示秒杀结束
if(Integer.parseInt(kc) <= 0) {
System.out.println("秒杀已结束");
jedis.close();
return false;
}
//7.秒杀过程
Transaction multi = jedis.multi();
//组队操作
multi.decr(kcKey);
multi.sadd(userKey,uid);
//执行
List<Obeject> results = multi.ext();
if(results == null || results.size() == 0) {
System.out.println("秒杀失败");
jedis.close();
return false;
}
//7.1库存-1
//jedis.decr(kcKey);
//7.2把秒杀成功用户添加到清单中
//jedis.sadd(userKey,uid);
System.out.println("秒杀成功");
jedis.close();
return true;
}
库存遗留
乐观锁修改版本号的问题
但是redis不能直接使用悲观锁,需要LUA脚本,但LUA只支持redis2.6以上的版本。
持久化
RDB
fork写时复制技术
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。
save bgsave
save :save时只管保存,其它不管,全部阻塞。手动保存。不建议。
bgsave:Redis会在后台异步进行快照操作, 快照同时还可以响应客户端请求。
rdb的备份
先通过config get dir 查询rdb文件的目录
将*.rdb的文件拷贝到别的地方
rdb的恢复
cp dump.rdb d.rdb
ll
# 关闭Redis
ps -ef | grep redis
kill -9 3524
# 先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb
rm -f dump.rdb
mv d.rdb dump.rdb
# 启动Redis, 备份数据会直接加载
redis-server /etc/redis.conf
redis-cli
key *
优势
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高更适合使用
- 节省磁盘空间
- 恢复速度快
劣势
- Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑
- 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
AOF
appendfsync always #每次都记入,始终同步,但性能差
appendfsync everysec #每秒都记入,但是本秒可能丢失
appendfsync no #redis不主动,交给操作系统
优点
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
缺点
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复不能。
官方推荐两个都启用。如果对数据不敏感,可以选单独用RDB,不建议单独用 AOF,因为可能会出现Bug,如果只是做纯内存缓存,可以都不用。
主从复制
读写分离、主写从读;容灾快速恢复
vi redis6379.conf
# 拷贝多个redis.conf文件include(写绝对路径)
include /myredis/redis.conf
# 开启daemonize yes
# Pid文件名字pidfile
pidfile /var/run/redis_6379.pid
# 指定端口port
port 6379
# Log文件名字
# dump.rdb名字dbfilename
dpfilename dump6379.rdb
# Appendonly 关掉或者换名字
#6379 6380 6381
ls
# 启动redis
redis-server /redis6379
redis-server /redis6380
redis-server /redis6381
# 查看进程
ps -ef | grep redis
# 打印主从复制的相关信息
info replication
# 在从机上配置主机
slaveof <主机ip><主机port>
一主两仆
主机挂掉,重启就行,一切如初
从机重启需重设:slaveof 127.0.0.1 6379,可以将配置增加到文件中。永久生效。
从机只能读,主机可写
薪火相传
反客为主
当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改。
slaveof no one
哨兵模式
redis-sentinel setinel.conf
优先级,偏移量(同步度最高),runid
java判别主从
private static JedisSentinelPool jedisSentinelPool=null;
public static Jedis getJedisFromSentinel(){
if(jedisSentinelPool==null){
Set<String> sentinelSet=new HashSet<>();
sentinelSet.add("192.168.11.103:26379");
JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10); //最大可用连接数
jedisPoolConfig.setMaxIdle(5); //最大闲置连接数
jedisPoolConfig.setMinIdle(5); //最小闲置连接数
jedisPoolConfig.setBlockWhenExhausted(true); //连接耗尽是否等待
jedisPoolConfig.setMaxWaitMillis(2000); //等待时间
jedisPoolConfig.setTestOnBorrow(true); //取连接的时候进行一下测试 ping pong
jedisSentinelPool=new JedisSentinelPool("mymaster",sentinelSet,jedisPoolConfig);
return jedisSentinelPool.getResource();
}else{
return jedisSentinelPool.getResource();
}
}