day044 Redis 第二天

1 Redis的数据结构

1.1 存储list

1.1.1 概述

在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295。

   从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。相信对于有良好数据结构基础的开发者而言,这一点并不难理解。

1、 ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。

2、 LinkedList使用双向链接方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢。

3、 双向链表中添加数据

day044 Redis 第二天_第1张图片

4、 双向链表中删除数据

day044 Redis 第二天_第2张图片

1.1.2 常用命令

1.1.2.1 两端添加

 lpush key values[value1 value2…]:在指定的key所关联的list的头部插入所有的values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。

image.png

 

 rpush key values[value1、value2…]:在该list的尾部添加元素。

image.png

1.1.2.2 查看列表

 lrange key start end:获取链表中从start到end的元素的值,start、end从0开始计数;也可为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推…  

day044 Redis 第二天_第3张图片

1.1.2.3 两端弹出

 lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素。如果该key不存在,返回nil;若key存在,则返回链表的头部元素。

day044 Redis 第二天_第4张图片

 rpop key:从尾部弹出元素。

day044 Redis 第二天_第5张图片

1.1.2.4 获取列表中元素的个数

 llen key:返回指定的key关联的链表中的元素的数量。

day044 Redis 第二天_第6张图片

1.1.3 扩展命令(了解)

 lpushx key value:仅当参数中指定的key存在时,向关联的list的头部插入value。如果不存在,将不进行插入。

day044 Redis 第二天_第7张图片

 rpushx key value:仅当参数中指定的key存在时,向关联的list的尾部插入value。如果不存在,将不进行插入。

day044 Redis 第二天_第8张图片

 lrem key count value:删除count个值为value的元素,如果count大于0,从头向尾遍历并删除count个值为value的元素,如果count小于0,则从尾向头遍历并删除。如果count等于0,则删除链表中所有等于value的元素。(注意:左边第一个数据的索引是0,左边第二个索引是1,右边第一个数据的索引是-1,右边第二个是-2)

0)初始化数据

day044 Redis 第二天_第9张图片

1) 从头删除,2个数字“3”

lrem mylist3 2 3

day044 Redis 第二天_第10张图片

2) 从尾删除,2个数字“1”

lrem mylist3 -2 1

day044 Redis 第二天_第11张图片

3) 删除所有数字“2”

lrem mylist3 0 2

day044 Redis 第二天_第12张图片

 lset key index value:设置链表中的index的脚标的元素值,0代表链表的头元素,-1代表链表的尾元素。操作链表的脚标不存在则抛异常。

day044 Redis 第二天_第13张图片

 linsert key before|after pivot value:在pivot元素前或者后插入value这个元素。

day044 Redis 第二天_第14张图片

 rpoplpush resource destination:将链表中的尾部元素弹出并添加到头部。[循环操作]

image.png

1) 将mylist5右端弹出,压入到mylist6左边。

day044 Redis 第二天_第15张图片

2) 将mylist6右端数据弹出,压入到左端

day044 Redis 第二天_第16张图片

1.1.4使用场景

rpoplpush的使用场景:

Redis链表经常会被用于消息队列的服务,以完成多程序之间的消息交换。假设一个应用程序正在执行LPUSH操作向链表中添加新的元素,我们通常将这样的程序称之为"生产者(Producer)",而另外一个应用程序正在执行RPOP操作从链表中取出元素,我们称这样的程序为"消费者(Consumer)"。如果此时,消费者程序在取出消息元素后立刻崩溃,由于该消息已经被取出且没有被正常处理,那么我们就可以认为该消息已经丢失,由此可能会导致业务数据丢失,或业务状态的不一致等现象的发生。然而通过使用RPOPLPUSH命令,消费者程序在从主消息队列中取出消息之后再将其插入到备份队列中,直到消费者程序完成正常的处理逻辑后再将该消息从备份队列中删除。同时我们还可以提供一个守护进程,当发现备份队列中的消息过期时,可以重新将其再放回到主消息队列中,以便其它的消费者程序继续处理。

day044 Redis 第二天_第17张图片

1.2 存储set

1.2.1 概述

