手把手教你如何玩转Redis入门

情景引入

小白:起床起床,,快快快快起床。。。
我: 怎么了就。。。又大惊小怪的。
小白:我发现mysql数据库的存储形式好单一。
我: 本身它就是这样进行设计的,要不然为什么要叫它关系型数据库呢?要不然它的底层怎么进行索引处理以及查询优化呢?
小白: 那有没有什么其他的数据库是非关系的呢?
我: 你说到这个的话,你的意思是指NoSQL吗?
小白: 咦,好像听说过这个名词。。是不是最近挺火的呢。
我: 是的,NoSQL就是一种新型的存储方式的数据库,它与mysql这种数据库的存储方式还是存在很多的区别的。
小白: 那redis是否是属于这一种NoSQL呢?
我: 对的,redis就是其中的代表之一。当然,还有很多其他的啦。
小白: 哇塞,,那你给我讲讲这块的知识呗。我想学,我想学。。。
我: 好吧。看着你这样积极,我又来给你上一课。好好听哦~!
小白: 好的,小板凳已经搬来了~

情景分析

在现在技术的飞速发展中,越来越多的新兴技术发展起来,这也正是催促着我们不断学习的动力。在上面小白说的想解决Mysql这类关系型数据库的弊端来说,一种NoSQL数据库确实可以弥补它的存储单一的问题。但是,我们要记住,它们两者之后都有好坏,优势和弊端,这完全要基于我们的使用场景来进行分析的哦。在这篇博文中,我就主要带大家进行redis数据库的入门学习。

什么叫做NoSQL?

关系型数据库是基于关系表的数据库,最终会将数据持久化到磁盘上,而nosql数据 库是基于特殊的结构,并将数据存储到内存的数据库。从性能上而言,nosql数据库 要优于关系型数据库,从安全性上而言关系型数据库要优于nosql数据库,所以在实 际开发中一个项目中nosql和关系型数据库会一起使用,达到性能和安全性的双保证。
NoSQL(NoSQL = Not Only SQL ),意即”不仅仅是SQL”。
在现代的计算系统上每天网络上都会产生庞大的数据量。
这些数据有很大一部分是由关系数据库管理系统(RDBMS)来处理。 1970年 E.F.Codd’s提出的关系模型的论文 “A relational model of data for large shared data banks”,这使得数据建模和应用程序编程更加简单。
通过应用实践证明,关系模型是非常适合于客户服务器编程,远远超出预期的利益,今天它是结构化数据存储在网络和商务应用的主导技术。
NoSQL 是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。

为什么要使用Nosql?

今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL数据库的发展也却能很好的处理这些大的数据。

RDBMS和Nosql之间的对比

RDBMS
- 高度组织化结构化数据
- 结构化查询语言(SQL) (SQL)
- 数据和关系都存储在单独的表中。
- 数据操纵语言,数据定义语言
- 严格的一致性
- 基础事务

NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
-键 - 值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理
- 高性能,高可用性和可伸缩性

Nosql数据库分类

这里写图片描述

Redis简介

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

(一)Linux环境安装redis

说明:为什么要把redis安装在linux系统中呢?

对于redis来说的话,我们一般都是安装在linux环境中,主要是根据它的作用来的。试想,我们的服务一般都是部署在linux环境当中,这样也比较安全和运行效率高。所以,针对这样的原因,非常不介意把redis用于windows环境,当然,如果你想这样做也是可以的,可以下载windows中操作redis的界面工具即可。但是在这里的话,我主要是介绍说明,如何在linux环境安装redis哦!

环境:linux(ubuntu系统)

说明:
(1)因为,我自身习惯用了Ubuntu系统,所以,这里主要介绍的就是安装在这样的系统当中。如果你喜欢用Centos系统也当然是没问题的(只是安装的命令中,有些地方不是一样),换汤不换药嘛。
(2)我这里是讲解直接在linux系统的电脑上进行的操作哦。如果你有linux的云系统,那么就需要在windows系统中用到SSH工具,连接你的云系统即可。操作都是类似的,只不过,我这里拷贝文件什么的都可以直接进行,而你们就需要用到文件传输工具(比如:filezilla工具)传到linux系统而已。这都是基本操作了,就不属于我的讲解,如果不是很明白,欢迎查看我的另外一篇博文。
https://blog.csdn.net/cs_hnu_scw/article/details/79125582

