【分布式架构之旅-理论篇】Redis入门

前言

昨天和室友去包夜,玩了一晚上的LOL,跪了一整夜,但是很开心。从S1末开始玩LOL的我,到现在还是青铜,真是菜的抠脚。最近负能力满满的,唯有睡觉和学习才可解忧愁。今天也看了慕课网上面的《Redis入门》,来记一下学习笔记。(写这篇文章开头的时候应该是一个星期之前)

DNA.png

NoSQL概述

  • NoSQL就是Not Only SQL的意思,是非关系型数据库。

  • 为什么需要NoSQL?

    • High performance - 高并发读写

    • Huge Storage - 海量数据的高效率存储和访问

    • High Scalability & High Availability - 高可扩展性和高可用性

  • NoSQL数据库的四大分类

    • 键值(Key - Value)存储:优点是快速查询,缺点存储的数据缺少结构化。

    • 列存储:优点是查询比较快,扩展性比较强,缺点是功能相对局限。

    • 文档数据库:对应的产品就是MongoDB。对数据结构要求不是特别严格,查询性能不能特别高,缺少统一查询的语法。

    • 图形数据库:优点是利用图结构相关的算法,缺点是需要对整个图进行结算才能得出结果,不能作为分布式的解决方案。

image.png
  • 现在来说说NoSQL的特点,美滋滋。
    • 易扩展
    • 灵活的数据模型
    • 高可用
    • 大数据量,高性能

Redis的概述

  • 高性能键值对数据库,支持的键值数据类型:

    • 字符串类型 - String

    • 列表类型 - Set

    • 有序集合类型 - Sorted Set

    • 散列类型 - Hash

    • 集合类型 - List

  • Redis的应用场景

    • 缓存

    • 任务队列

    • 网站访问统计

    • 应用排行榜

    • 分布式集群架构中的session分离


Redis在Linux上的使用

可以看我这篇文章【Linux学习】 Redis常用的一些指令


Jedis的入门

  • 我们要在Java平台上使用redis,肯定需要Jedis这个客户端。首先在pom文件中引入Jedis的依赖
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
  • 普通方式创建Jedis对象。
    public void methodOne() {
        Jedis jedis = new Jedis("100.64.84.47", 6379);
        jedis.set("name", "cmazxiaoma");
        String value = jedis.get("name");
        System.out.println(value);
        jedis.close();
    }
  • 通过线程安全的连接池来创建Jedis对象。
  public void methodTwo() {
        //获得连接池的配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        //设置最大连接数
        config.setMaxTotal(30);
        //设置最大空闲连接数
        config.setMaxIdle(10);
        //获得连接池
        JedisPool jedisPool = new JedisPool(config, "100.64.84.47", 6379);

        Jedis jedis = null;

        try {
            jedis = jedisPool.getResource();
            String value = jedis.get("name");
            System.out.println(value);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }

            jedisPool.close();
        }
    }
}
  • 编写测试类。
public class JedisDemo1Test {
    private JedisDemo1 demo;

    @Before
    public void setUp() {
        demo = new JedisDemo1();
    }

    @Test
    public void methodOne() throws Exception {
        demo.methodOne();
    }

    @Test
    public void methodTwo() throws Exception {
        demo.methodTwo();
    }

}
  • 运行Test Case,测试成功。

    image.png

  • 我们在Xshell软件,输入get name指令,也可以看到输出cmazxiaoma

    image.png


Redis的数据结构

String

  • Key定义的注意点:

    • 不要过长。
    • 不要过短。
    • 统一的命名规范。
  • 存储String

    • 二进制安全的,存入和获取的数据相同。
    • Value最多可以容纳的数据长度是512M
  • 存储String常用的命令:

    • 赋值


      image.png
    • 取值


      image.png
    • 删除


      image.png
    • 数值增减

如果属性不存在的话,那么integer类型默认为0

image.png

如果name属性的值不能转换成integer类型,那么会抛出ERR is not an integer or out of range异常。

image.png

decr指令也是一样的。

image.png

  • 扩展命令
    incrbydecrby也是一样的,很简单。
    image.png

append指令可以拼接字符串。

image.png