在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间复杂度为O(1),即常量时间内完成次操作。Set可包含的最大元素数量是4294967295。

和List类型不同的是,Set集合中不允许出现重复的元素,这一点和C++标准库中的set容器是完全相同的。换句话说,如果多次添加相同元素,Set中将仅保留该元素的一份拷贝。和List类型相比,Set类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计算操作,如unions、intersections和differences。由于这些操作均在服务端完成,因此效率极高,而且也节省了大量的网络IO开销。

1.2.2 常用命令

1.2.2.1 添加/删除元素

 sadd key values[value1、value2…]:向set中添加数据,如果该key的值已有则不会重复添加

image.png

 srem key members[member1、member2…]:删除set中指定的成员

image.png

1.2.2.2 获得集合中的元素

 smembers key:获取set中所有的成员

day044 Redis 第二天_第18张图片

 sismember key member:判断参数中指定的成员是否在该set中,1表示存在,0表示不存在或者该key本身就不存在。(无论集合中有多少元素都可以极速的返回结果)

image.png

1.2.2.3 集合的差集运算 A-B

 sdiff key1 key2…:返回key1与key2中相差的成员,而且与key的顺序有关。即返回差集。

day044 Redis 第二天_第19张图片

day044 Redis 第二天_第20张图片

1.2.2.4 集合的交集运算 A ∩ B

 sinter key1 key2 key3…:返回交集。

day044 Redis 第二天_第21张图片

day044 Redis 第二天_第22张图片

1.2.2.5 集合的并集运算 A ∪ B

 sunion key1 key2 key3…:返回并集。

day044 Redis 第二天_第23张图片

day044 Redis 第二天_第24张图片

1.2.3 扩展命令(了解)

 scard key:获取set中成员的数量

day044 Redis 第二天_第25张图片

 srandmember key:随机返回set中的一个成员

image.png

 sdiffstore destination key1 key2…:将key1、key2相差的成员存储在destination上 

day044 Redis 第二天_第26张图片

 sinterstore destination key[key…]:将返回的交集存储在destination上

day044 Redis 第二天_第27张图片

 sunionstore destination key[key…]:将返回的并集存储在destination上

day044 Redis 第二天_第28张图片

1.2.4 使用场景

1、可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。对于此场景,我们仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一性。

2、充分利用Set类型的服务端聚合操作方便、高效的特性,可以用于维护数据对象之间的关联关系。比如所有购买某一电子设备的客户ID被存储在一个指定的Set中,而购买另外一种电子产品的客户ID被存储在另外一个Set中,如果此时我们想获取有哪些客户同时购买了这两种商品时,Set的intersections命令就可以充分发挥它的方便和效率的优势了。

1.3 存储sortedset

1.3.1 概述

Sorted-Set和Set类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Set中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Set中的成员必须是唯一的,但是分数(score)却是可以重复的。

在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为集合中成员数量的对数。由于Sorted-Set中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。事实上,Redis所具有的这一特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和Redis同样的高效,在其它数据库中进行建模是非常困难的。

例如:游戏排名、微博热点话题等使用场景。

1.3.2 常用命令

1.3.2.1 添加元素

 zadd key score member score2 member2 … :将所有成员以及该成员的分数存放到sorted-set中。如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。

image.png

image.png

1.3.2.2 获得元素

 zscore key member:返回指定成员的分数

image.png

 zcard key:获取集合中的成员数量

image.png

1.3.2.3 删除元素

 zrem key member[member…]:移除集合中指定的成员,可以指定多个成员。image.png

1.3.2.4 范围查询

 zrange key start end [withscores]:获取集合中脚标为start-end的成员,[withscores]参数表明返回的成员包含其分数。

day044 Redis 第二天_第29张图片

day044 Redis 第二天_第30张图片

 zrevrange key start stop [withscores]:照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)

day044 Redis 第二天_第31张图片

 zremrangebyrank key start stop: 按照排名范围删除元素

image.png

 zremrangebyscore key min max:按照分数范围删除元素

day044 Redis 第二天_第32张图片

