* 1.为什么要使用redis
* 一般来说是为了提高性能,缓解数据库的压力,同时可以进行持久化操作RDB和AOF
* 2.在liunx安装redis,可以将redis的bin,加入到环境变量中。
* bin目录的常用命令:
* Redis-benchmark :压力测试。标准是每秒80000次写操作,110000次读操作(服务启动起来后执行,类似安兔兔跑分)
* Redis-check-dump:修改有问题的dump.rdb文件
* Redis-sentinel:启动哨兵,集群使用
* Redis-server 启动服务器
* Redis-cli:启动客户端
*
* 3.数据库连接操作
* select <dbip> :切换数据库 select 1
* flushdb:清空当前数据库
* dbsize:查看数据库数据个数
* flushall:通杀全部库
*
* 4.key 的操作
* key基本都是字符串
* value是五种类型
* keys pattern:查询符合指定表达式的所有key,支持*,?等
* type key:查看类型
* exists key:指定的key是否存在。0代表不存在,1代表存在
* del key:删除指定的key
* randomkey:在现在有的key中随机返回一个
* expire key sceonds:为键值设置过期时间,单位是秒,过期后key会被redis移除
* ttl key:查看key还有多少秒过期,-1表示永不过期,-2表示已过期
* rename key newkey :重命名一个key,不管在不在都执行
* renamenx key newkey:只有在newkey不存在时候能够执行成功,否则失败
* /**
* 1.为什么要使用redis
* 一般来说是为了提高性能,缓解数据库的压力,同时可以进行持久化操作RDB和AOF
* 2.在liunx安装redis,可以将redis的bin,加入到环境变量中。
* bin目录的常用命令:
* Redis-benchmark :压力测试。标准是每秒80000次写操作,110000次读操作(服务启动起来后执行,类似安兔兔跑分)
* Redis-check-dump:修改有问题的dump.rdb文件
* Redis-sentinel:启动哨兵,集群使用
* Redis-server 启动服务器
* Redis-cli:启动客户端
*
* 3.数据库连接操作
* select <dbip> :切换数据库 select 1
* flushdb:清空当前数据库
* dbsize:查看数据库数据个数
* flushall:通杀全部库
*
* 4.key 的操作
* key基本都是字符串
* value是五种类型
* keys pattern:查询符合指定表达式的所有key,支持*,?等
* type key:查看类型
* exists key:指定的key是否存在。0代表不存在,1代表存在
* del key:删除指定的key
* randomkey:在现在有的key中随机返回一个
* expire key sceonds:为键值设置过期时间,单位是秒,过期后key会被redis移除
* ttl key:查看key还有多少秒过期,-1表示永不过期,-2表示已过期
* rename key newkey :重命名一个key,不管在不在都执行
* renamenx key newkey:只有在newkey不存在时候能够执行成功,否则失败
*
* * hash 操作
* HSET key field value 为 key 中的 field 赋值 value
* HMSET key field value [field value ...] 为指定 key 批量设置 field-value
* HSETNX key field value 当指定 key 的 field 不存在时,设置其 value
* HGETALL key 获取指定 key 的所有信息(field 和 value)
* HKEYS key 获取指定 key 的所有 field
* HVALS key 获取指定 key 的所有 value
* HLEN key 指定 key 的 field 个数
* HGET key field 从 key 中根据 field 取出 value
* HMGET key field [field ...] 为指定 key 获取多个 filed 的值
* HEXISTS key field 指定 key 是否有 field
* HINCRBY key field increment 为指定 key 的 field 加上增量 increment
*
* zset
* ZADD key [score member ...] 添加
* ZSCORE key member 返回指定值的分数
* ZRANGE key start stop [WITHSCORES] 返回指定区间的值,可选择是否一起返回
* scores
* ZRANGEBYSCORE key min max [WITHSCORES]
* [LIMIT offset count]
* 在分数的指定区间内返回数据,从小到大排列
* ZREVRANGEBYSCORE key max min
* [WITHSCORES] [LIMIT offset count]
* 在分数的指定区间内返回数据,从大到小排列
* ZCARD key 返回集合中所有的元素的数量
* ZCOUNT key min max 统计分数区间内的元素个数
* ZREM key member 删除该集合下,指定值的元素
* ZRANK key member 返回该值在集合中的排名,从 0 开始
* ZINCRBY key increment value 为元素的 score 加上增
*
* 5.持久化
* reids的持久化分为两种
* AOF和rdb
* redis是基于内存的数据的数据存储
* 当写入数据的时候会先从内存中读取数据,任何加载到磁盘中,以此来实现redis的持久化
* RDB是基于快照存储,在指定时间内对数据进行快照存储
* AOF是记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议保存写的操作到文件末尾,还可以对AOF文件进行后台重写,使得AOF文件不至于过大
*
* 当然,如果只是为了在服务器运行期间保留数据,完全不需要开启两种方式
*
* 一般来说建议是两种方式都开启
* ,一般来说在重启的时候都会加载AOF文件来恢复原始数据,因为在通常情况下AOF文件保存的数据集要比RDBB保存的数据集要完整
*
* 优点:
* RDB是一个非常紧凑性的文件,它保存了某个时间点的数据,非常适合数据集的备份
* ,比如过去24小时内的数据,同时每天保存过去30天的数据。这样即使出了问题你也可以根据需求恢复到不同版本的数据集
* RDB是一个紧凑的单一文件,很方便传送到另外一个远端数据中心或者亚马逊的s3,非常适用于灾难恢复
* RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久方式可以最大化redis性能
* AOF相比,在恢复大的数据集的时候,RDB方式会更快一些
*
* RDB的缺点:
* 如果你希望在redis意外停止工作时候的情况下丢失的数据最少的话,rdb明显不适合你,虽然你可以配置不同的save时间点,但是redis要完整的保存数据集是一个比较繁重的工作,你通常每
* 隔五分钟或者更久做一次完整的保存,万一再redis意外宕机的时候,你可能丢失几分钟的数据
* RDB需要经常fork子进程来保存数据到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致redis再一些毫秒级内不能响应客户端的请求
* ,如果数据集巨大并且cpu性能不是很好的情况下,这阵子情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度
*
*AOF
* AOF 保存策略
* appendfsync always:每次产生一条新的修改数据的命令都执行保存操作;效率低,但是安全!
* appendfsync everysec:每秒执行一次保存操作。如果在未保存当前秒内操作时发生了断电,仍然会导致一部分数据
* 丢失(即 1 秒钟的数据)。
* appendfsync no:从不保存,将数据交给操作系统来处理。更快,也更不安全的选择。
* 推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安
*
*AOF 文件的修复
* 如果 AOF 文件中出现了残余命令,会导致服务器无法重启。此时需要借助 redis-check-aof 工具来修复!
* 命令: redis-check-aof –fix
*
* AOF 的优缺点
* 使用AOF会让你的redis更加耐久,你可以使用不同的fsync策列,一旦出现故障,你最多丢失1秒的数据
* AOF文件是一个只进行追加的日志文件,不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等),未执行完的写入命令,你也可以通过redis-check-aof-fix工具修复这些问题。
* redis可以再aof文件体积中变得过大时,自动的再后台对AOF进行重写,重写后端新AOF文件包含了恢复当前数据集的所需的最小命令集合。整个重写操作是绝对安全的
* 。因为redis再创建aof文件的过程中,会将命令追加到现有的AOF文件里面,即使重写过程中发生停机,现有的aof文件也不会丢失,而一旦新的aof文件创建完毕,redis就会替换掉旧的aof我加你,并开始对新的AOF
* 文件进行追加操作。
* Aof文件有序的保存了对数据库执行的所有写入操作,这些写入操作已redis协议格式保存,因此aof文件的内容非常容易被人读懂,对文件进行分析也很轻松,
* 举个例子:如果不小心执行了flushall命令,但是只要还没有被重写,那么只要停止服务器,移除
* aof文件末尾的flushall命令,并重启redis,就可以将数据集恢复到flushall执行之前的状态。
*
*
* 缺点:
* 对于相同的数据集来说,aof文件的体积通常要大于rdb文件的体积
* 根据所使用的fsync策略,aof的速度可能会慢于rdb,在一般情况下,每秒fsync的性能依然非常的高,而关闭fsync可以让aof速度和rdb一样快,
* 即使再高负荷之下也是如此,不过再处理巨大的写入载入时,rdb可以提供更有保证的最大延迟时间
* * 如何看待数据“绝对”安全
* Redis 作为内存数据库从本质上来说,如果不想牺牲性能,就不可能做到数据的“绝对”安全。
* RDB 和 AOF 都只是尽可能在兼顾性能的前提下降低数据丢失的风险,如果真的发生数据丢失问题,尽可能
* 减少损失。
* 在整个项目的架构体系中,Redis 大部分情况是扮演“二级缓存”角色。
* 二级缓存适合保存的数据
* 经常要查询,很少被修改的数据。
* 不是非常重要,允许出现偶尔的并发问题。
* 不会被其他应用程序修改。
* 如果 Redis 是作为缓存服务器,那么说明数据在 MySQL 这样的传统关系型数据库中是有正式版本的。数据
* 最终以 MySQL 中的为准
*
* 为什么redis不支持回滚?
* redis命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或者是命令用在了错误类型的键上面,这也就是说,=从实用性的角度来说,失败的命令是由于编程的错误造成的,而这些错误应该在开发
* 的过程中被发现,而不应该出现在生产环境。
* 因为不需要对回滚进行支持,所以redis的内部可以保持简单快速。
* 还有就是回滚并不能解决编程错误代理的问题
* 举个例子,如果你本来想通过incr命令将键值加1,不小心加2,又或者对错误类型的键执行ince,回滚是没有办法处理这些情况的
* * 下线:
* ①主观下线:Subjectively Down,简称 SDOWN,指的是当前 Sentinel 实例对某个 redis 服务器做出的
* 下线判断。
* ②客观下线:Objectively Down, 简称 ODOWN,指的是多个 Sentinel 实例在对 Master Server 做出
* SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的 Master Server 下线
* 判断,然后开启 failover.
*
*
*
* 工作原理:
* ①每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命
* 令 ;
* ②如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所
* 指定的值, 则这个实例会被 Sentinel 标记为主观下线;
* ③如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认
* Master 的确进入了主观下线状态;
* ④当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认 Master 的确进入了主
* 观下线状态, 则 Master 会被标记为客观下线 ;
* ⑤在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令
* ⑥当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的
* 频率会从 10 秒一次改为每秒一次 ;
* ⑦若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除;
* 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除
*
* Jedis
*
* 使用连接池
* 连接池的好处:节省每次连接 redis 服务带来的消耗,将创建好的连接实例反复利用;
* 连接池常用参数
* 参数 含义
* MaxTotal 控制一个 pool 可分配多少个 jedis 实例,通过 pool.getResource()来获取;如果赋值为
* -1,则表示不限制;如果 pool 已经分配了 MaxTotal 个 jedis 实例,则此时 pool 的状态
* 为 exhausted。
* maxIdle 控制一个 pool 最多有多少个状态为 idle(空闲)的 jedis 实例。
* MaxWaitMillis 表示当 borrow 一个 jedis 实例时,最大的等待毫秒数,如果超过等待时间,则直接抛
* JedisConnectionException。
* testOnBorrow 获得一个 jedis 实例的时候是否检查连接可用性(ping());如果为 true,则得到的 jedis
* 实例均是可用的
* * 5. 常用五大数据类型:string,list,set,zset,hash
* string:redis中一个字符最大512m
* SET key value 添加键值对
* GET key 查询指定 key 的值
* APPEND key value 将给定的 value 追加到原值的末尾
* STRLEN key 获取值的长度
* SETNX key value 只有在 key 不存在时设置 key 的值
* INCR key 指定 key 的值自增 1,只对数字有效
* DECR key 指定 key 的值自减 1,只对数字有效
* INCRBY key num 自增 num
* DECRBY key num 自减 num
* MSET key1 value1 key2 value2… 同时设置多个 key-value 对
* MGET key1 key2 同时获取一个或多个 value
* MSETNX key1 value1 key2 value2 当 key 不存在时,设置多个 key-value 对
* GETRANGE key 起始索引 结束索引 获取指定范围的值,都是闭区间
* SETRANGE key 起始索引 value 从起始位置开始覆写指定的值
* GETSET key value 以新换旧,同时获取旧值
* SETEX key 过期时间 value 设置键值的同时,设置过期时间,单位秒
*
* list:双链表
* LPUSH/RPUSH KEY value1,value2 从左边或者右边压入一个或者多个值,头尾效率高,中间效率低
* Lpop/Rpop key 删除
* Lrange key start stop 查看指定区间的数据 0,-1
* Lindex key index 按照索引下标获取元素
* llie key 获取长度
* LINSERT key BEFORE|AFTER value newvalue 在指定 value 的前后插入 newvalue
* LREM key n value 从左边删除 n 个 value
* LSET key index value 把指定索引位置的元素替换为另一个值
* LTRIM key start stop 仅保留指定区间的数据
* RPOPLPUSH key1 key2 从 key1 右边弹出一个值,左侧压入到 key2
*
* set 操作
* set 是无序的,且是不可重复的。
* SADD key member [member ...] 将一个或多个 member 元素加入到集合 key 当中,已
* 经存在于集合的 member 元素将被忽略。
* SMEMBERS key 取出该集合的所有值
* SISMEMBER key value 判断集合<key>是否为含有该<value>值,有返回 1,没有
* 返回 0
* SCARD key 返回集合中元素的数量
* SREM key member [member ...] 从集合中删除元素
* SPOP key [count] 从集合中随机弹出 count 个数量的元素,count 不指定就
* 弹出 1 个
* SRANDMEMBER key [count] 从集合中随机返回 count 个数量的元素,count 不指定就
* 返回 1 个
* SINTER key [key ...] 将指定的集合进行“交集”操作
* SINTERSTORE dest key [key ...] 取交集,另存为一个 set
* SUNION key [key ...] 将指定的集合执行“并集”操作
* SUNIONSTORE dest key [key ...] 取并集,另存为 set
* SDIFF key [key ...] 将指定的集合执行“差集”操作
* SDIFFSTORE dest key [key ...] 取差集,另存为 set
* * 主从复制:
* 配置多台redis。根据主机和从机的身份分开。主机数据更新后,根据配置和策略,自动同步到备机
* master以写为主,slaveof以读为主,二者之间自动同步数据。
* 目的:读写分离提高redis性能
* 避免单点故障,发生灾难能够快速恢复
* 原理:
* 每次从机联通后,都会给主机发送sync指令,主机立刻进行存盘操作,发送RDB文件给从机,收到从机文件后,进行全盘加载,之后主机每次的写操作,都会立刻发送给从机,从机执行相同的命令
*
*
* 主从建立
* 1. 临时建立
* 原则:配从不配主。
* 配置:在从服务器上执行 SLAVEOF ip:port 命令;
* 查看:执行 info replication
*
* 2 永久建立
* 在从机的配置文件中,编写 slaveof
*
* 3.恢复身份
* 执行命令 slaveof no noe 恢复自由身
*
*
*
* 4. 主从常见问题
* ①从机是从头开始复制主机的信息,还是只复制切入以后的信息?
* 答:从头开始复制,即完全复制。
* ②从机是否可以写?
* 答:不能
* ③主机 shutdown 后,从机是上位还是原地待命?
* 答:原地待命
* ④主机又回来了后,主机新增记录,从机还能否顺利复制?
* 答:可以
* ⑤从机宕机后,重启,宕机期间主机的新增记录,丛集是否会顺利复制?
* 答:可以
* ⑥其中一台从机 down 后重启,能否重认旧主?
* 答:不一定,看配置文件中是否配置了 slaveof
* ⑦如果两台从机都从主机同步数据,此时主机的 IO 压力会增大,如何解决?
* 答:按照主---从(主)---从模式配置
*
测试:
String
public class RedisClient {
public static void main(String[] args) {
Jedis jedis = new Jedis("172.16.19.80",6379);
String ping = jedis.ping();
System.out.println(ping);
//获取所有key
jedis.set("a","1");
Set<String> keys = jedis.keys("a");
System.out.println(keys);
for (String key : keys) {
System.out.println(key);
}
//判断是否存在某个key
System.out.println("是否存在k2:"+jedis.exists("k2"));
//测试某个key的过期时间
System.out.println("k1的存活时间:"+jedis.ttl("k2"));
jedis.close();
}
Map
public class RedisClient {
public static void main(String[] args) {
//连接指定的redis,需要ip地址和端口号
Jedis jedis1 = new Jedis("172.16.19.80",6379);
jedis1.zadd("myZset", 100, "math");
//将多个数据封装为一个map
Map<String, Double> subject=new HashMap<String, Double>();
subject.put("chinese", 88d);
subject.put("english", 86d);
//批量添加数据
jedis1.zadd("myZset", subject);
Set<String> zset = jedis1.zrange("myZset", 0, -1);
for (String val : zset) {
System.out.println(val);
}
//关闭连接
jedis1.close();
}
list
public class RedisClient {
public static void main(String[] args) {
//连接指定的redis,需要ip地址和端口号
Jedis jedis = new Jedis("172.16.19.80",6379);
jedis.lpush("mylist", "1","2","3","4");
List<String> list = jedis.lrange("mylist", 0, -1);
for (String element : list) {
System.out.println(element);
}
//关闭连接
jedis.close();
}
Set
public class RedisClient {
public static void main(String[] args) {
//连接指定的redis,需要ip地址和端口号
Jedis jedis = new Jedis("172.16.19.80",6379);
//添加元素
jedis.sadd("mySet", "Jack","Marry","Tom","Tony");
//删除指定元素
jedis.srem("mySet", "Tony");
//获取指定key的元素
Set<String> smembers = jedis.smembers("mySet");
for (String member : smembers) {
System.out.println(member);
}
//关闭连接
jedis.close();
zset
public class RedisClient {
public static void main(String[] args) {
//连接指定的redis,需要ip地址和端口号
Jedis jedis = new Jedis("172.16.19.80",6379);
jedis.zadd("myZset", 100, "math");
//将多个数据封装为一个map
Map<String, Double> subject=new HashMap<String, Double>();
subject.put("chinese", 88d);
subject.put("english", 86d);
//批量添加数据
jedis.zadd("myZset", subject);
Set<String> zset = jedis.zrange("myZset", 0, -1);
for (String val : zset) {
System.out.println(val);
}
//关闭连接
jedis.close();
数据库的设置
//默认的连接池配置
GenericObjectPoolConfig objectGenericObjectPoolConfig = new GenericObjectPoolConfig();
System.out.println("========="+objectGenericObjectPoolConfig);
JedisPool jedisPool = new JedisPool(objectGenericObjectPoolConfig,"172.16.19.80",6379,60000);
Jedis resource = jedisPool.getResource();
String ping = resource.ping();
System.out.println(resource);
System.out.println(ping);
resource.close();
jedisPool.close();
数据库工具类
/**
* @author 180937
* 数据库连接池
*/
public class JedisPoolUtil {
@Autowired
RedisTemplate redisTemplate;
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);//最大空闲连接数
//获取连接时的最大等待毫秒数,如果超时就抛异常,默认-1
poolConfig.setMaxWaitMillis(100 * 1000);
//连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
poolConfig.setBlockWhenExhausted(true);
//在获取连接的时候检查有效性, 默认false
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig, "172.16.19.80", 6379, 60000);
}
}
}
return jedisPool;
}
}