多个key作为参数进行删除_Redis:Key-Value的NoSQL数据库

bb5d10173fd0540e172c6eb71203ad69.png

主要内容

  1. Redis简介
  2. 使用Redis作为缓存工具时流程
  3. Redis单机版安装
  4. Redis数据类型
  5. Redis持久化策略
  6. Redis主从复制
  7. 哨兵(Sentinel)
  8. Redis集群(Cluster)
  9. Jedis
配套视频教程:Redis精品实战教程

一、 Redis简介

1、NoSQL简介

目前市场主流数据存储都是使用关系型数据库。每次操作关系型数据库时都是I/O操作,I/O操作是主要影响程序执行性能原因之一,连接数据库关闭数据库都是消耗性能的过程。关系型数据库索引数据结构都是树状结构,当数据量特别大时,导致树深度比较深,当深度深时查询性能会大大降低。尽量减少对数据库的操作,能够明显的提升程序运行效率。

针对上面的问题,市场上就出现了各种NoSQL(Not Only SQL,不仅仅可以使用关系型数据库)数据库,它们的宣传口号:不是什么样的场景都必须使用关系型数据库,一些特定的场景使用NoSQL数据库更好。

常见NoSQL数据库:

  • memcached:键值对,内存型数据库,所有数据都在内存中。
  • Redis:和Memcached类似,还具备持久化能力。
  • HBase:以列作为存储。
  • MongoDB:以Document做存储。

2 Redis简介

Redis是以Key-Value形式进行存储的NoSQL数据库。

Redis是使用C语言进行编写的。

平时操作的数据都在内存中,效率特高,读的效率110000/s,写81000/s,所以多把Redis当做缓存工具使用(在一些框架中还把Redis当做临时数据存储工具)。缓存工具:把数据库中数据缓存到Redis中,由于Redis读写性能较好,访问Redis中数据,而不是频繁访问数据库中数据。

Redis以solt(槽)作为数据存储单元,每个槽中可以存储N多个键值对。Redis中固定具有16384。理论上可以实现一个槽是一个Redis。每个向Redis存储数据的key都会进行crc16算法得出一个值后对16384取余就是这个key存放的solt位置。

同时通过Redis Sentinel(哨兵)提供高可用,通过Redis Cluster(集群)提供自动分区

二、 使用Redis作为缓存工具时流程(写代码时思路)(边路缓存思想中一部分)

1、应用程序向Redis查询数据

2、判断Key是否存在

3、是否存在

1) 存在

  • 把结果查询出来
  • 返回数据给应用程序

2) 不存在

  • 向MySQL查询数据
  • 把数据返回给应用程序
  • 把结果缓存到Redis中

0082c96c86e3df69e06598547ba6ef8e.png

三、 基于Docker安装Redis单机版

1 拉取镜像

docker pull redis:5.0.5

1 创建并启动容器

docker run -d --name redis -p 6379:6379 --restart always redis:5.0.5

2 客户端测试

docker exec -it redis bash

在任意目录在输入redis-cli 即可进入redis命令行。

四、 Redis数据类型(面试问题)

Redis中数据是key-value形式。不同类型Value是有不同的命令进行操作。key和value都支持下面类型(在代码中多把key设置为String类型):

  • String 字符串
  • Hash 哈希表
  • List 列表
  • Set 集合
  • Sorted Set 有序集合

Redis中命令有很多,抽取出部分进行讲解。

1、Key操作

1.1 exists

判断key是否存在。

  • 语法:exists key名称
  • 返回值:存在返回数字,不存在返回0

1.2 expire

设置key的过期时间,单位秒

  • 语法:expire key 秒数
  • 返回值:成功返回1,失败返回0

1.3 ttl

查看key的剩余过期时间

  • 语法:ttl key
  • 返回值:返回剩余时间,如果不过期返回-1

1.4 del

根据key删除键值对。

  • 语法:del key
  • 返回值:被删除key的数量

1.5 keys

命令: keys *

查看所有存在的key

2、字符串值(String)

2.1 set

设置指定key的值。如果key不存在是新增效果,如果key存在是修改效果。键值对是永久存在的。

  • 语法:set key value
  • 返回值:成功OK

2.2 get

获取指定key的值

  • 语法:get key
  • 返回值:key的值。不存在返回nil

2.3 setnx

当且仅当key不存在时才新增。恒新增,无修改功能。

  • 语法:setnx key value
  • 返回值:不存在时返回1,存在返回0

底层:

setnx具备分布式锁能力。在编写代码时如果调用setnx,时会对代码进行加锁。直到删除该key时会解锁。

setnx();// 加锁
// 代码
del();//解锁。