1.3.3 扩展命令(了解)

 zrangebyscore key min max [withscores] [limit offset count]:返回分数在[min,max]的成员并按照分数从低到高排序。[withscores]:显示分数;[limit offset count]:offset,表明从脚标为offset的元素开始并返回count个成员。

day044 Redis 第二天_第33张图片

image.png

 zincrby key increment member:设置指定成员的增加的分数。返回值是更改后的分数。

image.png

 zcount key min max:获取分数在[min,max]之间的成员

image.png

 zrank key member:返回成员在集合中的排名。(从小到大)

image.png

 zrevrank key member:返回成员在集合中的排名。(从大到小)

image.png

1.3.4 使用场景

1、可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOPTEN的用户信息。当然我们也可以利用ZRANK命令通过username来获取玩家的排行信息。最后我们将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息。

2、Sorted-Set类型还可用于构建索引数据。

第2章 keys的通用操作

 keys pattern:获取所有与pattern匹配的key,返回所有与该key匹配的keys。*表示任意一个或多个字符,?表示任意一个字符

day044 Redis 第二天_第34张图片

 del key1 key2…:删除指定的key

image.png

 exists key:判断该key是否存在,1代表存在,0代表不存在

day044 Redis 第二天_第35张图片

 rename key newkey:为当前的key重命名

day044 Redis 第二天_第36张图片

 expire key :设置过期时间,单位:秒

image.png

 ttl key:获取该key所剩的超时时间,如果没有设置超时,返回-1。如果返回-2表示超时不存在。

day044 Redis 第二天_第37张图片

 type key:获取指定key的类型。该命令将以字符串的格式返回。 返回的字符串为string、list、set、hash和zset,如果key不存在返回none。

day044 Redis 第二天_第38张图片

3 Jedis 入门

3.1 Jedis介绍

Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。  

在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。  

Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis

3.2 Java连接Redis

3.2.1 导入jar包

image.png

3.2.2 单实例连接

day044 Redis 第二天_第39张图片

@Test
public void testJedisSingle(){
    //1 设置ip地址和端口
    Jedis jedis = new Jedis("192.168.137.128", 6379);
    //2 设置数据
    jedis.set("name", "hahaha");
    //3 获得数据
    String name = jedis.get("name");
    System.out.println(name);
    //4 释放资源
    jedis.close();
}

3.2.3 连接超时

 如果运行上面代码时,抛如下异常

redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out

 必须设置linux防火墙

vim /etc/sysconfig/iptables

image.png

service iptables restart

image.png

3.2.4 连接池连接

day044 Redis 第二天_第40张图片

@Test
public void testJedisPool(){
    //1 获得连接池配置对象,设置配置项
    JedisPoolConfig config = new JedisPoolConfig();
    // 1.1 最大连接数
    config.setMaxTotal(30);
    // 1.2  最大空闲连接数
    config.setMaxIdle(10);
    
    //2 获得连接池
    JedisPool jedisPool = new JedisPool(config, "192.168.137.128", 6379);
    
    //3 获得核心对象
    Jedis jedis = null;
    try {
        jedis = jedisPool.getResource();
        
        //4 设置数据
        jedis.set("name", "itcast");
        //5 获得数据
        String name = jedis.get("name");
        System.out.println(name);
        
    } catch (Exception e) {
        e.printStackTrace();
    } finally{
        if(jedis != null){
            jedis.close();
        }
        // 虚拟机关闭时,释放pool资源
        if(jedisPool != null){
            jedisPool.close();
        }
    }
}

做一个工具类

MyJedisPoolUtils.java

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/*
 * jedis连接池工具类
 */
public class MyJedisPoolUtils {
    private static JedisPool jp;
    static{
        //1:创建一个连接池初始化对象;(可以用于设置最大连接数和最大空闲数)
        JedisPoolConfig jpc = new JedisPoolConfig();
        jpc.setMaxTotal(10);
        jpc.setMaxIdle(2);
        //2:使用初始化对象,ip地址,端口号,创建一个连接池对象
         jp = new JedisPool(jpc,"192.168.116.130",6379);
    }
    