步骤:

  1. 将redis安装包,拷贝到linux系统。(这个安装包,我会在最后和源代码一起进行打包放在云盘中)
  2. 解压我们刚刚上传的压缩包
    通过命令:tar -xvf redis-3.0.0tar.gz ——-如果你需要指定目录,那么后面再添加目录的路径即可。我这样的话,就是直接默认解压到和我压缩包目录一样的地方。
  3. 在linux系统中,安装C/C++编译环境。
    说明:为什么安装redis要按照C/C++环境呢?因为redis是主要通过C语言进行编写和编译的,里面有很多都是基于C的环境,所以必须确保我们的系统中有这样的环境,如果没有的话,在后面的编译redis中会出现问题。
    通过命令:
sudo apt-get install vim g++ openssh-server libgl1-mesa-dev
  1. 进入我们压缩后的redis文件目录下,并直接执行命令:make
    一定要确保是在这个解压后的目录并且linux系统有C编译环境,运行上面的编译命令,否则会出错
  2. 继续再该目录下面运行下面的命令:
make PREFIX=/home/scw/scw/redis/redisdown install

说明:这其中的/home/scw/scw/redis/redisdown 是我需要把redis存放的目录,所以,这个完全由你们自己决定放在哪即可。
6.拷贝我们解压文件目录中的redis.conf文件到redis安装目录的bin文件目录下(也就是第五步中我们设置的目录中后生成的bin文件下面)。
- 修改redis.conf文件中的下面内容
说明:修改这个内容主要是因为避免我们像通过命令行运行tomcat一样,运行了那个命令行就不能继续执行其他的操作了。所以,通过修改下面的内容,可以让redis运行之后处于后台运行状态,这样就可以继续操作这个终端窗口。
当然,大家可以对比一下,不修改和修改的区别!!!!

redis.conf文件中的daemonize从no修改成yes表示后台启动
  • 在redis的文件下的bin目录中运行下面的命令:
./redis-server redis.conf

说明:通过这个命令,如果安装顺利的话,那么就可以显示redis启动成功了。
- 通过下面的命令可以查看是否redis安装成功:

ps -ef |grep redis

说明:如果是开启的话,那么就会显示redis服务对应的启动窗口的端口号等信息。
- 另外,还可以通过允许redis客户端的方式来判断是否启动成功。
通过命令开启redis客户端(同理:还是在开启服务的那么bin目录下面执行):

./redis-cli

说明:当我们执行完这个命令之后,我们在命令行中就会看到类似下面的这样的语句:

127.0.0.1:6379>

说明:这就表示我们进行了客户端啦,,那么我们就可以进行我们后续的操作了。
比如执行:

set name scw
然后
get name
就会发现:打印出来我们存入的东西scw了。。

说明:其实上面的代码就表示我们已经在进行redis相关的命令操作了。。哈哈。是不是很简单呢?对的,就是这么简单,这就是最基本以及最重要的操作字符串的命令。后续,我会继续说的。。。
顺便再说一下:我们直接crtl + C 就可以退出客户编辑环境了哦。。
- OK,到这里就完成了基本的redis的配置和操作了哦。。。。
特别说明:

  • 如果你是采取的SSH方式连接的linux系统,那么首先要确保linux系统是支持了SSH服务的,否则你通过ssh工具是无法连接,会提示
the remote refused connected

那么,我们就要在linux系统先安装ssh服务。通过下面的命令即可:

sudo apt-get install openssh-server

另外,可以通过命令查看是否安装成功:

ps -e|grep ssh
如果安装成功的话,就可以看到这个ssh服务对应的开启端口等信息。
  • 要确保我们的服务器是支持22端口的,这也是ssh连接的端口。如果这个端口未被允许的话,那么自然而然是无法连接ssh的。
  • 说到端口,那么因为redis是默认使用6379端口的,所以,要实现redis的服务,我们还要保证系统对这个端口是进行支持的。
/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT
/etc/rc.d/init.d/iptables save
  • 我们还要确保我们系统中的防火墙的一些处理,因为防火墙是可以防止我们有些远程操作的,所以,这个也是我们需要注意的地方。