如果在并发访问时第一个线程setnx()时发现没有指定key会正常向下运行。其他线程在执行setnx()时发现有这个key就会等待,等待第一个线程删除key时才会继续向下执行。

2.3.1 常见的锁

  1. 锁:在Java中可以通过锁,让多线程执行时某个代码块或方法甚至类是线程安全的。通俗点说:一个线程访问,别的线程需要等待。
  2. 线程锁:同一个应用。多线程访问时添加的锁。synchronized(自动释放)或Lock(手动释放)
  3. 进程锁:不同进程(一个进程就是一个应用)需要访问同一个资源时,可以通过添加进程锁进行实现。
  4. 分布式锁:在分布式项目中不同项目访问同一个资源时,可以通过添加分布式锁保证线程安全。常见的分布式锁有两种:Redis的分布式锁和Zookeeper的分布式锁(通过调用Zookeeper的API给Zookeeper集群添加一个节点。如果节点能添加继续向下执行,执行结束删除该节点。如果其他线程发现该节点已经添加,会阻塞等待该节点删除才继续向下执行。)。

2.4 setex

设置key的存活时间,无论是否存在指定key都能新增,如果存在key覆盖旧值。同时必须指定过期时间。

  • 语法:setex key seconds value
  • 返回值:OK

3、哈希表(Hash)

Hash类型的值中包含多组field value。

9caf3bd084a2eba272c3bfa85905644e.png

3.1 hset

给key中field设置值。

  • 语法:hset key field value
  • 返回值:成功1,失败0

3.2 hget

获取key中某个field的值

  • 语法:hget key field
  • 返回值:返回field的内容

3.3 hmset

给key中多个filed设置值

  • 语法:hmset key field value field value
  • 返回值:成功OK

3.4 hmget

一次获取key中多个field的值

  • 语法:hmget key field field
  • 返回值:value列表

3.5 hvals

获取key中所有field的值

  • 语法:hvals key
  • 返回值:value列表

3.6 hgetall

获取所有field和value

  • 语法:hgetall key
  • 返回值:field和value交替显示列表

3.7 hdel

删除key中任意个field

  • 语法:hdel key field field
  • 返回值:成功删除field的数量

4、列表(List)

key value1 value2 value3 value4

4.1 Rpush

向列表末尾中插入一个或多个值

  • 语法;rpush key value value
  • 返回值:列表长度

4.2 lrange

返回列表中指定区间内的值。可以使用-1代表列表末尾

  • 语法:lrange list 0 -1
  • 返回值:查询到的值

4.3 lpush

将一个或多个值插入到列表前面

  • 语法:lpush key value value
  • 返回值:列表长度

4.4 llen

获取列表长度

  • 语法:llen key
  • 返回值:列表长度

4.5 lrem

删除列表中元素。count为正数表示从左往右删除的数量。负数从右往左删除的数量。

  • 语法:lrem key count value
  • 返回值:删除数量。

5、集合(Set)

set和java中集合一样。不允许重复值,如果插入重复值,后新增返回结果为0。

5.1 sadd

向集合中添加内容。不允许重复。

  • 语法:sadd key value value value
  • 返回值:集合长度

5.2 scard

返回集合元素数量

  • 语法:scard key
  • 返回值:集合长度

5.3 smembers

查看集合中元素内容

  • 语法:smembers key
  • 返回值:集合中元素

6、有序集合(Sorted Set)

有序集合中每个value都有一个分数(score),根据分数进行排序。

6.1 zadd

向有序集合中添加数据

  • 语法:zadd key score value score value
  • 返回值:长度

6.2 zrange

返回区间内容,withscores表示带有分数

  • 语法:zrange key 区间 [withscores]
  • 返回值:值列表

五、 Redis持久化策略(面试问题)

Redis不仅仅是一个内存型数据库,还具备持久化能力。

Redis每次启动时都会从硬盘存储文件中把数据读取到内存中。运行过程中操作的数据都是内存中的数据。

一共包含两种持久化策略:RDB 和 AOF

1、RDB(Redis DataBase)

rdb模式是默认模式,可以在指定的时间间隔内生成数据快照(snapshot),默认保存到dump.rdb文件中。当redis重启后会自动加载dump.rdb文件中内容到内存中。

用户可以使用SAVE(同步)或BGSAVE(异步)手动保存数据。

可以设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令,可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令。

例如:

save 900 1
save 300 10
save 60 10000

那么只要满足以下三个条件中的任意一个,BGSAVE命令就会被执行。计时单位是必须要执行的时间,save 900 1 ,每900秒检测一次。在并发量越高的项目中Redis的时间参数设置的值要越小。

服务器在900秒之内,对数据库进行了至少1次修改
服务器在300秒之内,对数据库进行了至少10次修改
服务器在60秒之内,对数据库进行了至少10000次修改。