    //提供一个获取会话对象的方法
    public static Jedis getJredis(){
        return jp.getResource();
    }
}

4 Redis特性

4.1 多数据库

4.1.1 概念

一个Redis实例可以包括多个数据库,客户端可以指定连接某个redis实例的哪个数据库,就好比一个mysql中创建多个数据库,客户端连接时指定连接哪个数据库。

一个redis实例最多可提供16个数据库,下标从0到15,客户端默认连接第0号数据库,也可以通过select选择连接哪个数据库,如下连接1号库:  

image.png

连接0号数据库:

day044 Redis 第二天_第41张图片

4.1.2 将newkey移植到1号库

 move newkey 1:将当前库的key移植到1号库中

day044 Redis 第二天_第42张图片

4.2 服务器命令(自学)

 ping,测试连接是否存活

image.png

//执行下面命令之前,我们停止redis 服务器

redis 127.0.0.1:6379> ping

Could not connect to Redis at 127.0.0.1:6379: Connection refused

 echo,在命令行打印一些内容

image.png

 select,选择数据库。Redis 数据库编号从0~15,可以选择任意一个数据库来进行数据的存取。

image.png

当选择16 时,报错,说明没有编号为16 的这个数据库

 quit,退出连接。

image.png

 dbsize,返回当前数据库中key 的数目。

image.png

 info,获取服务器的信息和统计。

day044 Redis 第二天_第43张图片

 flushdb,删除当前选择数据库中的所有key。

day044 Redis 第二天_第44张图片

 flushall,删除所有数据库中的所有key。

day044 Redis 第二天_第45张图片

在本例中我们先查看了一个1 号数据库中有一个key,然后我切换到0 号库执行flushall 命令,结果1 号库中的key 也被清除了,说是此命令工作正常。

4.3 消息订阅与发布

 subscribe channel:订阅频道,例:subscribe mychat,订阅mychat这个频道

 psubscribe channel*:批量订阅频道,例:psubscribe s*,订阅以”s”开头的频道

 publish channel content:在指定的频道中发布消息,如 publish mychat ‘today is a newday’

 步骤1:在第一个连接中,订阅mychat频道。此时如果没有人“发布”消息,当前窗口处于等待状态。

day044 Redis 第二天_第46张图片

 步骤2:在另一个窗口中,在mychat频道中,发布消息。

day044 Redis 第二天_第47张图片

day044 Redis 第二天_第48张图片

 步骤3:再第三个窗口,批量订阅以my开头所有频道。

day044 Redis 第二天_第49张图片

 步骤4:在第二个窗口,分别在“mychat”和“mychat2”发布消息

day044 Redis 第二天_第50张图片

4.4 redis事务

4.4.1 概念

和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/这三个命令是我们实现事务的基石。

4.4.2 redis事务特征

1、 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行

2、 和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。

3、 我们可以通过MULTI命令开启一个事务,有关系型数据库开发经验的人可以将其理解为"BEGIN TRANSACTION"语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令可被视为等同于关系型数据库中的COMMIT/ROLLBACK语句。

4、 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事务中的所有命令都会被服务器执行。

5、 当使用Append-Only模式时,Redis会通过调用系统函数write将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。Redis服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。此时,我们就要充分利用Redis工具包中提供的redis-check-aof工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动Redis服务器了。

4.4.3 命令解释

 multi:开启事务用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行,类似与关系型数据库中的:begin transaction

 exec:提交事务,类似与关系型数据库中的:commit

 discard:事务回滚,类似与关系型数据库中的:rollback

4.4.4 测试

4.4.4.1 正常执行事务

 步骤1:在窗口1,设置num,并获得数据

image.png

 步骤2:在窗口2,num累加1,并获得数据

image.png

 步骤3:在窗口1,获得数据

image.png

 步骤4:在窗口1,开启事务,多次累加数据。

day044 Redis 第二天_第51张图片

 步骤5:在窗口2,获得数据

image.png

 步骤6:提交事务

image.png

