【分布式架构之旅】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 withiscores 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的区别 前者是保存了数据本身,而后者是记录了数据的变更。

尾言

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏進无尽的文章

简述OC语言

对于一门语言的学习是需要时间领悟的,而对于一些原理性的问题,我们需要清楚其核心思想,知其然而知其所以然,这样才能有利于自己的后续发展。本文只是简述,没有面面具到...

2112
来自专栏犀利豆的技术空间

徒手撸框架--实现 RPC 远程调用

微服务已经是每个互联网开发者必须掌握的一项技术。而 RPC 框架,是构成微服务最重要的组成部分之一。趁最近有时间。又看了看 dubbo 的源码。dubbo 为了...

1432
来自专栏pangguoming

C# 事件(Event)

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。事件是用...

3745
来自专栏小樱的经验随笔

堆和栈的区别

一、预备知识—程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量...

3569
来自专栏草根专栏

使用 Moq 测试.NET Core 应用 -- Mock 属性

第一篇文章, 关于Mock的概念介绍: https://www.cnblogs.com/cgzl/p/9294431.html

1094
来自专栏CDA数据分析师

工具 | 很全的 Python 面试题

Python语言特性 1 Python的函数参数传递 看两个例子: ? 所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。 ...

1.3K9
来自专栏生信技能树

linux命令行文本操作一文就够

主要是 awk/grep/sed这三驾马车,加上vi这个神器,最后辅助一些小工具,包括 wc,cat,diff,join,paste,cut,uniq 这里 简...

4629
来自专栏分布式系统和大数据处理

Command模式入门

提起Command模式,我想没有什么比遥控器的例子更能说明问题了,本文将通过它来一步步实现GOF的Command模式。

1002
来自专栏安恒网络空间安全讲武堂

seacms修复历程总结

seacms修复历程总结 从6.45版本开始search.php就存在前台getshell的漏洞,到6.54官方对其进行修补,但修复方法是对用户输入的参数进行过...

6107
来自专栏java达人

硬编码,常量,枚举类

假如有一笔业务需要审核,审核状态分:未审核,审核中,审核通过,审核不通过。我们在程序里是否可以直接这么写: if(state==1){//1代表未操作 //...

3486

扫码关注云+社区

领取腾讯云代金券