(二)使用Java操作Redis

说明:对于Java语言进行操作Redis,我们一般采取使用Jedis进行。所以,我们需要提前导入下面的两个jar包。
这里写图片描述

具体步骤如下:

  1. 导入上面提及的两个jar包
  2. 编写代码,测试Redis环境是否连接
/**
     * 测试redis安装环境是否成功
     */
    @Test
    public void test(){
        //1:连接redis
        Jedis jedis = new Jedis("49.123.64.145", 6379);
        //2:获取数据
        String value = jedis.get("name");
        System.out.println(value);
        // 3、存储
        jedis.set("addr", "长沙");
        System.out.println(jedis.get("addr"));
    }
  1. 通过jedis的连接池获取jedis对象
// 通过jedis的pool获得jedis连接对象
    @Test
    public void testJedisPool() {
        // 0、创建池子的配置对象
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(30);// 最大闲置个数
        poolConfig.setMinIdle(10);// 最小闲置个数
        poolConfig.setMaxTotal(50);// 最大连接数

        // 1、创建一个redis的连接池
        JedisPool pool = new JedisPool(poolConfig, "49.123.64.145", 6379);

        // 2、从池子中获取redis的连接资源
        Jedis jedis = pool.getResource();

        // 3、操作数据库
        jedis.set("hello", "kugou");
        System.out.println(jedis.get("hello"));

        // 4、关闭资源
        jedis.close();
        pool.close();

    }
  1. 封装JedisPoolUtils操作redis的相关工具类
package com.hnu.sc.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**  
 * <p>Title: JedisPoolUtils.java</p>  
 * <p>Description: 
 * 测试redis池的工具类</p>  
 * <p>Company: 湖南大学</p>  
 * @author scw  
 * @date 2018年6月29日  
 */
public class JedisPoolUtils {
    private static JedisPool pool = null;

    static {

        // 加载配置文件
        InputStream in = JedisPoolUtils.class.getClassLoader()
                .getResourceAsStream("redis.properties");
        Properties pro = new Properties();
        try {
            pro.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 获得池子对象
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(Integer.parseInt(pro.get("redis.maxIdle")
                .toString()));// 最大闲置个数
        poolConfig.setMinIdle(Integer.parseInt(pro.get("redis.minIdle")
                .toString()));// 最小闲置个数
        poolConfig.setMaxTotal(Integer.parseInt(pro.get("redis.maxTotal")
                .toString()));// 最大连接数
        pool = new JedisPool(poolConfig, pro.getProperty("redis.url"),
                Integer.parseInt(pro.get("redis.port").toString()));
    }

    // 获得jedis资源的方法
    public static Jedis getJedis() {
        return pool.getResource();
    }

    public static void main(String[] args) {
        Jedis jedis = getJedis();
        System.out.println(jedis.get("name"));
    }
}

说明:上面用到了properties文件来进行灵活性的管理我们redis的配置环境,所以需要用到下面的properties文件

redis.maxIdle=30
redis.minIdle=10
redis.maxTotal=100
redis.url=49.123.64.145
redis.port=6379

分析:上面就是对Java如何操作redis的简单使用,其实还是比较容易理解的。我们可以先简单的把redis的操作看成是对Map集合的一种相似处理(当然,并不是一样,简化的来理解先)。

(三)Redis中的value支持的数据类型

redis是一种高级的key-value的存储系统
其中的key是字符串类型,尽可能满足如下几点:
1)key不要太长,最好不要操作1024个字节,这不仅会消耗内存还会降低查找 效率
2)key不要太短,如果太短会降低key的可读性
3)在项目中,key最好有一个统一的命名规范(根据企业的需求)
其中value 支持五种数据类型:

1)字符串型 string
2)字符串列表 lists
3)字符串集合 sets
4)有序字符串集合 sorted sets
5)哈希类型 hashs

好了,下面我就逐一的来进行简单讲解。

