redis是一个高性能的通过C语言开发的key-value数据库,是为了解决高并发,高扩展的NOSQL(非关系)类型数据库,支持的数据类型包括,String,list,hash,set和Zset。 redis把数据都存储在内存中以提高读取效率,周期性(如 y=sinx T= 2Π)的进行数据写入磁盘。
NoSQL:是对关系型数据库做补充,且离不开关系型数据库。 是用于解决海量用户和高并发存在的问题,好处有:1.可以减少磁盘I/O的次数 2.可以只存储数据,而不存储数据之间的关系。
redis特征:
1.数据之间没有相互关系
2.内部采用单线程机制进行工作
3.高性能
4.可以支持多种数据类型。
5.持久化支持,比如断电之后虽然在内存中,但是可以进行数据恢复。
redis应用:为热点数据进行加速查询,如微博突然出现了一个热点事件,秒杀买东西,直播,验证码控制等等。
1.启动redis(采用3.2.100)
出现相关界面
port是redis的端口号,如果想要启动多个redis就需要修改端口号,pid是这个redis的名称,每次启动都会有不同的redis实例化对象
然后启动redis客户端对redis进行操作,默认主机是localhost(127.0.0.1)
2.redis的基本操作
redis操作采用命令行形式:
分为功能性,清屏性,帮助查询命令,退出指令
1.功能性
添加信息命令:set key value 如:set name liming
查询信息命令: get key 如:get name(key不存在就会返回nil)
2.清除屏幕信息指令:
clear
3.帮助查询信息指令:用于查看一个命令或者群组的具体使用方式
help 命令名称:如 help get
第一行是:使用命令的格式
第二行是:命令的具体说明
第三行是:版本号
第四行是:所属群组
help @群组 :如 help @string,help @list…
4.退出命令
quit,exit,esc键
(当value是数字时,也是字符串类型,但是可以进行加减乘除操作)
1.基本操作
***添加/修改:
单个数据:set key value
多个数据:mset key value key value .......
获取:
单个数据:get key
多个数据:mget key key key....
删除:
单个数据:del key,删成功后会返回删除记录的条数
多个数据:del key key key......,删成功后会返回删除记录的条数
获取字符串长度:
strlen key
追加信息到原始信息后部:
append key value 如果不存在这个key,就会新建
mset与set的比较
如果需要存放10条数据,set所花费的时间为 发送*10+响应*10+处理*10;
mset所花费的时间为:发送*1+响应*10+处理*1,明显快于set。
但是,当数据有1亿条或者非常大时,就需要对这1亿条数据进行合理分割,因为redis是单线程的,会在请求,处理,响应的时候发现堵塞的情况,就会影响处理的效率和时间。
当mysql表中字段数据太多时,就需要进行分表操作,但是不同表的主键ID是自增的,这就导致主键ID可能会重复,交给redis管理就可以解决这些问题
设置数值数据增加指定返回的值(只能对数值进行处理)
incr key 对key的value进行加一
incrby key increment 对key增加指定的值 如:incryby age 10
incrbyfloat key increment 对key增加指定的小数值 如:incryby age 10.5
设置数值数据减少指定返回的值
decr key 对key的value进行减一
decrby key increment 对key减少指定的值 如:decryby age 10
当需要对某种操作进行限制的时候,如给一个明星投票,只限制小时投一次,4小时以后才能继续投,需要限制投票操作就可以用redis
给数据指定生命周期
setex key seconds value 给key的数据value设置以秒为单位的生命周期
psetex key milliseconds value 给key的数据value设置以毫秒为单位的生命周期
业务场景的具体使用
如在微博中每个用户都在查看一个明星的关注数 粉丝数,如何提高查询和访问效率
解决办法,在redis中设置明星的用户信息,把id和字段属性作为key,数据作为value后台及时刷新即可。
key | value |
---|---|
user: id:111:fans | 666666 |
user: id:111:focus | 2222 |
key----------value(filed-value,
filed-value,
filed-value,
..............)
存储格式也是key-value,但是这个value我们可以把它当作存储了一个 实体类对象,如 存储一个user对象,key可以时user的主键,value里面有name password,age,sex等属性。
1.基本操作
添加/修改数据
单个操作:hset key filed value 给key中添加一个名为filed的value数据
多个操作:hmset key filed value filed value.....
根据是否有值来存入数据
hsetnx key fileld value 如果当前的filed中没有值就把value存入,如果有值就什么都不做
获取数据
单个操作:hget key filed 获取key中,名称为filed的数据
多个操作:
hmget key filed filed .....
hgetall key 获取key中全部的数据 ,如果fileld过多,此方法效率非常低。
删除数据
hdel key filed filed filed ....... 删除key中,名为field的数据
获取哈希表中字段的个数
hlen key
查找哈希表中是否存在指定的字段
hexists key filed
获取hash表中所有的字段名和字段值
hkeys key
hvals key
给指定的数值数据增加指定的值
hincrby key fileld increment 给名为key的hash表中字段名为fileld的字段属性增加一个increment
如:hincrby user age 10
hincrbyfloat key fileld increment
hash类型应用场景
淘宝网站购物车的实现
一 用于存放用户的购买信息
1.一个用户对应一个购物车,把用户id作为key
2.一个购物车里有多个物品,把物品编号作为fileld
3.一个物品可以购买多个,把物品数量作为value
二用户存放商品的具体信息
1.某一类商品对应一个hash,把商品类型id作为key
2.把商品编号作为fileld
3.把商品的具体信息用json字符串存入value中。
对应操作需要的命令
添加 hset hmset
查看 hget hmget hgetall hlen hexists hkeys hvals
修改 hset incrby incrbyfloat
删除/清空 hdel
list底层采用双向链表进行构造,可以存储多个数据,并且可以体现数据 ***进入存储空间的顺序***
1.基本操作
***添加/修改数据***
lpush key value value value..... 给名为key的list从 ***左边*** 添加一个或多个数据
rpush key value value value...给名为key的list从***右边***修改一个或多个数据
获取数据
lrange key start stop 从左边开始获取key中从start到stop的value
lindex key index
llen key
获取并且移除数据
lpop key 从左边第一个位置移除一个value并获取
rpop key 从右边第一个位置移除一个value并获取
规定时间内获取多个lisit表中的一个数据并且移除数据
blpop key key.......timeout 在timeout的时间内,若干个list表中的其中一个出现了数据就直接移除,如果同时出现,就移除排在在前面的list表,如果超时就返回空。
brpop key key......timeout
移除指定的数据
lrem key count value 从key中移除count个数据value的值
如 lrem age 3 50 从 age中移除3个数据为50的值
业务场景
有顺序又有多个数据。
如朋友圈点赞和评论
点赞队列就是一个list,里面的点赞人名称就是value。
评论队列也是一个list,里面的评论内容就是value。
set要求有1.存储大量数据
2.提供更高效的查询效率,比list双向链表查询快。
set模型
set模型采用hash的模型,但是把hash中fileld用来存储数据,value变成空。
1.基本操作
添加数据
sadd key member member.... 给名为key的set表中添加一个或者多个member数据
获取全部数据
smembers key
删除数据
srem key member member...
获取set的数据总量.
scard key 获取名为key的set表中一共有的数据条数。
判断set集合中是否存在指定的数据
sismember key member 如果有就返回1,没有就返回0
随机获取set集合中的指定数量的数据
srandmember key count
随机获取set集合中的指定数据,并且移除
spop key count
业务场景:随机推送类的信息检索,如新闻,歌曲,旅游路线
如你最开始玩某一个application的时候,会让你选择兴趣爱好,但是后期会随机推送其他的热点set集合到你的set集合中,然后一起展现给你,如果你喜欢就存入你的set,不喜欢就不存入。
求多个集合的交,并,差集
sinter key1 key2 .....
sunion key1 key2....
sduff key1 key2.....
求多个集合的交,并,差集并且存储到指定的集合当中
sinterstore destination key1 key2.... destination是值指定的集合中,
如 sinterstore u3 u1 u2,就是把u1和u2中都有的数据存入u3中
sunionstore destination key1 key2...
sduffstore destination key1 key2....
将指定数据从原来的集合中移动到另一个集合
smove source destination member
业务场景
QQ 微信需要推送你可能认识的朋友的时候就可用到这些操作。如可以从好友A出发,获取到A的好友B的好友信息列表。
sort set 是在set的基础上添加了可排序字段score。
1.基本操作
添加数据
zadd key score1 member1 score2 member2 ...... score用于指定排序顺序,member用于存放数据
获取数据
zrange key start stop {withscores} 输出start到stop的值,如果有wtihscores机会输出里面的score
如:zrange name 0 -1 输出全部的值(member)
zrange name 0 -1 输出全部的值(member)和用于指定排序的数值(score)
zrevrange key start stop {withscores} 倒序排序。
删除数据
zrem key member
按条件获取数据
查询score字段范围在srart-stop的所有memeber值
如果添加withscores,也查询出排序字段的score值,
如果添加limit,表示从min~max中查询出指定返回的数据记录。
zrangebyscore key min max {withscores} {limit}
如:zrangebyscore price 20 80 limit 0 3
zrevrangebyscore key min max {withscores} {limit}
按条件删除数据
删除索引从start~stop的所有memeber值
zremrangebyrank key start stop
删除score字段范围在min-max的所有memeber值
zremrangebyscore key min max
注意:
min、max用于限定搜索查询的条件 min
获取集合数据总量:
zcard key
zcount key min max 查看score里min到max的数据一共有几个
交集、并集操作
交集:zinterstore destination numkeys key key ... numkeys表示后面需要提供的集合个数
如 zinterstore name 3 zhangsan wangwu lisi.
并集:zunionstore destination numkeys key key....
业务场景
对资源进行排序,如选出音乐当作最受欢迎的10首歌曲,并对他们进行从小到大排序,还有QQ会员,音乐会员存在的时长。
获取数据对应的索引
zrank key member
zrevrank key member
获取数据对应的score值
zscore key member
给socre值增加指定的数据
zincrby key increment member 给一个sort set中的meber的socre值增加increment
如 zincryby name 1 zhangsan
key作用:表示数据存储的名称
删除指定key
del key
判断key是否存在
exists key
获取key的类型
ytpe key
为key设置生命周期
expire key seconds
pexpire key milliseconds
//以下两个一般在Linix中使用
expireat key timestamp
pexpireat key milliseconds-timestamp
获取key的有效时间
ttl key 不存在返回-2,永久存在返回-1,设置了有效期返回时长
pttl key
把key的生命周期变为永久性
persist key 操作成功返回1,操作失败返回0
查询key
keys pattern
keys * 查询所有的key,返回的是一个list列表
key的重命名
rename key newkey 当newkey存在时,newkey的数据会被覆盖掉
renamex key newkey 当存在newkey时,会操作失败返回0
对所有的key排序
sort 只能对list,set,sort set进行排序
其他通用的key操作
help @generic
redis为每个服务提供有16个数据库,分别是0-15,每个数据库之间相互独立,占用一块内存空间
不危险的操作
select index 用于切换数据库
ping 用于测试服务器是否连通
move key db 把数据移动到另一个数据库db
dbsize 查看当前库有多少key
数据清除操作(当你想删库跑路的时候)
flushdb 删除当前数据库的数据
flushall 删除所有数据库的所有数据
什么是jedis:jedis是JAVA程序操作redis的一个工具
如果项目采用springboot则也可以用此依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
maven项目中的jedis依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
入门程序
1.连接redis(创建一个jedis对象)
2.操作redis(方法名与命令一模一样)
3.关闭redis
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.List;
import java.util.Map;
public class jedisTest {
@Test
public void TestString(){
//1.连接redis
Jedis jedis = new Jedis("127.0.0.1",6379);
//2.操作redis,操作方法名和redis命令一模一样
jedis.set("name","zxh");
String name = jedis.get("name");
System.out.println(name);
jedis.lpush("list","name","age");
List<String> list = jedis.lrange("list", 0, -1);
for (String s: list)
{
System.out.println(s);
}
//3.关闭redis
jedis.close();
}
@Test
public void TestList(){
//1.连接redis
Jedis jedis = new Jedis("127.0.0.1",6379);
//2.操作redis,操作方法名和redis命令一模一样
jedis.lpush("list","name","age");
List<String> list = jedis.lrange("list", 0, -1);
for (String s: list)
{
System.out.println(s);
}
System.out.println(jedis.llen("list"));
//3.关闭redis
jedis.close();
}
@Test
public void TestHash(){
//1.连接redis
Jedis jedis = new Jedis("127.0.0.1",6379);
//2.操作redis,操作方法名和redis命令一模一样
jedis.hset("user","name","张三");
jedis.hset("user","age","18");
jedis.hset("user","price","50");
Map<String, String> user = jedis.hgetAll("user");
for (String u : user.keySet())
{
System.out.println(user.get(u));
}
System.out.println(jedis.hlen("user"));
//3.关闭redis
jedis.close();
}
}
Jedis连接池的创建和使用
连接池对象:JedisPool
JedisPool构造方法中参数:
poolConfig:连接池配置对象(用于配置连接池的属性)
host:redis服务地址
port:redis服务端口号
具体配置如下:
package com.zxh.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisUtil {
public static Jedis getJdis(){
//配置连接池配置对象的属性
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//配置最大连接数(连接池中允许存在的jedis对象个数)
jedisPoolConfig.setMaxTotal(30);
//配置活动连接数(最多允许10个jedis对象一起运行)
jedisPoolConfig.setMaxIdle(10);
//服务地址
String host = "127.0.0.1";
//端口号
int port=6379;
//获取jedis连接池,并获得连接池对象
JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port);
//返回 从连接池获取的jedis对象
return jedisPool.getResource();
}
public static void main(String[] args) {
Jedis jedis = JedisUtil.getJdis();
System.out.println(jedis);
}
}
改进后的jedis工具优点:
jedis连接池会在初始化的适合就执行,并且只执行一次,不用每次调用都执行,提高了效率
package com.zxh.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisUtil {
public static JedisPool jedisPool = null;
static {
//配置连接池配置对象的属性
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//配置最大连接数(连接池中允许存在的jedis对象个数)
jedisPoolConfig.setMaxTotal(30);
//配置活动连接数(最多允许10个jedis对象一起运行)
jedisPoolConfig.setMaxIdle(10);
//服务地址
String host = "127.0.0.1";
//端口号
int port=6379;
//获取jedis连接池,并获得连接池对象
jedisPool = new JedisPool(jedisPoolConfig,host,port);
}
public static Jedis getJdis(){
//返回 从连接池获取的jedis对象
return jedisPool.getResource();
}
}
Redis可视化工具的使用
工具名称:Redis Desktop Manager
如果你redis命令用得很熟练就可以用这个,新手建议不用,因为在进行操作时可以练习基本的命令。
一:redis的安装和启动
采用虚拟机:VMware Workstation
操作系统:centOS7 64位
首先下载基本命令和编译环境
yum install -y wget
yum install -y gcc c++ gcc-c++ make
因为redis安装时需要编译,所有需要C的编译环境
1.下载redis
wget http://download.redis.io/releases/redis-4.0.0.tar.gz
2.解压redis
tar -xvf redis-4.0.0.tar.gz
3.进入redis目录
cd redis-4.0.0
4.安装redis,默认在src下
make install
如果失败就输入 make MALLOC=libc
5.然后查看安装的情况
cd src
ll
6.启动redis
redis -server
7.克隆虚拟机
在src目录下,输入 redis-cli ,启动客户端
二.Redis持久化
什么是持久化
持久化就是指:文件可以长久的存在,断电也不丢失,把数据保存在硬板中。
可以防止数据丢失,保证数据安全性。
redis的持久化方式
1.数据快照,每隔一段时间就把数据存入到硬盘中,存储格式为二进制。 RDB
2.操作过程(日志),记录在操作数据时的操作过程。AOF
RDB(数据快照)
命令
save:作用是手动执行一次保存操作
会把相应的持久化数据存储在 dump.rdb的数据库中。
dump.rdb的所在路径 ,由redis.conf文件中的 dir 属性设置。
save指令生成的文件相关配置
在redis.conf中设置
1.dbfilename dump.rdb
用途:设置本地数据库名称,默认是dump.rdb
通常设置为 dump.端口号。rdb
2.dir
用途:设置存储 .rdb 文件的存储路径
通常把目录名称设置为data
3.rdbcompression yes
用途:设置把数据存储到本地数据库时是否压缩
通常默认为yes,设置为no可以减少cpu运行时间,但空间变大
4.rdbchecksum yes
用途:设置是否进行RDB文件格式校验,在读或者写RDB文件时都会校验
通常为yes状态,设置为可以提高10%的读写速度,但文件可能会损坏
服务器意外关闭时,会把数据存入到dump.rdb的本地数据库中。当你再次打开服务器时,数据会自动从dump.rdb加载到内存中,这样就可以进行数据操作了,说明RDB方式保证了数据的不丢失。
注意: save指令可能导致服务器长时间堵塞,线上环境不建议使用
交给后台执行保存数据指令
bgsave
用途:启动后台保存数据操作,不是立即执行,不会去占用redis的单线程。
bgsave的工作原理
当执行bgsave指令时,会发送指令给redis,
然后返回一个消息background saving startd ,
当在返回这个消息时,会去调用fork函数生成子进程,
子进程执行bgsave命令去创建rdb文件,而不占用redis的进程去创建文件
bgsave的配置
stop-writes-on-bgsave-error yes
用途:如果后台存储出现错误,是否停止保存操作
默认为开启状态。
命令
save second changes 后台也用的bgsave的指令
用途:只要在限定时间范围内,key的变化数量到达了指定的数量,就进行自动持久化
second 指定限定的时间范围
changes 指定key的变化数量
书写位置
在 redis.conf 中进行配置
例如:设置 为 save 10 2 ,在10秒内,如果有两条数据改变,就保存到本地数据库中
操作结果 第一次 10秒内只有一条数据改变,没有dump-6379.rdb数据库,第二次10秒内两天则有了dump.6379.rdb数据库
自动配置持久化的工作原理
首先用户端发送操作set del 等命令时,redis会知道这些命令是否让数据真正的发生了变化,如果数据真正的发生了变化,redis就会记录变化的数据数量,如果在规定的时间内达到了规定的变化数量。redis就会去进行数据持久化的工作。
主流方式进行redis持久化
AOF(记录操作过程) 日志防止
AOF可以解决RDB的存在的问题
1.RDB每次都是读写全部的数据,当数据量大时,效率非常的低
2.bgsave会创建新的子进程,会产生额外的内存消耗
3.不能实时的持久化,只能间隔的持久化,容易带来数据丢失风险。
解决思路
把记录数据变成记录操作过程,就大大降低了数据量。对所有操作均进行记录,就可以排除数据丢失的风险,也不用创建子进程
。
以独立日志的方式,记录每次的写命令,当服务器启动时会执行AOF文件中的命令来恢复数据。
主要是解决了RDB不能实时持久化的问题,目前是redis持久化的主流方式
AOF提供的记录操作过程的三种策略
建议使用 everysec 策略
1.always 每次
每次操作记录都同步到AOF文件中 ,数据无误差,但性能很低
2.everysec 每秒
每秒记录操作,然后同步到AOF文件中
数据准确性较高,性能较高,最多丢失一秒数据
3 no ,系统来控制
由操作系统控制每次同步到AOF的周期,整体不可控
AOF功能的开启和配置
在redis.conf中设置
1.appendonly yes|no
用途:配置是否开启AOF功能
2.appendfsync always|everysec|no
用途:说明AOF的数据保存策略
配置结果如图
配置之后启动redistribution服务,在存储数据文件的地方会有相关的aof文件
当我们随便set一个数据后,可以发现,相关操作数据已经被持久化到aof文件中了。
其他相关配置
appendfilename filename
用途:配置aof的文件名,一般是appendonly-端口号.aof
dir
用途:配置aof文件的保存路径。
AOF重写
AOF重写是为了减少AOF文件大小而产生的,简单来说就是——————假如同一个数据有若干条执行命令,不使用重写这若干条命令的操作都被会记录在AOF中,AOF重写可以只记录数据最终操作结果的命令,忽略掉前面的若干条命令。
AOF重写的作用和好处
减少了AOF文件的大小
提高了持久化效率,降低了持久化时间和IO性能
降低数据恢复的时间,提高了数据恢复的效率。
AOF重写的规则
1.进程内超时的文件不再写入
2.忽略无效指令,
如del key,因为最终数据没有del
set name zxh,set name zzz,就可以忽略掉第一个set,因为最终的数据是第二个set
3.对同一数据的多条写命令合并为一条
如 三条incr num,incr num,incr num 就合并成一条 incr num 3
AOF重写配置
手动重写
bgrewriteaof
在redis.conf中设置。
自动重写促发条件设置
auto-aof-rewrite-min-size size
auto-aof-rewrite-peroentage percentage
自动重写触发对比参数
aof_current_size 表示当前的数据大小
aof_base_size
自动重写触发条件
aof_current_size>auto-aof-rewrite-min-size
如果当前大小大于设定的值,进行重写
aof_current_size - aof_base_size
—————————————————————————————————— > auto-aof-rewrite-peroentage
aof_base_size
如果当前百分比大于设定的值,就进行重写
开启手动重写
到AOF文件中查看持久化结果,可以发现AOF重写了,只记录了最后一次set name 789 的操作
redis事务就是让一个命令执行队列连续的一次性执行,中间不会被打断或者被干扰。
事务的基本操作
开启事务
multi 跟mysql的began或者start transaction一样
用途:设定事务开启的位置,后续的所有命令都将会加入到事务中
执行事务
exec 跟mysql的commit一样
用途:声明事务结束位置,并执行事务,与multi成对使用
取消事务
discard 跟mysql的rollback一样
用途:声明事务结束位置,并取消事务的执行。
事务执行流程
if(如果是事务状态)
{
if(如果是事务命令)
{
if(命令==exec)
{
执行事务队列并且销毁
}
if(命令==discard)
{
退出事务队列并且销毁
}
}
if(如果是普通命令)
{
加入事务队列
}
}
if(命令==multi)
{
创建事务队列
}
if(如果是普通命令)
{
执行命令
}
如果事务中存在语法错误,则所有命令都不会执行
如根本没有test这个语法
如果事务执行过程中,出现了不匹配的错误,不匹配的命令不会执行
如 给 List数据 进行 incr操作
对key添加监视锁
必须在开启事务之前监控
watch key1 key2....
用途:监视key的变化,如果在exec之前,只要有任意监控数据发生了变化,就终止正在运行的所有事务执行。
取消对所有key的监视
unwatch
事务分布式锁
设置一个公共锁
setnx lock-key value
作用:给key设置一个公共锁,
如果有值则返回设置失败,不允许对lock进行操作
没有值就会返回设置成功,允许对lock进行操作
释放公共锁
del lock-key
死锁解决方案
如果在规定时间内,没有解锁就把锁去掉。
expire lock-key seconds
作用:设置lock-key存在的时间,时间一到就删除lock-key
pexpire lock-key milliseconds
删除策略的目标:在内存占用和cpu占用之间寻找一种平,提升或保持redis的性能
时效数据的存储策略:给某一个数据设定一个生命周期后,redis会开辟一个expire的空间,用来存储这个数据的 存放地址和生命周期。利用的是hash结构。
过期数据
过期数据是指,数据的生命周期已经结束,但还存在内存中的数据。
过期数据删除策略
1.定时删除
2.惰性删除
3.定期删除
定时删除
策略:创建一个定时器,当key设置有存在时间,且时间到达,让定时器立即执行对key的删除
优点:节约内存
缺点:导致CPU工作压力大,
无论CPU负载量多高,均会占用CPU,影响redis效率
总结:牺牲处理器的性能换器存储空间
惰性删除
策略:数据到达生命周期时时,不做处理;等下次访问该数据时,再进行删除。
优点:可节约cpu性能,提升redis效率
缺点:内存占用很大,出现长期占用内存的数据
总结:用存储空间换取处理器性能
定期删除
策略:
1.当redis服务启动时,读取配置server.hz的值,默认为10
2.每秒调用server.hz次的serverCron()
3.serverCron()又调用databasesCron()对每一个数据库中的 exipre空间 进行轮巡
4.datavasesCron()又调用activeExpireCycle()对每个exipre空间进行检测,每次执行250ms/server.hz
5.对expire空间进行检测时,每次随机挑选W个检测。
(1).如果key超时就删除key
(2).如果删除的key数量>w25%,就循环执行5过程
(3)如果删除的key数量<=w25%,就去检查下一个expire空间 (0-15)
6.然后由参数current_db记录activeExpireCycle()执行 expire空间 的位置(0-15)
ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP=w
W由ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性定义 ,再在edis.conf中设定。
特点:CPU用于删除的性能可以自定义设置
过时数据不能长久的占用内存。
总结:周期性随机检查内存中的过时数据。
什么是逐出算法:如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为点给钱指令清理存储空间,清理数据的策略叫做 逐出算法。
在redis.conf中配置
最大可用内存
maxmemory
用途:指定占用计算机物理内存大小,默认为0表示不限制。通常设置在50%以上
每次选取待删除数据的个数
maxmemory-samples
用途:指定随机删除的数量
指定删除策略
maxmemory-policy 逐出的相关配置。
用途:达到最大内存后,对被挑选的数据执行删除操作
逐出的相关配置
检测易失数据 expire空间
volatile-lru:挑选最近使用最少数据淘汰
volatile-lfu:挑选最近使用次数最少的数据淘汰
volatile-ttl:挑选要过期的数据淘汰
volatile-random:任意选择数据淘汰
检测全库数据
allkeys-lru:挑选最近使用最少数据淘汰
allkeys-lfu:挑选最近使用次数最少的数据淘汰
allkeys-random:任意选择数据淘汰
禁止数据逐出
no-enviction
设置服务器以守护进程的方式运行
daemonize yes|no
绑定主机地址
bind 127.0.0.1
设置服务器端口号
port 6379
设置数据库数量
database 16
设置服务器其的日志记录级别
Loglevel debug|werbose|notioc|warning
设置日志记录文件名
logfile 端口号.log
设置同一时间的最大连接数
maxclients 0 默认是无限制
客户端闲置的最大时间
timeout 300 如果达到了这个时间就会关闭连接
导入其他配置文件内容
include /path/redis-端口号.conf
三种
Bitmaps
HyperLoglog
GEO
一:Bitmaps
利用计算机中最小的存储单位bit进行数据状态的存储。
基本操作
获取指定key对应偏移量上的bit值
getbit key offset
获取key指定位置的 状态
设置指定key对应偏移量上的bit值,value只能是0或1
setbit key offset value
offset表示位置,value表示存入的状态。
如:setbit age 0 1
统计key中1的数量
bitcount key {start end}
对指定的key进行交,并,非,异或操作,把结果保持到desrKey中
bitop op destKey key1 key2...
and 交
or 并
not 非
xor 异或
二.HyperLoglog
存储不重复的数据的数量,而不存储数据本身。
基本操作
添加数据
pfadd key element1 element2.....
统计数据
pfcount key1 key2....
合并数据
pfmerge destKey key1 key2 .....
三.GEO
用于处理坐标点与坐标点之间的距离关系
基本操作
添加坐标点
geoadd key longitude1 latitude1 memer1 longitude2 latitude2 memer2....
将若干个坐标放到一个容器key中。
longitude:横坐标
latitude:纵坐标
memer:点的名称
获取坐标点
geopos key member1 member2.....
计算坐标点之间的距离
geodist key member1 member2 [unit]
unit表示单位 m,km,fi,mi
如 geodist geos a b m 表示a到b有多少米
根据坐标求范围内的数据
georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist][wtichhash] [count count]
如 georadius geo 1 1 50 km 返回距离(1,1)点 50km以内的数据
withdist:显示出两点之间的距离
withcoord:显示出另一个点的坐标
求一个点指定范围内的数据
georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist][wtichhash] [count count]
如 georadiusbymember geo x 50 km 求在点x 50km以内的点
什么是主从(一对多)
专门写数据:master 主节点 主节点集群
专门读数据:slave 从节点 从节点集群
主从复制:是指master的数据可以同步复制到不同的slave节点中
主从复制的好处
1.可以实现负载均衡,用slave节点分担master的压力
2.故障恢复方便,当master故障时可以由slave提供服务
3.数据持久化,可以实现数据热备份。
主从复制的工作流程
1. slave需要连接master
2. master需要把数据反复同步到slave中
3. 命令传播阶段
建立连接
1. 从节点发送指令 slaveof ip port 请求连接主节点的端口号
再redis.conf中配置也可
2.master接受指令,响应给slave
3.slave保存master的host与port
4.slave创建与master建立连接的socket
5.判断连接是否一直存在
slave周期性的给master ping,master给slave进行响应。
6.slaver发送指令 replconfilstening- ,
把自我端口号发给给master,master保存slave的端口号
具体操作
可以看到,先用从节点6379 去连接主节点6380后,然后再给6380存入数据,从节点可以获取到6380的数据,就说明连接成功了。
slaveof no one 从节点中断开主节点。
数据同步
//全量复制
1.slave发送指令 psync2
2.master执行bgsave,开辟子进程,把数据传入到RDB文件中
3.master创建命令缓冲区,防止漏掉写的指令。
4.RDB文件通过socket发送给slave。
5.slave清空原有数据,进行RDB文件恢复。
//部分复制或者增量复制
6.发送命令告知主节点数据已经恢复,
7.主节点把命令缓冲区的命令发送给从节点
8.从节点执行指令和AOF重写,进行AOF文件恢复。
命令传播阶段(对数据同步阶段的命令具体说明)
socket接受命令缓存区的命令,然后传递给slave,保持数据同步。
部分复制的三个核心要素
1.服务器的运行ID,用于标识服务器
2.主服务器的复制缓冲区
3.主从服务器的数据偏移量
执行过程
主从复制的常见问题
master的数据越来越大,让master重启时,runid会发生变化会导致所以的slave都进行全量复制
内部优化方案:
1.在master执行 shutdown save关闭命令时,将进行RDB持久化,把runnid和offset保存到RDB文件中
分别是 repl-id 和 repl-offset
2.master重启时加载RDB文件,恢复里面的数据,还有runnid和offset,这样就与重启之前的runnid和offset一样,也于slave保持一致,从节点就不必执行全量复制了。
什么是哨兵(暗中观察和操作的黑手!)
哨兵也是一台redis服务器,但是不提供数据服务,哨兵通常配置数量为单数
情节引入:让主节点宕机后,需要需要一个slave节点作为新的master,然后通知所有节点与新的主节点进行连接,然后再进行数据同步。
以上出现了三个问题 1.谁来确认master宕机了,2.谁来找一个新的主节点 3. 最开始的master上线了怎么处理。
以上的三个问题 都是由哨兵进行控制和监视的。
哨兵是一个分布式系统,用于对主从结构中的节点进行监控,当出现故障时采用投票机制选择新的master,并将所有的slave与新的master连接
**哨兵的作用:
监控
不断检查master和slave是否正常运行,检查master的存活情况,master和slave的运行情况。
通知
当被监控的服务器出现问题,向其他客户端发送通知
自动故障转移
断开master和slave连接,选取一个slave作为master,将其他的slave连接到新的master,并告知客户端新的服务地址。
配置哨兵
配置 一拖二的主从结构
主节点6379
从节点 6380 6381
配置三个哨兵
查看 sentinel.conf
26379 26380 26381
启动哨兵
redis-sentinel sentinel-端口号.conf
sentinel.conf配置说明
port 代笔哨兵的端口号
monitor 主节点名称 服务地址 端口号 哨兵数量
如 monitor mymaster 127.0.0.1 6379 2
当有两个哨兵认为主节点宕机时,就替换主节点。
down-after-millsenconds 主机名称 时间
如 down-after-millsenconds mymaster 30000
如果在30000毫秒内,mymaster主机没有响应,就表明主机宕机
parallel-syncs 主机名称 数量
如 parallel-syncs mymaster 1
每次只有一个slave节点和mymaster节点进行数据同步
failover-timeout 主机名称 时间
如 failover-timeout mymaster 180000
如果在180000毫秒的时间内,数据没有同步完成,就认定同步超时,进行主机更换
启动哨兵服务器
可以发现,哨兵识别了主节点和从节点,并且能对他们进行监视了。
哨兵工作原理
哨兵对主从节点进行监控,然后哨兵之间进行信息互通。
监控阶段:
1.sentinel给master发送info,建立cmd连接
2.sentinel保存master和master里面的slave,master里面的其他sentinel的信息
3.sentinel根据保存master里面的slave信息去连接每一个slave,并发送info指令
4.sentinel之间建立连接,相互交换信息进行更新
通知阶段:
只要有一个哨兵监听到数据时,就会告知其他哨兵,其他哨兵也进行数据同步
故障转移阶段:
1.当master宕机之后,首先一个哨兵去访问master没有反应,把master状态变成Sdown,然后超过半数多一个的哨兵去访问master,用去确定master真正的宕机了,把master状态变成Odown。
2.哨兵群进行投票,哨兵得票次数多的用来处理宕机的情况。
3.哨兵sentinel会从
在线的,
响应快的,
与原来master断开时间短的 ,
优先级高的,
slave中挑选出新的master。
4.哨兵sentinel发送指令让其他的slave去连接新的master。
什么是集群
集群就是指将多台计算机(多个主从结构)连接起来,并统一管理的这种方式,对外呈现单机的服务效果。
集群的作用
集群能够把ops压力,存储压力分担到每一台服务器,实现负载均衡,减少单台服务器的压力。
redis集群结构的设计
对集群进行分割设计
首先对集群的所有redis服务器进行取余
每一个redis服务器空间中对应的余数就是此空间的编号
集群存储数据设计
首先对key进行hashcode,然后再取余
找到余数对应的redis槽,然后再把key放入这个槽中
集群添加redis服务器设计
首先对集群进行优化,从每个redis服务器中拿出相等的槽
然后把槽放入到添加的redis服务器之中
集群内部通讯设计
每个redis都服务器都记录了其他redis服务器槽的编号
当key进行查找时
一次命中,key直接一次找到了存储他的redis服务器的槽
两次命中,key在第一个redis服务器查找时没有找到存储他的槽,
于是这个redis服务器就会根据key的编号,告诉key存储他的槽。
在redis.conf中配置集群相关的参数
cluster-enabled yes
声明redis服务器是一个集群中的节点
cluster-config-file nodes-端口号.conf
设置该redis服务器节点在集群中的配置文件名称
cluster-node-timeout time
设置节点超时下线的时间
将节点连接起来,形成集群的指令
1.下载对应的ruby和gem
2.在redis的src目录下执行 redis-trib.rb文件 ,启动集群
./redis-trib.rb create --replicas 一个主节点对应的从节点个数 主节点地址:主节点端口号 主节点地址:主节点端口号 ..... 从节点地址:从节点端口号 从节点地址:从节点端口号....
如:
./redis-trib.rb creat--replicas 2 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
其中 6379 6380是主节点 6381 6382 6383 6384分别是主节点对应的从节点
打开集群客户端对集群进行数据操作指令
redis-cli -c
不加-c 只有到集群中key对应的节点进行数据操作才行,不然会报错。
cluster节点操作指令
查看集群节点信息
cluster nodes
进入一个从节点,切换其主节点
cluster replicate 主节点id
把新节点加入到主节点中
cluster meet ip:port
忽略一个没有slot(槽)的节点
cluster forget id
缓存预热就是系统启动前,提前将相关的缓存数据直接加载到redis中,避免用户进行查询时,直接操作数据库,然后再将数据缓存到redis中的问题,让用户直接查询事先别缓存的数据。
缓存预热的数据前提。
1.需要缓存的数据请求较高
2.主从直接数据吞吐量大,数据同步操作频繁
解决方案
1.统计日常的数据访问记录,保存频度较高的数据
2.利用LRU数据删除策略,构建数据缓存队列
LUR是页面置换算法,给每一个页面标记一个访问字段,当需要删除页面时,挑选最长时间没有改变访问字段的页面。
3.将统计的数据进行分类,让redis优先加载高级别的热点数据。
数据库服务器崩溃
系统在平稳的运行时,忽然数据库连接量激增,导致应用服务器无法及时响应请求,出现了大量的404,500错误页面,用户继续反复刷新页面获取数据,数据量太大导致数据库崩溃,接着应用服务器崩溃,redis服务器崩溃,形成了雪崩,重启数据库后又再次被瞬间流量击倒,导致崩溃。
产生的原因
1.在短时间内,大量的key集中过期,缓存大量的失效,当用户此时大量访问过期数据,redis没有命中,只有大量访问数据库,数据库无法及时处理请求,然后导致redis大量请求被积压,然后数据库流量激增,数据库崩溃,重启后redis中没有数据,接着redis服务器志愿被严重占用,redis崩溃,应用服务器崩溃。
解决的方案
1.更多的页面静态化处理,把常用的热点数据变为静态。
2.构建更多的缓存架构 nginx+redis+ehcache
3.检测mysql严重耗时的业务,并进行优化
4.限流降级,短时间内限制一些用户的体现,限制一些请求访问,降低服务器压力。
1.LRU和LFU相互切换
2.数据有效期调整,不让数据集中失效。给key进行分类失效机制,A类90分钟,B类60分钟,C类30分钟,然后把过期时间变换成固定时间+随机时间的形式,稀释集中到期的key数量。
3.超然数据使用永久key,实时统计访问量,延长热点数据的过期时间。
解决方案总结
首先可以限制用户的流量,然后前端把需要访问的大量热点数据变成静态页面,redis端可以采用页面置换算法LRU和LFU对热点key进行操作,也可以使用分类失效机制和随机时间机制对key进行操作,把超热点数据变成永久key,接着可以在redis和数据库端直接加入更多的缓存架构,即使redis没有命中,其他的缓存架构也可以命中,最后可以查询数据库中严重耗时的业务,然后对这些业务进行优化,减少redis的压力,提高数据库的效率。
redis中某一个key过期,但是该key访问量突然巨大,多个数据请求到redis中后都没有命中,然后redis在短期内让大量的请求对数据库中的同一数据进行访问。
产生的原因
redis中单个key的超热数据过期,然后又有大量的请求进行访问
解决的方案
1.预先设定,当需要进行某个活动时,在活动之前延长相应可以的过期时间。
2.监控访问量,对访问量激增的key进行过期时间延迟,或者设置为永久key
3.后台刷新数据,刷新key的有效期。
4.设置二级缓存,保障key不会同时淘汰
5.给key加锁,不让多个请求同时访问一个数据。
系统平稳的运行过程中,应用服务器访问量越来越大,(黑客攻击)redis服务器的命中概率越来越低数据库压力开始增大,导致了数据库崩溃。而redis内存也比较平稳,没有大量增加新内存。
redis大面积没有命中,导致数据库压力变大,比如;出现了非正常的错误的url访问,因为在redis中没有这些错误的url,所有无法命中,于是要访问数据库进行校验,导致数据库崩溃。
产生的原因
获取的数据在数据库中也不存在,所以也不会缓存到redis服务器中,反复如此,redis中也不会存在此类错误数据,当黑客攻击时,发送大量错误数据,数据库请求量大时,数据库就会崩溃。
解决的方案
1.缓存错误的url,把他置为null,设置短时间的过期时间
2.白名单策略
给数据id做对应的bitmaps,用id跟bitmaps做比较,当id在bitmaps中时放行,不在bitmaps中就拦截
使用布隆过略器,不一定是100%过略,少部分不影响
3.进行实时监控,监控redis命中率和null数据的比例,比例变大后,就启用白名单策略和把错误的数据缓存为null
4.对key进行加密,把key在业务层进行校验,如果不满住加密规则,就不让key对数据库和redis进行访问。