【分布式架构之旅】Redis入门

2018-06-05 18:22:26 浏览数 (1)

  • 易扩展
  • 灵活的数据模型
  • 高可用
  • 大数据量,高性能

Redis的概述

  • 高性能键值对数据库,支持的键值数据类型:
代码语言:txt复制
- 字符串类型 - `String`
- 列表类型 - `Set`
- 有序集合类型 - `Sorted Set`
- 散列类型 - `Hash`
- 集合类型 - `List`
代码语言:txt复制
- 缓存
- 任务队列
- 网站访问统计
- 应用排行榜
- 分布式集群架构中的session分离

Redis在Linux上的使用

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


Jedis的入门

  • 我们要在Java平台上使用redis,肯定需要Jedis这个客户端。首先在pom文件中引入Jedis的依赖
代码语言:javascript复制
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
  • 普通方式创建Jedis对象。
代码语言:javascript复制
    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对象。
代码语言:javascript复制
  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();
        }
    }
}
  • 编写测试类。
代码语言:javascript复制
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定义的注意点:
代码语言:txt复制
- 不要过长。
- 不要过短。
- 统一的命名规范。存储
代码语言:txt复制
- 二进制安全的,存入和获取的数据相同。
-  `Value`最多可以容纳的数据长度是`512M`。存储
代码语言:txt复制
-  赋值

image.png

代码语言:txt复制
-  取值

image.png

代码语言:txt复制
-  删除

image.png

代码语言:txt复制
- 数值增减

如果属性不存在的话,那么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:
代码语言:txt复制
-  `ArrayList`使用数组方式
-  `LinkedList`使用双向链表方式两端添加
代码语言:txt复制
- 使用`lpush a b c` 命令在左端添加,那么`c`肯定是在最左端。

image.png

代码语言:txt复制
- 使用`rpush a b c`命令在右端添加,那么`c`肯定是在最右端。

image.png

  • 两端弹出
代码语言:txt复制
- 使用`lpop mylist`弹出`mylist`中头部元素、`rpop mylist2`弹出`mylist2`尾部中的元素。它们都是`3`。

image.png

代码语言:txt复制
- 我们接着来查看`mylist`、`mylist2`。

image.png

  • 查看列表
    • 使用lrange mylist 0 5来查看mylist列表,mylist插入的方式从头结点开始添加的,那么输出肯定是321abc

image.png

代码语言:txt复制
- 使用`lrange mylist2 0 5`查看`mylist2`列表,`mylist2`插入的方式是从最右端添加的,那么输出肯定是`abc123`

image.png

image.png

  • 获取列表元素的个数
代码语言:txt复制
- 使用`llen mylist`命令

image.png

  • 扩展命令
代码语言:txt复制
-  使用`lpushx mylist x`,使插入的元素在头部位置。

image.png

代码语言:txt复制
-  使用`rpushx mylist x`,使插入的元素在尾部位置。

image.png

代码语言:txt复制
-  使用`lrem mylist count element`,`count`代表是删除的次数,`element`代表是需要删除的元素。如果`count > 0` 代表删除的方式从头到尾,删除`count`个`element`,`count < 0`代表删除的方式从尾到头,删除`count`个`element`。如果`count = 0`,删除`mylist`中所有和`element`相同的元素。

image.png

image.png

代码语言:txt复制
-  `lrem mylist 2 test1`

image.png

代码语言:txt复制
-  `lrem mylist -2 test1`

image.png

代码语言:txt复制
-  `lrem mylist 0 cmazxiaoma`

image.png

代码语言:txt复制
-  在某一个下标位置插入元素。`lset mylist index element`

image.png

代码语言:txt复制
-  在目标元素之前插入指定的元素。`linsert mylist before helloworl before_helloworld`

image.png

image.png

代码语言:txt复制
-  在目标元素之后插入指定的元素。`linsert mylist helloworld after after_helloworld`

image.png

代码语言:txt复制
-  弹出`mylist`中最后一个元素,并插入到`mylist`中的头部。`rpoplpush mylist mylist`

image.png

代码语言:txt复制
-  `rpoplpush`使用场景

image.png


Set

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

  • 添加/删除元素
    • 添加 sadd myset a b c,如果我们重复添加相同的元素,肯定是不成功的。比如sadd myset a

image.png

代码语言:txt复制
- 删除 `srem myset a`,删除`myset`中的`a`元素。

image.png