1. 存储字符串string类型(简单,并且非常常用)
说明:
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。 在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
具体语法如下:
(1)set key value:设定key持有指定的字符串value,如果该key存在则进行覆盖 操作。总是返回”OK”
(2)get key:获取key的value。如果与该key关联的value不是String类型,redis 将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返 回null。
如下所示:
这里写图片描述
(3)getset key value:先获取该key的值,然后在设置该key的值。
这里写图片描述
(4)incr key:将指定的key的value原子性的递增1.如果该key不存在,其初始值 为0,在incr之后其值为1。如果value的值不能转成整型,如hello,该操作将执 行失败并返回相应的错误信息。
(5)decr key:将指定的key的value原子性的递减1.如果该key不存在,其初始值 为0,在incr之后其值为-1。如果value的值不能转成整型,如hello,该操作将执 行失败并返回相应的错误信息。
这里写图片描述
(6)incrby key increment:将指定的key的value原子性增加increment,如果该 key不存在,器初始值为0,在incrby之后,该值为increment。如果该值不能转成 整型,如hello则失败并返回错误信息
(7)decrby key decrement:将指定的key的value原子性减少decrement,如果 该key不存在,器初始值为0,在decrby之后,该值为decrement。如果该值不能 转成整型,如hello则失败并返回错误信息。
这里写图片描述
(8)append key value:如果该key存在,则在原有的value后追加该值;如果该 key 不存在,则重新创建一个key/value
这里写图片描述
2. 存储list类型
说明:
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表 一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不 存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移 除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是 4294967295。
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将 会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间 内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是 非常低效的。相信对于有良好数据结构基础的开发者而言,这一点并不难理解。
(1)lpush key value1 value2…:在指定的key所关联的list的头部插入所有的 values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链 表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。
(2)rpush key value1、value2…:在该list的尾部添加元素
(3)lrange key start end:获取链表中从start到end的元素的值,start、end可 为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推…
这里写图片描述
(4)lpushx key value:仅当参数中指定的key存在时(如果与key管理的list中没 有值时,则该key是不存在的)在指定的key所关联的list的头部插入value。
(5)rpushx key value:在该list的尾部添加元素
这里写图片描述
(6)lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素。
(7)rpop key:从尾部弹出元素。
这里写图片描述
(8)rpoplpush resource destination:将链表中的尾部元素弹出并添加到头部
这里写图片描述
(9)llen key:返回指定的key关联的链表中的元素的数量。
这里写图片描述
(10)lset key index value:设置链表中的index的脚标的元素值,0代表链表的头元 素,-1代表链表的尾元素。
这里写图片描述
(11)lrem key count value:删除count个值为value的元素,如果count大于0,从头向尾遍历并删除count个值为value的元素,如果count小于0,则从尾向头遍历并删除。如果count等于0,则删除链表中所有等于value的元素。
这里写图片描述
(12)linsert key before|after pivot value:在pivot元素前或者后插入value这个元素。
这里写图片描述
3. 存储set类型。
说明:
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我 们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要 说明的是,这些操作的时间是常量时间。Set可包含的最大元素数是4294967295。
和List类型不同的是,Set集合中不允许出现重复的元素。和List类型相比,Set类 型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计 算操作,如unions、intersections和differences。由于这些操作均在服务端完成, 因此效率极高,而且也节省了大量的网络IO开销。
(1)sadd key value1、value2…:向set中添加数据,如果该key的值已有则不会 重复添加
(2)smembers key:获取set中所有的成员
(3)scard key:获取set中成员的数量
这里写图片描述
(4)sismember key member:判断参数中指定的成员是否在该set中,1表示存 在,0表示不存在或者该key本身就不存在
(5)srem key member1、member2…:删除set中指定的成员
这里写图片描述
(6)srandmember key:随机返回set中的一个成员
这里写图片描述
(7)sdiff sdiff key1 key2:返回key1与key2中相差的成员,而且与key的顺序有 关。即返回差集
这里写图片描述
(8)sdiffstore destination key1 key2:将key1、key2相差的成员存储在 destination上
这里写图片描述
(9)sinter key[key1,key2…]:返回交集
(10)sinterstore destination key1 key2:将返回的交集存储在destination上
这里写图片描述
(11)sunion key1、key2:返回并集
这里写图片描述
(12)sunionstore destination key1 key2:将返回的并集存储在destination上
这里写图片描述
4. 存储有序字符串sorted sets类型
说明:
Sorted-Sets和Sets类型极为相似,它们都是字符串的集合,都不允许重复的成员出 现在一个Set中。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分 数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然 而需要额外指出的是,尽管Sorted-Sets中的成员必须是唯一的,但是分数(score) 却是可以重复的。
在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为 集合中成员数量的对数。由于Sorted-Sets中的成员在集合中的位置是有序的,因此, 即便是访问位于集合中部的成员也仍然是非常高效的。事实上,Redis所具有的这一 特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和Redis 同样的高效,在其它数据库中进行建模是非常困难的。
例如:游戏排名、微博热点话题等使用场景。
(1)zadd key score member score2 member2 … :将所有成员以及该成员的 分数存放到sorted-set中
(2)zcard key:获取集合中的成员数量
这里写图片描述
(3)zcount key min max:获取分数在[min,max]之间的成员
(4)zincrby key increment member:设置指定成员的增加的分数
(5)zrange key start end [withscores]:获取集合中脚标为start-end的成员,[withscores]参数表明返回的成员包含其分数
(6)zrangebyscore key min max [withscores] [limit offset count]:返回分数在[min,max]的成员并按照分数从低到高排序。[withscores]:显示分数;[limit offset count]:offset,表明从脚标为offset的元素开始并返回count个成员。
(7)zrank key member:返回成员在集合中的位置
(8)zrem key member[member…]:移除集合中指定的成员,可以指定多个成员
(9)zscore key member:返回指定成员的分数
5. 存储哈希类型hash
说明:
Redis中的Hashes类型可以看成具有String Key和String Value的map容器。所 以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果 Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash 可以存储4294967295个键值对。
(1)hset key field value:为指定的key设定field/value对(键值对)
(2)hgetall key:获取key中的所有filed-vaule
这里写图片描述
(3)hget key field:返回指定的key中的field的值
这里写图片描述
(4)hmset key fields:设置key中的多个filed/value
(5)hmget key fileds:获取key中的多个filed的值
(6)hexists key field:判断指定的key中的filed是否存在
(7)hlen key:获取key所包含的field的数量
(8)hincrby key field increment:设置key中filed的值增加increment,如:age增加20