1.1 优点

rdb文件是一个紧凑文件,直接使用rdb文件就可以还原数据。

数据保存会由一个子进程进行保存,不影响父进程做其他事情。

恢复数据的效率要高于aof

总结:性能要高于AOF

1.2 缺点

每次保存点之间导致redis不可意料的关闭,可能会丢失数据。

由于每次保存数据都需要fork()子进程,在数据量比较大时可能会比较耗费性能。

2、AOF(AppendOnly File)

AOF默认是关闭的 appendonly no,需要在配置文件redis.conf中开启AOF。Redis支持AOF和RDB同时生效,如果同时存在,AOF优先级高于RDB(Redis重新启动时会使用AOF进行数据恢复)

AOF原理:监听执行的命令,如果发现执行了修改数据的操作,同时直接同步到数据库文件中,同时会把命令记录到日志中。即使突然出现问题,由于日志文件中已经记录命令,下一次启动时也可以按照日志进行恢复数据,由于内存数据和硬盘数据实时同步,即使出现意外情况也需要担心。

2.1 优点

相对RDB数据更加安全。

2.2 缺点

相同数据集AOF要大于RDB。

相对RDB可能会慢一些。

2.3 开启办法

修改redis.conf中。

appendonly yes 开启aof

appendfilename 设置aof数据文件,名称随意。

# 默认no
appendonly yes
# aof文件名
appendfilename "appendonly.aof"

六、 Redis主从复制

Redis支持集群功能。为了保证单一节点可用性,redis支持主从复制功能。每个节点有N个复制品(replica),其中一个复制品是主(master),另外N-1个复制品是从(Slave),也就是说Redis支持一主多从。

一个主可有多个从,而一个从又可以看成主,它还可以有多个从。

7a882146155af211233c6a8a0f58dd70.png

1、主从优点

增加单一节点的健壮性,从而提升整个集群的稳定性。(Redis中当超过1/2节点不可用时,整个集群不可用)

从节点可以对主节点数据备份,提升容灾能力。

读写分离。在redis主从中,主节点一般用作写(具备读的能力),从节点只能读,利用这个特性实现读写分离,写用主,读用从。

2、基于Docker一主多从搭建

1.1 拉取redis镜像

# docker pull redis

1.2 创建并运行三个Docker容器

先停止单机版Redis。单机版Redis端口6379

三个容器分别占用系统的6379、6380、6381端口

# docker run --name redis1 -p 6379:6379 -v /opt/redis:/data -d redis:5.0.5
# docker run --name redis2 -p 6380:6379 -v /opt/redis:/data -d redis:5.0.5
# docker run --name redis3 -p 6381:6379 -v /opt/redis:/data -d redis:5.0.5

1.3 在从中指定主的ip和端口

进入redis2容器内部设置主的ip和端口

# docker exec -it redis2 redis-cli
# slaveof 192.168.108.128 6379
# exit

进入redis2容器内部设置主的ip和端口

# docker exec -it redis3 redis-cli
# slaveof 192.168.108.128 6379
# exit

9d24ad4873d819270ef95ab010057872.png

1.1 测试主从效果

进入redis1容器内部,新增key-value

# docker exec -it redis1 redis-cli
# set name "bjsxt"
# exit

分别进入redis2和redis3容器,查看是否有name键

# docker exec -it redis1 redis-cli
# get name

七、哨兵(Sentinel)

在redis主从默认只有主具备写的能力,而从只能读。如果主宕机,整个节点不具备写能力。但是如果这是让一个从变成主,整个节点就可以继续工作。即使之前的主恢复过来也当做这个节点的从即可。

Redis的哨兵就是帮助监控整个节点的,当节点主宕机等情况下,帮助重新选取主。

Redis中哨兵支持单哨兵和多哨兵。单哨兵是只要这个哨兵发现master宕机了,就直接选取另一个master。而多哨兵是根据我们设定,达到一定数量哨兵认为master宕机后才会进行重新选取主。

八、Redis集群(Cluster)

1、集群原理

a) 集群搭建完成后由集群节点平分(不能平分时,前几个节点多一个槽)16384个槽。

b) 客户端可以访问集群中任意节点。所以在写代码时都是需要把集群中所有节点都配置上。

c) 当向集群中新增或查询一个键值对时,会对Key进行Crc16算法得出一个小于16384值,这个值就是放在哪个槽中,在判断槽在哪个节点上,然后就操作哪个节点。

1425c90356bc951be883ade2f8785567.png

集群:集群中所有节点都安装在不同服务器上。

伪集群:所有节点都安装在一台服务器上,通过不同端口号进行区分不同节点。