如果append key cmazxiaoma。这个key不存在的话,首先会创建这个key,然后存入cmazxiaoma内容,接着输出cmazxiaoma

image.png


Hash

  • 赋值,可以使用hset myhash key value单一赋值、hmset myhash key value key value多次赋值。

    image.png

  • 取值,可以用hget myhash key单一取值、hmget myhash key key 多个取值、hgetall取出所有key所对应的值。

    image.png

  • 删除 ,可以用hdel myhash key删除单一的key

    image.png

hdel myhash key key删除多个的key

image.png

del myhash删除myhash中所有的key

image.png

  • 数值增减,hincrby myhash key 100

    image.png

  • 使用hexists myhash key 判断key是否在myhash中存在,存在返回1,不存在返回0

    image.png

  • hlen myhash获取myhash中存在key的数量。

    image.png

  • hkeys myhash获取myhash中所有的key

    image.png

  • hvals myhash获取myhash中所有的values

    image.png


List

  • 存储list:

    • ArrayList使用数组方式
    • LinkedList使用双向链表方式
  • 两端添加

    • 使用lpush a b c 命令在左端添加,那么c肯定是在最左端。
      image.png
    • 使用rpush a b c命令在右端添加,那么c肯定是在最右端。
      image.png
  • 两端弹出

    • 使用lpop mylist弹出mylist中头部元素、rpop mylist2弹出mylist2尾部中的元素。它们都是3
      image.png
    • 我们接着来查看mylistmylist2
      image.png
  • 查看列表
    • 使用lrange mylist 0 5来查看mylist列表,mylist插入的方式从头结点开始添加的,那么输出肯定是321abc
      image.png
    • 使用lrange mylist2 0 5查看mylist2列表,mylist2插入的方式是从最右端添加的,那么输出肯定是abc123
      image.png
image.png
  • 获取列表元素的个数

    • 使用llen mylist命令
      image.png
  • 扩展命令

    • 使用lpushx mylist x,使插入的元素在头部位置。

      image.png

    • 使用rpushx mylist x,使插入的元素在尾部位置。

      image.png

    • 使用lrem mylist count elementcount代表是删除的次数,element代表是需要删除的元素。如果count > 0 代表删除的方式从头到尾,删除countelementcount < 0代表删除的方式从尾到头,删除countelement。如果count = 0,删除mylist中所有和element相同的元素。

      image.png

      image.png

    • lrem mylist 2 test1

      image.png

    • lrem mylist -2 test1

      image.png

    • lrem mylist 0 cmazxiaoma

      image.png

    • 在某一个下标位置插入元素。lset mylist index element

      image.png

    • 在目标元素之前插入指定的元素。linsert mylist before helloworl before_helloworld

      image.png

      image.png

    • 在目标元素之后插入指定的元素。linsert mylist helloworld after after_helloworld

      image.png

    • 弹出mylist中最后一个元素,并插入到mylist中的头部。rpoplpush mylist mylist

      image.png

    • rpoplpush使用场景

      image.png


Set

List类型不同的是,Set集合中不允许出现重复的元素。

  • 添加/删除元素
    • 添加 sadd myset a b c,如果我们重复添加相同的元素,肯定是不成功的。比如sadd myset a
      image.png
    • 删除 srem myset a,删除myset中的a元素。
      image.png
    • 查看myset中的元素。smembers myset
      image.png
    • 查看指定元素是否是myset中的成员。sismember myset a,返回0代表不存在,返回1代表存在。
      image.png
  • 获得集合中的元素, smembers myset

  • 集合中的差集运算,sdiff myset2 mysetmyset2中元素有b,c,dmyset中元素有b,c。它们之间的差集运算结果应该为d

    image.png

    • 集合中的交集运算,sinter myset2 myset,应该输出cb

      image.png

    • 集合中的并集运算,sunion myset2 myset,应该输出cbd

      image.png

  • 扩展命令

    • scard myset 查看myset有多少个元素。

      image.png

    • srandmember myset 随机返回myset中的一个元素。

      image.png

    • sdiffstore new_myset myset2 mysetmysetmyset2差集元素的结果存储到new_myset中。(sinterstore, sunionstore也是一样的用法)

      image.png

  • Set使用场景

    • 跟踪一些唯一性的数据。
    • 用于维护数据对象之间的关联关系。