127.0.0.1:6379> set num 1
OK
127.0.0.1:6379> get num
"1"
127.0.0.1:6379> get num
"2"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr num
QUEUED
127.0.0.1:6379> incr num
QUEUED
127.0.0.1:6379> exec
1) (integer) 3
2) (integer) 4
127.0.0.1:6379>

4.4.4.2 回滚

day044 Redis 第二天_第52张图片

4.4.4.3 失败命令

day044 Redis 第二天_第53张图片

5 redis持久化(了解)

5.1 概述

Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。

Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。

1、 RDB持久化(默认支持,无需配置)

该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘。

2、 AOF持久化

该机制将以日志的形式记录服务器所处理的每一个写操作,在Redis服务器启动之初会读取该文件来重新构建数据库,以保证启动后数据库中的数据是完整的。

3、 无持久化

我们可以通过配置的方式禁用Redis服务器的持久化功能,这样我们就可以将Redis视为一个功能加强版的memcached了。

4、 redis可以同时使用RDB和AOF

5.2 RDB

5.2.1 优势

1、 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

2、 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上

3、 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork(分叉)出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

4、 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

5.2.2 劣势

1、 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

2、 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟

5.2.3 配置说明Snapshotting

5.2.3.1 快照参数设置

 save 900 1 #每900秒(15分钟)至少有1个key发生变化,则dump内存快照。

 save 300 10 #每300秒(5分钟)至少有10个key发生变化,则dump内存快照

 save 60 10000 #每60秒(1分钟)至少有10000个key发生变化,则dump内存快照

day044 Redis 第二天_第54张图片

day044 Redis 第二天_第55张图片

5.2.3.2 保存位置设置

day044 Redis 第二天_第56张图片

day044 Redis 第二天_第57张图片

5.3 AOF

5.3.1 优势

1、 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。

2、 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。

3、 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4、 AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

5.3.2 劣势

1、 对于相同数量的数据集而言,AOF文件通常要大于RDB文件

2、 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

5.3.3 配置AOF

5.3.3.1 配置信息

 always    #每次有数据修改发生时都会写入AOF文件

 everysec  #每秒钟同步一次,该策略为AOF的缺省策略

 no       #从不同步。高效但是数据不会被持久化

重写AOF:若不满足重写条件时,可以手动重写,命令:bgrewriteaof

day044 Redis 第二天_第58张图片

策略的选择:

day044 Redis 第二天_第59张图片

5.3.3.2 数据恢复演示

1、 flushall操作  清空数据库

2、 及时关闭redis服务器(防止dump.rdb)。  shutdown nosave

3、 编辑aof文件,将日志中的flushall命令删除并重启服务即可

 步骤1:开启aop,并设置成总是保存。然后重启redis。

day044 Redis 第二天_第60张图片

 步骤2:在窗口1进行若干操作

day044 Redis 第二天_第61张图片

 步骤3:在窗口1,清空数据库

image.png

 步骤4:在窗口2,关闭redis

day044 Redis 第二天_第62张图片

 步骤5:修改“appendonly.aof”文件,将最后的命令“flushall”删除

image.png

 步骤6:在窗口1启动redis,然后查询数据库内容

day044 Redis 第二天_第63张图片

6 redis使用场景(了解)

1、 取最新N个数据的操作  

比如典型的取你网站的最新文章,通过下面方式,我们可以将最新的5000条评论的ID放在Redis的List集合中,并将超出集合部分从数据库获取  

(1)使用LPUSH latest.comments 命令,向list集合中插入数据  

(2)插入完成后再用LTRIM latest.comments 0 5000命令使其永远只保存最近5000个ID  

(3)然后我们在客户端获取某一页评论时可以用下面的逻辑(伪代码)

# 伪代码

 

FUNCTION get_latest_comments(start, num_items):

   id_list = redis.lrange("latest.comments", start, start+num_items-1)

   IF id_list.length < num_items

       id_list = SQL_DB("SELECT ... ORDER BY time LIMIT ...")

   END

   RETURN id_list

END

如果你还有不同的筛选维度,比如某个分类的最新N条,那么你可以再建一个按此分类的List,只存ID的话,Redis是非常高效的。

2、 排行榜应用,取TOP N操作