当集群中超过或等于1/2节点不可用时,整个集群不可用。为了搭建稳定集群,都采用奇数节点。

Redis每个节点都支持一主多从。会有哨兵监控主的状态。如果出现(配置文件中配置当多少个哨兵认为主失败时)哨兵发现主不可用时会进行投票,投票选举一个从当作主,如果后期主恢复了,主当作从加入节点。在搭建redis集群时,内置哨兵策略。

演示时:创建3个节点,每个节点搭建一主一从。一共需要有6个redis。

2、Redis集群安装步骤

2.1 新建配置模板文件

# cd /usr/local
# mkdir redis-cluster
# cd redis-cluster
# vim redis-cluster.tmpl

红色IP部分需要修改为自己的IP

f6a63c02ee6d2c5f5f1c96d79e4dd686.png

2.2 使用Shell脚本创建6个目录

for port in `seq 7000 7005`; do
mkdir -p ./${port}/conf
&& PORT=${port} envsubst < ./redis-cluster.tmpl > ./${port}/conf/redis.conf
&& mkdir -p ./${port}/data;
done

2.3 创建桥连网络

# docker network create redis-net

查看网络是否创建成功

# docker network ls

3b72fe9b1a2cb1952734190a8ff08b58.png

2.4 创建并启动6个容器

for port in `seq 7000 7005`; do
docker run -d -ti -p ${port}:${port} -p 1${port}:1${port}
-v /usr/local/redis-cluster/${port}/conf/redis.conf:/usr/local/etc/redis/redis.conf
-v /usr/local/redis-cluster/${port}/data:/data
--restart always --name redis-${port} --net redis-net
--sysctl net.core.somaxconn=1024 redis:5.0.5 redis-server /usr/local/etc/redis/redis.conf;
done

2.5 查看6个容器ip及端口

# docker inspect redis-7000 redis-7001 redis-7002 redis-7003 redis-7004 redis-7005 | grep IPAddress

7aea49fec121464221e191e677e6d610.png

2.6 执行集群脚本

进入6个容器中任意一个。示例中以redis-7000举例

# docker exec -it redis-7000 bash

执行创建脚本命令。 --cluster-relicas 1表示每个主有1个从。

redis-cli --cluster create
172.19.0.2:7000
172.19.0.3:7001
172.19.0.4:7002
172.19.0.5:7003
172.19.0.6:7004
172.19.0.7:7005
--cluster-replicas 1

输入后给出集群信息,输入yes后创建集群

ece3caa6e8efbc4999b100585540344f.png

2.7 验证集群

在任意Redis容器内部,进入Redis客户端工具。

示例中还是以Redis-7000举例。

# redis-cli -c -p 7000

c71106f3fc893da8e02475e3b874649c.png

九、Jedis(了解)

Redis给Java语言提供了客户端API,称之为Jedis。

Jedis API和Redis 命令几乎是一样的。

例如:Redis对String值新增是set命令,Jedis中也是set方法。所以本课程中没有重点把所有方法进行演示,重要演示Jedis如何使用。

Jedis API特别简单,基本上都是创建对象调用方法即可。由于Jedis不具备把对象转换为字符串的能力,所以每次都需要借助Json转换工具进行转换,这个功能在Spring Data Redis中已经具备,推荐使用Spring Data Redis。

1、添加依赖

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <version>2.2.6.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

2、单机版

public void testStandalone(){
    Jedis jedis = new Jedis("192.168.32.132",6379);
    jedis.set("name","smallming-standalone");
    String value = jedis.get("name");
    System.out.println(value);
}

3、带有连接池

public void testPool(){
    JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
    jedisPoolConfig.setMaxTotal(20);
    jedisPoolConfig.setMaxIdle(5);
    jedisPoolConfig.setMinIdle(3);
    JedisPool jedisPool = new JedisPool(jedisPoolConfig,"192.168.32.132",6379);
    Jedis jedis = jedisPool.getResource();
    jedis.set("name","smallming-pool");
    String value = jedis.get("name");
    System.out.println(value);
}

4、集群

public void testCluster(){
    Set<HostAndPort> set = new HashSet<>();
    set.add(new HostAndPort("192.168.32.132",7001));
    set.add(new HostAndPort("192.168.32.132",7002));
    set.add(new HostAndPort("192.168.32.132",7003));
    set.add(new HostAndPort("192.168.32.132",7004));
    set.add(new HostAndPort("192.168.32.132",7005));
    set.add(new HostAndPort("192.168.32.132",7006));
    JedisCluster jedisCluster = new JedisCluster(set);
    jedisCluster.set("name","smallming");
    String value = jedisCluster.get("name");
    System.out.println(value);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值