SortedSet

SortedSet中的成员在集合中的位置是有序的。

  • 添加元素 zadd mysort 70 cmazxiaoma 80 xiaoma 100 doudou

    image.png

  • 获得元素

    • zcard mysort 获得mysort中所有元素的个数

      image.png

    • 获得mysortnamecmazxiaoma对应的成绩。zscore mysort cmazxiaoma

      image.png

  • 删除元素,zrem mysort cmazxiaoma deli doudou xiaoma

    image.png

  • 范围查询 zrange mysort 0 -1,输出的key是按成绩正序排列。

    image.png

    • 如果输出的数据项要带上成绩的话,指令应该是zrange mysort 0 -1 withscores

      image.png

    • 如果输出的数据项想按成绩逆序排序,那么就应该zrevrange mysort 0 -1 withscores

      image.png

    • 如果想按排名范围进行删除的话,那么应该zremrangebyrank mysort 0 2

      image.png

    • 如果想按成绩范围进行删除的话,那么应该zremrangebyscore mysort 0 10。顾名思义删除成绩在0-10之内的数据项。

      image.png

  • 扩展命令

    • 查看分数在0-10之内的学生信息,zrangebyscore mysort 0 10 withscores

      image.png

    • 查看分数在0-100之内且在第1行-第2行的学生信息。zrangebyscore mysort 0 100 withscores limit 0 2

      image.png

    • cmazxiaoma的成绩加100分。zincrby mysort cmazxiaoma 100

      image.png

    • 查看成绩0-10之间的学生的个数。zcount mysort 0 10

      image.png

  • Sorted Set使用场景

    • 如大型在线游戏积分排行榜

    • 构建索引数据


Redis中的通用命令

  • key * 获取所有redis中的key

    image.png

  • keys my* 获取所有redis中以my开头的key

    image.png

  • exists mylist 查看redis中是否存在mylist,0代表不存在,1代表存在。

    image.png

  • rename name new_name 给名字为name的数据结构重命名为new_name

    image.png

  • expire new_name 10 设置redisnew_name过期时间,通过ttl new_name看到其距离过期的时间。

    image.png

  • type mylist,可以查看mylist对应的数据结构类型。

    image.png


Redis的事务

  • Redis相关的特性:

    • 多数据库

    • Redis事务

  • 一个Redis最多可以提供16个数据库,下标分别是0-15。客户端默认连接的是第0号数据。我们可以通过select index来选择数据库。

    image.png

  • 我们想把第0号的数据库中某些key移动到第1号数据库里面,那么我们该怎么做呢。通过move cmazxiaoma_test_mayday_5 1就可以完成。

    image.png

  • 我们可以通过multiexecdiscard来完成事务操作。事务执行期间,Redis不会为其他客户端提供任何服务,以保证事务中的所有命令原子执行。multi相当于开启事务,exec相当于提交,discard相当于回滚。

  • 首先我们在第一个客户端进行如下操作。我们在第一个客户端开启了事务,在事务中我们incr num 2次,按理来说,get num 应该等于4

    redis第一个客户端.png

  • 我们在第二个客户端输入get num,发现num还是2。那么证明了我们的结论:事务执行期间,Redis不会为其他客户端提供任何服务,以保证事务中的所有命令原子执行。

    image.png

  • 如果我们在第一个客户端提交了事务。


    image.png
  • 接着我们在第二个客户端get num,发现可以num的值得到了更新。

    image.png

  • 我们可以演示一下回滚操作,首先set user cmazxiaoma,接着开启事务,在事务中set user xiaoma,然后进行回滚操作,发现 get user 依然是cmazxiaoma

    image.png


Redis的持久化