这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出马了,将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,每次只需要执行一条ZADD命令即可。

3、 需要精准设定过期时间的应用

比如你可以把上面说到的sorted set的score值设置成过期时间的时间戳,那么就可以简单地通过过期时间排序,定时清除过期数据了,不仅是清除Redis中的过期数据,你完全可以把 Redis里这个过期时间当成是对数据库中数据的索引,用Redis来找出哪些数据需要过期删除,然后再精准地从数据库中删除相应的记录。

4、 计数器应用

Redis的命令都是原子性的,你可以轻松地利用INCR,DECR命令来构建计数器系统。

5、 Uniq操作,获取某段时间所有数据排重值

这个使用Redis的set数据结构最合适了,只需要不断地将数据往set中扔就行了,set意为集合,所以会自动排重。

6、 实时系统,反垃圾系统

通过上面说到的set功能,你可以知道一个终端用户是否进行了某个操作,可以找到其操作的集合并进行分析统计对比等。没有做不到,只有想不到。

7、 Pub/Sub构建实时消息系统

Redis的Pub/Sub系统可以构建实时的消息系统,比如很多用Pub/Sub构建的实时聊天系统的例子。

8、 构建队列系统

使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

7 附

7.1 redis.conf 配置详情

Redis 支持很多的参数,但都有默认值。

参数名称

描述

daemonize

默认情况下,redis 不是在后台运行的,如果需要在后台运行,把该项的值更改为yes

pidfile

当Redis 在后台运行的时候,Redis 默认会把pid 文件放在/var/run/redis.pid,你可以配置到其他地址。当运行多个redis 服务时,需要指定不同的pid 文件和端口

bind

指定Redis 只接收来自于该IP 地址的请求,如果不进行设置,那么将处理所有请求,在生产环境中最好设置该项

port

监听端口,默认为6379

timeout

设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接

loglevel

log 等级分为4 级,debug, verbose, notice, 和warning。生产环境下一般开启notice

logfile

配置log 文件地址,默认使用标准输出,即打印在命令行终端的窗口上

databases

设置数据库的个数,可以使用SELECT 命令来切换数据库。默认使用的数据库是0

save

设置Redis 进行数据库镜像的频率。

if(在60 秒之内有10000 个keys 发生变化时){

进行镜像备份

}else if(在300 秒之内有10 个keys 发生了变化){

进行镜像备份

}else if(在900 秒之内有1 个keys 发生了变化){

进行镜像备份

}

rdbcompression

在进行镜像备份时,是否进行压缩

dbfilename

镜像备份文件的文件名

dir

数据库镜像备份的文件放置的路径。这里的路径跟文件名要分开配置是因为Redis 在进行备份时,先会将当前数据库的状态写入到一个临时文件中,等备份完成时,再把该该临时文件替换为上面所指定的文件,而这里的临时文件和上面所配置的备份文件都会放在这个指定的路径当中

slaveof

设置该数据库为其他数据库的从数据库

masterauth

当主数据库连接需要密码验证时,在这里指定

requirepass

设置客户端连接后进行任何其他指定前需要使用的密码。警告:因为redis 速度相当快,所以在一台比较好的服务器下,一个外部的用户可以在一秒钟进行150K 次的密码尝试,这意味着你需要指定非常非常强大的密码来防止暴力破解。

maxclients

限制同时连接的客户数量。当连接数超过这个值时,redis 将不再接收其他连接请求,客户端尝试连接时将收到error 信息。

maxmemory

设置redis 能够使用的最大内存。当内存满了的时候,如果还接收到set 命令,redis 将先尝试剔除设置过expire 信息的key,而不管该key 的过期时间还没有到达。在删除时,将按照过期时间进行删除,最早将要被过期的key 将最先被删除。如果带有expire 信息的key 都删光了,那么将返回错误。这样,redis 将不再接收写请求,只接收get 请求。maxmemory 的设置比较适合于把redis 当作于类似memcached 的缓存来使用。

appendonly