(四)keys的通用操作

(1)key pattern:获取所有与pattern匹配的key,返回所有与该key匹配的key。*表示任意一个或者多个字符,?表示任意一个字符
(2)del key1 key2 :删除指定的key
(3)exists:判断该key是否存在,1表示存在,0表示不存在
(4)rename key newkey:为当前的key重命名
(5)expire key:设置过期时间,单位,秒
(6)ttl key:获取该key所剩的超时事件,如果没有设置超时,返回-1,如果返回-2,表示超时不存在。
(7)select 数据库下标 :选择redis的数据库,默认是选择的0数据库,redis一共16个数据库,下标从0到15
(8)move key 数据库下标:将key移动到另外一个数据库中
(9)ping:测试连接是否存活
(10)quit:退出连接
(11)dbsize:返回当前数据库中key的数目
(12)info:获取服务器的信息和统计
(13)flushdb:删除当前选择数据库中的所有key
(14)flushall:删除所有数据库中的所有key(慎重使用)

(五)Redis的事务

redis中的事务是一组redis命令的集合,开启redis事务后,这组redis命令会当做一个原子操作被redis执行。
通过将一个个redis命令发送给redis,再由redis执行这些命令。发送给redis的这组命令并不会立即执行,只有当客户端通知redis可以执行了才会开始执行并将执行结果返回客户端。
与关系数据库中可以回滚的事务不同在于,redis事务中被MULTI和EXEC包裹的命令会一个接一个地执行,直到所有命令都执行完毕。当一个事务处理完毕,才会处理另一个客户端的命令。
也就是说,redis事务是针对一个客户端而言的,所以在redis分片部署模式下是不能处理事务的。

问题:如何进行redis的事务操作呢?
包括MULTI、EXEC、WATCH、UNWATCH和DISCARD。
MULTI:开始一个事务
EXEC:提交事务执行
WATCH:监视一个或多个key的变化,一旦有一个key发生了变化,之后的事务就不会执行,监视持续到EXEC命令
UNWATCH:取消WATCH命令对key的监视。每个客户端只会取消自己WATCH的key,而不会对其他客户端有影响
DISCARD:取消WATCH命令对key的监视并清空已入队命令