Redis的性能体现在它把数据都保存在内存当中。我们把内存中的数据同步到硬盘当中的操作称之为持久化。

  • Redis持久化方式:

    • RDB方式,在指定的时间内,把内存中的数据快照写入到硬盘当中。

    • AOF方式,将以日志的形式记录服务器所处理的每一个操作。当Redis服务器启动之初,它会读取该aof文件,会重新构建我们的数据库。保证我们启动之后,保证数据的完整性。

    • 无持久化,我们可以通过配置禁止Redis服务器的持久化,我们认为Redis就是缓存的一种机制了。

    • 同时使用。

  • RDB

    • 默认情况下,每隔一段时间redis服务器程序会自动对数据库做一次遍历,把内存快照写在一个叫做"dump.rdb"的文件里,这个持久化机制叫做SNAPSHOT。有了SNAPSHOT后,如果服务器宕机,重新启动redis服务器时,redis会自动加载"dump.rdb",将数据库状态恢复上一次SNAPSHOT的状态。

    • Redis服务器初始化过程中,设定了定时时间,每隔一段时间就会触发持久化操作,进入定时事件处理程序中,就会fork出子进程来进行持久化操作。

    • Redis服务器预设了save指令,客户端可要求服务器进程中断服务,执行持久化操作。

    • 我们可以通过vim /etc/redis.conf打开配置文件,可以看到以下配置。

      image.png

    • 同时我们还可以看到内存快照输出在file文件名。

      image.png

    优缺点:

    • 如果数据集很大,RDB相对于AOF启动效率很更高。

    • 如果想保证数据的高可用性,最大限度的避免数据的丢失,RDB将不是一个好的选择。因为系统在定时持久化操作之前,还没来得及在硬盘写入数据就发生宕机的话,就造成了数据的丢失。

    • RDB通过fork出子线程来完成数据持久化操作,如果当数据集很大的时候,可能会导致服务器停止几百ms,或者几s

RDB.png
  • AOF(append only file):

  • 对于Redis服务器而言,其缺省的机制是RDB,如果需要使用AOF,则需要修改appendonly no改成appendonly yesRedis在每一次收到数据修改的命令之后,都会将其追加到AOF文件中。在Redis下一次重新启动时,需要加载AOF文件中的信息来构建最新的数据到内存中。

    image.png

    image.png

  • 可以记录服务器的所有写操作。在服务器重新启动时,会把所有的写操作重新执行一遍从而实现数据的备份。当写操作集过大(比原有的数据集还大),Redis会重写写操作集。

  • 带来更好的数据安全性,有3种同步策略,每秒同步,每修改同步,不同步。 每秒同步也是异步完成的,效率也非常高。缺点是一旦系统发生宕机的现象,那么这一秒中的修改的数据就会发生丢失。每修改同步,我们可以视为同步持久化。每一次发生数据的变化,就会立即的记录在磁盘,这种效率很低,但是很安全。

  • 采用append追加的模式,就算系统发生宕机,也不会影响我们日志文件中已经存在的内容。然而我们本次操作中,只写入了一半数据就出现了系统崩溃的问题。在Redis下一次启动之前,我们可以通过"redis-check-aof --fix <filename>"命令来修复坏损的AOF文件,解决数据一致性的问题。

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

  • AOF在运行效率上往往会慢于RDB

AOF.png
  • RDBAOF的区别
    前者是保存了数据本身,而后者是记录了数据的变更。

尾言

这篇文章最后在网吧完成的,勿以善小而不为。

推荐阅读更多精彩内容

  • 分布式缓存技术PK:选择Redis还是Memcached? 经平台同意授权转载 作者:田京昆(腾讯后台研发工程师)...
    meng_philip123阅读 68,267评论 7 60
  • 1 Redis介绍1.1 什么是NoSql为了解决高并发、高可扩展、高可用、大数据存储问题而产生的数据库解决方...
    克鲁德李阅读 4,427评论 0 36
  • 1.1 资料 ,最好的入门小册子,可以先于一切文档之前看,免费。 作者Antirez的博客,Antirez维护的R...
    JefferyLcm阅读 16,459评论 1 51
  • 我一说我想吃榴莲其他人都说臭,臭脚丫味。我就不禁想问下,“你们吃过了吗?” 这么美味的水果,让我狼吞虎咽的吃完,想...
    雷云玲阅读 87评论 2 1
  • 今年的执考由之前7月,改回6月,少了一个多月的复习时间……………… 今年没什么特别的盼头,只求执助考必过! 好了,...
    小黄皇冠阅读 78评论 6 2