默认情况下,redis 会在后台异步的把数据库镜像备份到磁盘,但是该备份是非常耗时的,而且备份也不能很频繁,如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失。所以redis 提供了另外一种更加高效的数据库备份及灾难恢复方式。开启append only 模式之后,redis 会把所接收到的每一次写操作请求都追加到appendonly.aof 文件中,当redis 重新启动时,会从该文件恢复出之前的状态。但是这样

会造成appendonly.aof 文件过大,所以redis 还支持了BGREWRITEAOF 指令,对appendonly.aof 进行重新整理。所以我认为推荐生产环境下的做法为关闭镜像,开启appendonly.aof,同时可以选择在访问较少的时间每天对appendonly.aof 进行重写一次。

appendfsync

设置对appendonly.aof 文件进行同步的频率。always 表示每次有写操作都进行同步,everysec 表示对写操作进行累积,每秒同步一次。这个需要根据实际业务场景进行配置

vm-enabled

是否开启虚拟内存支持。因为redis 是一个内存数据库,而且当内存满的时候,无法接收新的写请求,所以在redis 2.0 中,提供了虚拟内存的支持。但是需要注意的是,redis中,所有的key 都会放在内存中,在内存不够时,只会把value 值放入交换区。这样保证了虽然使用虚拟内存,但性能基本不受影响,同时,你需要注意的是你要把vm-max-memory 设置到足够来放下你的所有的key

vm-swap-file

设置虚拟内存的交换文件路径

vm-max-memory

这里设置开启虚拟内存之后,redis 将使用的最大物理内存的大小。默认为0,redis 将把他所有的能放到交换文件的都放到交换文件中,以尽量少的使用物理内存。在生产环境下,需要根据实际情况设置该值,最好不要使用默认的0

vm-page-size

设置虚拟内存的页大小,如果你的value 值比较大,比如说你要在value 中放置博客、新闻之类的所有文章内容,就设大一点,如果要放置的都是很小的内容,那就设小一点。

vm-pages

设置交换文件的总的page 数量,需要注意的是,page table 信息会放在物理内存中,每8 个page 就会占据RAM 中的1 个byte。总的虚拟内存大小 = vm-page-size * vm-pages

vm-max-threads

设置VM IO 同时使用的线程数量。因为在进行内存交换时,对数据有编码和解码的过程,所以尽管IO 设备在硬件上本上不能支持很多的并发读写,但是还是如果你所保存的vlaue 值比较大,将该值设大一些,还是能够提升性能的

glueoutputbuf

把小的输出缓存放在一起,以便能够在一个TCP packet 中为客户端发送多个响应,具体原理和真实效果我不是很清楚。所以根据注释,你不是很确定的时候就设置成yes

hash-max-zipmap-entries

在redis 2.0 中引入了hash 数据结构。当hash 中包含超过指定元素个数并且最大的元素没有超过临界时,hash 将以一种特殊的编码方式(大大减少内存使用)来存储,这里可以设置这两个临界值

activerehashing

开启之后,redis 将在每100 毫秒时使用1 毫秒的CPU 时间来对redis 的hash 表进行重新hash,可以降低内存的使用。当你的使用场景中,有非常严格的实时性需要,不能够接受Redis 时不时的对请求有2 毫秒的延迟的话,把这项配置为no。如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存

7.2 启动多个Redis

 方法1:启动时指定端口可在一台服务器启动多个Redis进程。(多个Redis实例)

cd /usr/local/redis/bin
./redis-server ./redis.conf --port 6380

image.png

 方式2:复制redis目录,然后编写redis.conf修改端口【推荐使用】

 步骤1:拷贝redis目录

cp -r redis/ redis6380

day044 Redis 第二天_第64张图片

 步骤2:修改redis.conf文件

cd redis6380/
vim redis.conf

image.png

image.png

 步骤3:启动多个redis

cd /usr/local/
./redis/bin/redis-server ./redis/redis.conf         
./redis6380/bin/redis-server ./redis6380/redis.conf 
ps aux | grep -i redis           

image.png

 关闭指定端口号的Redis

./bin/redis-cli -p 6380 shutdown

image.png

 

你可能感兴趣的:(Java课堂笔记,redis)