(六)Redis持久化

说明:什么叫做持久化?
Redis的高性能是由于其将所有数据都存储在内存中,为了使redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis持久化,主要是两种方式,一种是RDB方式,一种是AOF,可以单独使用其中一种或者两者结合使用。

方式一:RDB持久化(默认支持,无需配置)

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

RDB的执行过程

  • redis调用fork,现在有了子进程和父进程。
  • 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
  • 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。
    注意事项:
    (1)client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。
    (2)另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

RDB的优势

  • 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样非常方便进行备份。比如你可能打算每一天归档一些数据。
  • 方便备份,我们可以很容易的将一个一个RDB文件移动到其他的存储介质上。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  • RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。

RDB的缺点

  • 如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。

RDB的配置事项

(1)首先,我们查看redis.conf文件,找出如下内容

save 900 1     #900秒内如果超过1个key被修改,则发起快照保存
save 300 10    #300秒内容如果超过10个key被修改,则发起快照保存
save 60 10000  #60秒内容如果超过10000个key被修改,则发起快照保存

(2)查看我们快照保存的位置,找到如下内容
这里写图片描述

方法二:AOF持久化(需手动开启)

说明:
redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。

AOF的过程

  • redis调用fork ,现在有父子两个进程
  • 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
  • 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
  • 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
  • 注意事项:
    需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

AOF的优势

  • 使用 AOF 持久化会让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。
  • AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF的缺点

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

AOF的配置事项

(1)AOF默认关闭,开启方法,修改配置文件reds.conf:appendonly yes

##此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能  
##只有在“yes”下,aof重写/文件同步等特性才会生效  
appendonly yes  

##aof文件的存放路径与文件名称
appendfilename appendonly.aof 

(2)修改redis.conf文件中AOF的同步策略,主要有以下三种:

##指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec 
##always 每一个命令,都立即同步到aof文件中去(很安全,但是速度慢,因为每一个命令都会进行一次磁盘操作)IO开支较大。
##everysec每秒将数据写一次到aof文件,redis推荐的方式。如果遇到物理服务器故障,有可能导致最近一秒内aof记录丢失(可能为部分丢失)。
##no 将写入工作交给操作系统,由操作系统来判断缓冲区大小,统一写到aof文件(速度快,但是同步频率低,容易丢数据)
# appendfsync always 
 appendfsync everysec
#  appendfsync no

(七)Redis的使用场景

说明:
主要是介绍一下Redis目前用得比较多的一些场景,当然不只这些,还有其他的很多地方都有用到redis这数据库,所以,主要是做到抛砖引玉的一个作用。
1. 取最新N个数据的操作。比如:典型的取网站的最新文章
2. 排行榜应用,取TopN操作。
说明:这个需求与上面需求的不同之处在于,前面操作以时间为权重,这个是以某个条件为权重,比如按顶的次数,这时候我们可以用到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构建的实时聊天系统。
pub/sub就是redis中的订阅和推送服务。(这个redis自带有这个服务,主要是对不同的频道进行消息处理。)
8. 构建队列系统
说明:使用list可以构建队列系统,使用sorted set甚至可以构建有优先级的队列系统。

(八)总结

(1)本篇博文主要是讲解Redis的相关的知识,比如起源,性质以及与NoSQL的关系。
(2)本篇博文主要还讲解了关于 Redis的安装,以及相关的简单操作,当然,这只是一个入门级的教程,如果想熟悉操作Redis还是需要花费一些时间去研究的。
(3)本篇还主要讲解了Redis里面非常重要的事务和数据持久化的服务。
(4)本篇主要对Redis的使用场景也简单描述了一下,在当今的一个比较大的系统中,都肯定会有用到redis相关的内容,所以还是值得我们进行研究的。另外,Redis对于数据缓存来减少数据库的压力也是非常重要的特性哦~~

关于本博文相关的资料都放在下面的百度云盘中:
链接:https://pan.baidu.com/s/1vsd0lxh9WMre_PwoHq8SHg 密码:3ovr

猜你喜欢

转载自blog.csdn.net/cs_hnu_scw/article/details/80856316