前记
1、NoSQL数据库2、Redis采用单线程+多路IO复用技术3、Redis的"两大维度,三大主线" 一、安装配置Redis二、常用五大数据类型
Key1、字符串(String)-- ArrayList2、列表(List)-- quickList3、集合(Set)-- Hash4、哈希(Hash)5、有序集合(Zset) 三、Redis配置文件详解
1、### NETWORK 网络相关配置###2、### SECURITY 安全###3、### GENERAL 通用###4、### LIMITS 限制### 四、Redis的发布与订阅
1、什么是发布与订阅2、流程3、命令行实现 五、Redis新数据类型
1、Bitmaps2、HypeLogLog3、Geospatial 六、Jedis操作
1、测试连接及jedis方法调用2、模拟验证码发送 七、Redis6与SpringBoot整合八、事务_锁机制_秒杀
1、Redis事务定义2、Muti,Exec,discard3、事务的错误处理4、事务冲突的问题
1、悲观锁(效率低)2、乐观锁3、WATCH key [key]4、UNWATCH5、Redis事务三大特性 5、秒杀
1、核心代码演示2、秒杀并发模拟3、连接超时问题4、超卖问题5、库存遗留问题 九、持久化之RDB(Redis Database)默认开启
优点缺点1、是什么2、备份是如何执行的3、Fork4、配置文件(### SNAPSHOT###)5、rdb备份与恢复 十、持久化之AOF(Append only File) 默认不开启
优点缺点1、是什么2、AOF持久化流程3、开启AOF(若RDB同时开启,Redis听AOF的)4、AOF启动/修复/恢复5、AOF同步频率6、Rewrite压缩 十一、RDB和AOF到底如何选择十二、主从复制
1、简介2、演示(一主两从)3、常用三招
1、一主二仆2、薪火相传3、反客为主 4、复制原理5、哨兵模式(sentinel) 十三、集群
前记 1、NoSQL数据库
1、概述
2、NoSQL适用场景
3、NoSQL不适用场景
2、Redis采用单线程+多路IO复用技术
与Memcache的三点不同:支持多数据类型,支持持久化,单线程+多路IO复用
1、多路复用指使用一个线程来检查多个文件描述符(Socket)的就绪状态,
比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就位,则返回,否则阻塞直到超时。
得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启用线程执行(比如使用线程池)、
就是说多路IO复用起到一个监视的效果,就绪后,Redis直接执行,不需要等待
2、串行 vs 采用多线程+锁(Memcached)vs 单线程+多路IO复用的比较
1、串行:阻塞IO,一件事一件事的做,在做当前事时,不能做其他事,有等待2、采用多线程+锁:非阻塞IO,一直重复做当前的事,没有等待3、单线程+多路IO复用:做某件事情需要一定的时间,可以监视这件事,我们可以做其他的事。多路IO复用有select,poll,epoll这些模式。select监测数量能力有限。poll监测数量没有限制,但是需要一个一个核查。配epoll监测数量没有限制,也不需要一个一个核查,直接看是否有一个正确的标识。
一、安装配置Redis
redis官网
1、下载最新版本tar.gz
2、安装C语言编译器gcc
yum install gcc查看gcc版本gcc --version
3、解压redis-tar.gz(解压到当前目录,本文的目录为/study,解压后为/study/redis-6.2.6)
tar -zxvf redis-6.2.6.tar.gz
4、编译为C文件(进入解压好的redis-6.2.6目录)
1、make安装bin启动器到/study/redis-6.2.6目录下2、make PREFIX=/study/redis-6.2.6 install
5、拷贝一份redis.conf到conf目录
cp /study/redis.6.2.6/redis.conf /study/redis.6.2.6/conf/redis.confvim /study/redis.6.2.6/conf/redis.conf开启后台运行daemonize no 改为 yes
6、以配置文件后台启动(进入/study/redis-6.2.6/bin目录),连接客户端
./redis-server ../conf/redis.conf ./redis-cli
7、关闭服务进程
客户端输入showdownps -ef|grep rediskill -9 pid
二、常用五大数据类型 Key
keys * 查看所有keyexists
1、字符串(String)-- ArrayList
1、简介
简单动态字符串(Simple Dynamic String SDS)
类似ArrayList,采用预分配冗余空间的方式减少内存的频繁分配
二进制安全,最大存储512M
2、数据结构
3、命令
1、设值
set
2、查询
get
3、数字值操作(原子操作:得益于redis单线程,不会被其他线程打断的)
incr k储存的数字值增 1incrby k ?储存的数字值增 ?decr k 储存的数字值减 1decrby k ?储存的数字值减 ?
2、列表(List)-- quickList
1、简介
单键多值
2、数据结构:
3、命令
例:从左插入v1 v2 v3,实际从左到右为v3 v2 v1
1、插吐值
lpush/rpush k v1 v2 v3从左/右插入v1,v2,v3lpop/rpop k从左/右吐出一个值,吐完值键消失rpoplpush k1 k2从k1右边吐一个值插到k2左边
2、查询(并非吐值)
lrange k s e按照索引下标范围获得元素s start, e endlindex k i按照索引下标获得元素llen l获取列表长度
3、插值
linsert k before "v1" "new v1"在v1后面插入newv1
演示
4、特殊删除
lrem k 2 "v" 删除k左边的两个"v"
演示
5、替换
lset k i v将k下标为i的值换为v
演示
1、简介
自动排重
String类型的无序集合
底层是一个value为null的hash表,增删查复杂度都为O(1)
一个算法,随着数据的增加,执行时间的长短,如果是O(1),数据增加,查找数据的时间不变
2、数据结构
是dict字典,字典使用哈希表实现的
Java中的HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。
Redis的set结构也一样,内部也使用Hash结构,所有的value指向同一个对象
3、命令
sadd k v1 v2 v3添加smembers k返回集合中所有元素srandmember k n随机从集合查询n个值sismember k v判断集合中是否有vscard k查询集合中元素个数srem k v1 v2删除集合中的某个元素spop k随机从集合k吐出某个值(吐出的值消失)smover k1 k2 v1把集合k1中的值v1移到k2sinter k1 k2返回两个集合的交集元素sunion k1 k2返回两个集合的并集元素sdiff k1 k2返回两个集合的差集元素(k1中的存在的,k2中不存在的)
4、哈希(Hash)
1、简介
Hash是一个String类型的field和value的映射表,类似于Map
2、数据结构
Hash类型对应数据结构为:ziplist(压缩列表),hashtable(哈希表),
当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable
3、命令
hset k f v给k中的f赋值vhsetnx k f v给k中的f赋值v,仅当f不存在时例 hset user:1001 id 1设置user1001的id为1hmest k f1 v1 f2 v2 给k中的多个f赋值vhexist k f 查看k中是否存在fhkeys k查看k中的所有fieldhvals k查看k中的所有valuehget k f从k集合f查询vhincrby k f n为k中的f的v加上增量n
5、有序集合(Zset) 1、简介
2、数据结构
跳跃表实现过程
3、命令
zadd k s v s v将一个或多个member元素及其score值加入有序集k中zrange k s e 返回k中下标在s-e中的元素zrange k s e withscores 返回k中下标在s-e中的元素并带score(按评分从小到大)zrevrange k s e withscores 返回k中下标在s-e中的元素并带score(按评分从大到小)zrangebyscore k min max 返回评分在min max的元素(包含min,max)从小到大zrevrangebyscore k max min返回评分在max min的元素(包含min,max)从大到小zincrby k
三、Redis配置文件详解 1、### NETWORK 网络相关配置###
1、bind
2、protected-mode
默认为yes,远程无法访问
修改为no,远程可以访问
3、Port
默认6379
4、tcp-backlog
5、timeout
1.requirepass
修改配置文件中的 requirepass 参数,
把前面 # 注释去掉,后面 foobared 改成自己想用的密码,
重启redis即可。
进入客户端输入
auth ***连接
3、### GENERAL 通用### 1.daemonize
2.pidfile
3.loglevel
4.logfile
5、database 16
数据库设置默认16个 0-15
4、### LIMITS 限制### 1.maxclients
2.maxmemory
3.maxmemory-policy
4.maxmemory-samples
四、Redis的发布与订阅 1、什么是发布与订阅 2、流程 3、命令行实现
1.打开一个客户端订阅channel1
subscribe channel1
2.打开另一个客户端,给channe11发布消息hello
publish channel1 hello
3.打开第一个客户端收到hello
五、Redis新数据类型 1、Bitmaps
1.简介
2、命令
汇总
1、setbit
1、设置bitmaps中某个偏移量的值
setbit
实例
2、getbit
getbit
3、bitcount(获取的数量是byte并非bit)
bitcount
4、bitop
bitop and(or/not/xor)
演示
Bitmaps与set的对比
1、简介
2、命令
1、pfadd
如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0
pfadd
2、pfcount
pfcount
3、pfmerge
pfmerge
3、Geospatial
1、简介
2、命令
1、geoadd
geoadd
2、geopos
geopos
3、geodist
geodist
4、georadius
georadius
六、Jedis操作 1、测试连接及jedis方法调用
1、pom文件
2、开放防火墙
systemctl status firewalld查看防火墙状态systemctl stop firewalld关闭防火墙
3、配置文件
1、bind127.0.0.1-》#bind127.0.0.1注释掉61行 2、protected-mode yes-》protected-mode no 关闭保护模式
4、重启redis
1、ps -ef|grep redis查看进程2、kill -9 pid杀掉进程
5、测试连接
public class JedisDemo01 { public static void main(String[] args) { Jedis jedis = new Jedis("192.168.88.129", 6379); String value = jedis.ping(); System.out.println(value); //输出PONG }}
6、测试String
public void demo1() { Jedis jedis = new Jedis("192.168.88.129", 6379); jedis.set("name", "jiali"); String name = jedis.get("name"); System.out.println(name); Set
2、模拟验证码发送
1、生成6位随机数
public static String getCode() { Random random = new Random(); String code = ""; for (int i = 0; i < 6; i++) { //取10之间的数字 int rand = random.nextInt(10); code += rand; } return code;}
2、每个手机每天只能发送三次,验证码放到redis中,设置过期时间
public static void sendCode(String phone) { Jedis jedis = new Jedis("192.168.88.129", 6379); jedis.auth("****"); //拼接key //手机发送次数key String countKey = phone + ":count"; //验证码key String codeKey = phone + ":code"; //每个手机每天只能发送三次 String count = jedis.get(countKey); if (count == null) { //没有发送次数,第一次发送 //设置发送次数是1 jedis.setex(countKey, 24 * 60 * 60, "1"); } else if (Integer.parseInt(count) <= 2) { //发送次数+1 jedis.incr(countKey); } else if (Integer.parseInt(count) > 2) { //发送三次,不能再发送 System.out.println("今天发送次数已经超过三次"); jedis.close(); //中断获取验证码后续操作 return; } //发送验证码放到redis String code = getCode(); jedis.setex(codeKey, 120, code); jedis.close();}
3、验证码校验
public static void verifyCode(String phone, String inputCode) { Jedis jedis = new Jedis("192.168.88.129", 6379); jedis.auth("****"); String codeKey = phone + ":code"; String redisCode = jedis.get(codeKey); if (redisCode.equals(inputCode)) { System.out.println("成功"); } else { System.out.println("失败"); }}
4、验证
1、sendCode("10086");2、进入redis-cli,查看keys *,查看验证码填入下面?3、verifyCode("10086","?");4、成功
七、Redis6与SpringBoot整合
1、pom
2、配置文件
spring: redis: host: 192.168.88.129 port: 6379 password: **** database: 0 #连接超时时间 timeout: 1800000 lettuce: pool: #最大连接数 max-active: 20 #最大阻塞等待时间(负数表示没限制) max-wait: -1 #最大最小空间连接 max-idle: 5 min-idle: 0
3、RedisConfig
package com.laptoy.redis.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CachingConfigurerSupport;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheConfiguration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializationContext;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;@EnableCaching@Configurationpublic class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate
4、测试Controller
@RestControllerpublic class RedisTestController { @Autowired RedisTemplate redisTemplate; @GetMapping("/test") public String redisTest() { redisTemplate.opsForValue().set("name", "jiali"); String name = (String) redisTemplate.opsForValue().get("name"); return name; }}
八、事务_锁机制_秒杀
1、Redis事务定义 2、Muti,Exec,discard 3、事务的错误处理
1、组队中某个命令出现报告错误,执行时整个的所有队列都会被回滚
2、执行阶段某个命令报错,则只有报错的不会执行,其他正常执行
4、事务冲突的问题
例子:同一个账户有10000块,三个人登录同一个账户同一时刻购买东西
客户端1
客户端2
被watch的客户端的key成功执行事务后,自动取消watch状态
1.客户端添加秒杀商品 iphone 10件
set sk:iphone:kc 10
2.获取随机用户id
public static String getUid() { Random random = new Random(); String uid = ""; int rand = random.nextInt(20); uid += rand; return uid;}
3.秒杀过程
public static void doSecKill(String uid, String pid) { if (uid == null || pid == null) { System.out.println("用户名或商品名不能为空"); } Jedis jedis = new Jedis("192.168.88.129", 6379); jedis.auth("****"); //1、库存key String kcKey = "sk:" + pid + ":kc"; //2、用户key String userKey = "sk:" + pid + ":user"; //3、判断秒杀是否开始 String kc = jedis.get(kcKey); if (kc == null) { System.out.println("秒杀未开始"); jedis.close(); //中止 return; } //4、判断用户是否重复秒杀 if (jedis.sismember(userKey, uid)) { System.out.println("您已经秒杀成功,无法重复参与"); jedis.close(); //中止 return; } //5、判断库存数量 if (Integer.parseInt(kc) <= 0) { System.out.println("秒杀已结束"); jedis.close(); //中止 return; } //6、秒杀过程 //6.1、库存-1 jedis.decr(kcKey); //6.2、把秒杀用户添加到清单里 jedis.sadd(userKey, uid); System.out.println("秒杀成功"); jedis.close();}
4.测试
public static void main(String[] args) { for (int i = 0; i <20 ; i++) { doSecKill(getUid(), "iphone"); }}
2、秒杀并发模拟
自行模拟tomcat测试,此处省略
1.使用工具ab模拟测试
centos6默认安装,centos7需要手动安装
2.安装ab
yum install httpd-tools
3.命令
ab --helpab http://....-n 请求数量-c 请求并发量-p post请求时提交参数-T 指定参数类型post/put
3、连接超时问题
1.使用数据库连接池解决–单例模式懒加载,双重校验锁
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); //测试连接状态 ping pong poolConfig.setTestOnBorrow(true); jedisPool = new JedisPool(poolConfig, "192.168.88.129", 6379, 60000, "****"); } } } return jedisPool; } public static void release(JedisPool jedisPool, Jedis jedis) { if (null != jedis) { jedisPool.returnResource(jedis); } }}
2.测试连接
JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();Jedis jedis = jedisPoolInstance.getResource();
4、超卖问题 1.乐观锁解决
代码演示
1.注释秒杀过程
//6、秒杀过程//6.1、库存-1//jedis.decr(kcKey);//6.2、把秒杀用户添加到清单里//jedis.sadd(userKey, uid);
2.加了乐观锁
jedis.watch(kcKey);Transaction multi = jedis.multi();multi.decr(kcKey);multi.sadd(userKey, uid);List