代码语言:txt复制
- 查看`myset`中的元素。`smembers myset`

image.png

代码语言:txt复制
- 查看指定元素是否是`myset`中的成员。`sismember myset a`,返回`0`代表不存在,返回`1`代表存在。

image.png

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

image.png

代码语言:txt复制
-  集合中的交集运算,`sinter myset2 myset`,应该输出`cb`。

image.png

代码语言:txt复制
-  集合中的并集运算,`sunion myset2 myse`t,应该输出`cbd`。

image.png

  • 扩展命令
代码语言:txt复制
-  `scard myset` 查看`myset`有多少个元素。

image.png

代码语言:txt复制
-  `srandmember myset` 随机返回`myset`中的一个元素。

image.png

代码语言:txt复制
-  `sdiffstore new_myset myset2 myset` 把`myset`和`myset2`差集元素的结果存储到`new_myset`中。(`sinterstore`, `sunionstore`也是一样的用法)

image.png

  • Set使用场景
代码语言:txt复制
- 跟踪一些唯一性的数据。
- 用于维护数据对象之间的关联关系。

SortedSet

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

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

image.png

  • 获得元素
代码语言:txt复制
-  `zcard mysort` 获得`mysort`中所有元素的个数

image.png

代码语言:txt复制
-  获得`mysort`中`name`为`cmazxiaoma`对应的成绩。`zscore mysort cmazxiaoma`

image.png

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

image.png

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

image.png

代码语言:txt复制
-  如果输出的数据项要带上成绩的话,指令应该是`zrange mysort 0 -1 withscores`

image.png

代码语言:txt复制
-  如果输出的数据项想按成绩逆序排序,那么就应该`zrevrange mysort 0 -1 withscores`

image.png

代码语言:txt复制
-  如果想按排名范围进行删除的话,那么应该`zremrangebyrank mysort 0 2`

image.png

代码语言:txt复制
-  如果想按成绩范围进行删除的话,那么应该`zremrangebyscore mysort 0 10`。顾名思义删除成绩在`0-10`之内的数据项。

image.png

  • 扩展命令
代码语言:txt复制
-  查看分数在`0-10`之内的学生信息,`zrangebyscore mysort 0 10 withscores`

image.png

代码语言:txt复制
-  查看分数在`0-100`之内且在第`1`行-第`2`行的学生信息。`zrangebyscore mysort 0 100 withiscores limit 0 2`

image.png

代码语言:txt复制
-  给`cmazxiaoma`的成绩加`100`分。`zincrby mysort cmazxiaoma 100`

image.png

代码语言:txt复制
-  查看成绩`0-10`之间的学生的个数。`zcount mysort 0 10`

image.png

  • Sorted Set使用场景
代码语言:txt复制
- 如大型在线游戏积分排行榜
- 构建索引数据

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相关的特性:
代码语言:txt复制
- 多数据库
- `Redis`事务一个

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持久化方式:
代码语言:txt复制
- `RDB`方式,在指定的时间内,把内存中的数据快照写入到硬盘当中。
- `AOF`方式,将以日志的形式记录服务器所处理的每一个操作。当`Redis`服务器启动之初,它会读取该`aof`文件,会重新构建我们的数据库。保证我们启动之后,保证数据的完整性。
- 无持久化,我们可以通过配置禁止`Redis`服务器的持久化,我们认为`Redis`就是缓存的一种机制了。
- 同时使用。
代码语言:txt复制
- 默认情况下,每隔一段时间`redis`服务器程序会自动对数据库做一次遍历,把内存快照写在一个叫做`"dump.rdb"`的文件里,这个持久化机制叫做`SNAPSHOT`。有了`SNAPSHOT`后,如果服务器宕机,重新启动`redis`服务器时,`redis`会自动加载`"dump.rdb"`,将数据库状态恢复上一次`SNAPSHOT`的状态。
- `Redis`服务器初始化过程中,设定了定时时间,每隔一段时间就会触发持久化操作,进入定时事件处理程序中,就会`fork`出子进程来进行持久化操作。
- `Redis`服务器预设了`save`指令,客户端可要求服务器进程中断服务,执行持久化操作。
-  我们可以通过`vim /etc/redis.conf`打开配置文件,可以看到以下配置。

image.png

代码语言:txt复制
-  同时我们还可以看到内存快照输出在`file`文件名。

image.png

优缺点:

代码语言:txt复制
- 如果数据集很大,`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 人点赞