Redis详解
第一节:Redis介绍
3.29.1 什么是NoSql
为了解决高并发、高可扩展、高可用、大数据存储问题而产生的数据库解决方案,就是NoSql数据库。 NoSQL,泛指非关系型的数据库,NoSQL即Not-Only SQL,它可以作为关系型数据库的良好补充。但是它不能替代关系型数据库,而且它是存储在内存中,所以它的访问速度很快。
3.29.2 Nosql的数据库分类
- 键值(Key-Value)存储数据库
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用:内容缓存,主要用于处理大量数据的高访问负载。
数据模型:一系列键值对
优势:快速查询
劣势:存储的数据缺少结构化
- 列存储数据库
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在文件系统中
优势:查找速度快,可扩展性强,更容易进行分布式扩展
劣势:功能相对局限
- 文档型数据库
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型:一系列键值对
优势:数据结构要求不严格
劣势:查询性能不高,而且缺乏统一的查询语法
- 图形(Graph)数据库
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
优势:利用图结构相关算法。
劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
3.29.3 Redis介绍
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
-
字符串类型
-
散列类型
-
列表类型
-
集合类型
-
有序集合类型。
3.29.4 Redis发展简史
2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人 Salvatore Sanfilippo便对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。
SalvatoreSanfilippo自己也没有想到,短短的几年时间,Redis就拥有了庞大的用户群体。Hacker News在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、街旁网、知乎网,国外如GitHub、Stack Overflow、Flickr等都是Redis的用户。
VMware公司从2010年开始赞助Redis的开发,Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。
3.29.5 Redis应用场景
-
缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
-
分布式集群架构中的session分离。
-
聊天室的在线好友列表。
-
任务队列。(秒杀、抢购、12306等等)
-
应用排行榜。
-
网站访问统计。
-
数据过期处理(可以精确到毫秒)
3.29.6 Redis的特性
-
redis数据访问速度快(数据在内存中)
-
redis有数据持久化机制(持久化机制有两种:1、定期将内存数据dump到磁盘;2、aof(append only file)持久化机制——用记日志的方式记录每一条数据更新操作,一旦出现灾难事件,可以通过日志重放来恢复整个数据库)
-
redis支持集群模式(容量可以线性扩展)
-
redis相比其他缓存工具(ehcache/memcached),有一个鲜明的优势:支持丰富的数据结构
第二节 Redis安装(重要)
3.29.6 下载Redis
官网地址:https://redis.io/
下载地址:http://download.redis.io/releases/redis-4.0.14.tar.gz
3.29.7 Redis安装环境
Redis安装一般会在Linux系统下进行安装,又因为redis是使用c语言开发,所以需要c语言环境。
-
Linux:centOS7.7
-
VMware:14
-
C语言环境:
3.29.8 Redis安装
第一步:在VMware中安装CentOS系统(Linux)。
第二步:在Linux系统中安装c语言环境
[root@qianfeng01~]# yum install gcc-c++
第三步:将redis的源码包上传到Linux系统。
第四步:解压源码包
[root@qianfeng01 ~] tar -zxvf redis-4.0.14.tar.gz -C /usr/local
第五步:进入redis-4.0.14包,然后执行make命令,编译redis的源码
[root@qianfeng01 redis-4.0.14]# make
第六步:安装
[root@qianfeng01 redis-4.0.14]# make install PREFIX=/usr/local/redis
3.29.9 Redis启动
3.29.9.1 前端启动
前端启动,如果客户端关掉或者执行ctrl+c命令。则整个redis服务也停掉。
前端启动,即在客户端中执行以下命令:
[root@qianfeng01 bin]# ./redis-server
关闭:ctrl+c
3.29.9.2 后端启动
第一步:执行cp命令将redis解压缩包中的redis.conf文件拷贝到bin目录下
[root@qianfeng01 bin]# cp redis.conf /usr/local/redis/bin
第二步:修改redis.conf文件:
第三步:启动redis服务
关闭:正常关闭
非正常关闭
第三节 Redis客户端(了解)
3.29.10 Redis自带的客户端
./redis-cli –h redis数据库的ip -p 端口号
默认可以执行 ./redis-cli 此时使用默认的ip为129.0.0.1 默认的端口为 6379
3.29.11 Redis桌面管理工具
第一步:安装redis桌面管理工具
第二步:创建连接
界面如下:
Redis默认有16个库,这个数字可以修改。
切换库使用如下命令:
3.29.11.1 访问超时
打开防火墙
[root@qianfeng01 ~]# vi /etc/sysconfig/iptables
# Firewall configuration written bysystem-config-firewall
# Manual customization of this file is notrecommended.
*filter
Redis.assetsINPUT ACCEPT [0Redis.assets0]
Redis.assetsFORWARD ACCEPT [0Redis.assets0]
Redis.assetsOUTPUT ACCEPT [0Redis.assets0]
-A INPUT -m state --stateESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp--dport 22 -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 6379 -jACCEPT
-A INPUT -j REJECT --reject-withicmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
"/etc/sysconfig/iptables" 15L,544C 已写入
[root@qianfeng01 ~]# service iptables restart
iptables:清除防火墙规则: [确定]
iptables:将链设置为政策 ACCEPT:filter [确定]
iptables:正在卸载模块: [确定]
iptables:应用防火墙规则: [确定]
[root@qianfeng01 ~]#
3.29.12 Java客户端
3.29.13 Jedis介绍
Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。
Jedis同样也是托管在github上,地址:httpsRedis.assets//github.com/xetorthio/jedis
3.29.14 环境准备
Jedis的pom依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.0</version>
</dependency>
3.29.15 单机连接Redis
import redis.clients.jedis.Jedis;
public class StringDemo1 {
public static void main(String[] args) {
// 创建一个Jedis连接
Jedis jedis = new Jedis("node02", 6379);
String setAck = jedis.set("s1", "hahaha");
System.out.println(setAck);
String getAck = jedis.get("s1");
System.out.println(getAck);
String ping = jedis.ping();
System.out.println(ping);
jedis.close();
}
}
输出:
3.29.16 连接池连接Redis
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisPoolTest {
public static void main(String[] args) {
// 创建JedisPool
JedisPool pool = new JedisPool("node02", 6379);
// 通过pool获取Jedis实例
Jedis jedis = pool.getResource();
// 写入数据
jedis.set("s2", "hehehe");
// 查询数据
String getAck = jedis.get("s2");
System.out.println(getAck);
// 关闭Jedis
jedis.close();
// 关闭JedisPool
pool.close();
}
}
输出:
第四节 Redis数据类型(重要)
3.29.17 Redis数据类型
String类型
Hash类型
List类型
Set类型
SortedSet类型
3.29.18 String类型
3.29.18.1 命令
Set
129.0.0.1Redis.assets6379> set str1 angelababy
OK
Get
129.0.0.1Redis.assets6379> get str1
" angelababy"
Del
129.0.0.1Redis.assets6379> del str1
(integer) 1
自增
必须value为数字类型
129.0.0.1Redis.assets6379> set s1 1
OK
129.0.0.1Redis.assets6379> incr s1
(integer) 2
129.0.0.1Redis.assets6379> incr s1
(integer) 3
129.0.0.1Redis.assets6379> incr s1
(integer) 4
129.0.0.1Redis.assets6379> incr s1
(integer) 5
自减
129.0.0.1Redis.assets6379> decr s1
(integer) 4
129.0.0.1Redis.assets6379> decr s1
(integer) 3
129.0.0.1Redis.assets6379> decr s1
(integer) 2
129.0.0.1Redis.assets6379> decr s1
自增自减指定数值
129.0.0.1Redis.assets6379> incrby s1 3
(integer) 4
129.0.0.1Redis.assets6379> decrby s1 3
(integer) 1
设置或者获取多个key/value
129.0.0.1Redis.assets6379> mset s1 v1 s2 v2
OK
129.0.0.1Redis.assets6379> mget s1 s2
1) "v1"
2) "v2"
3.29.18.2 案例实现
import com.google.gson.Gson;
import redis.clients.jedis.Jedis;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class StringDemo2 {
private static Jedis jedis = new Jedis("node02", 6379);
public static void main(String[] args) throws Exception {
// StringTest();
// objectTest();
objectToJsonTest();
}
/**
* 将字符串缓存到String数据结构中
*/
public static void StringTest(){
jedis.set("user:001:name", "xiaofang");
jedis.mset("user:002:name", "xiaofen", "user:003:name", "yaoyao");
String uname001 = jedis.get("user:001:name");
String uname002 = jedis.get("user:002:name");
String uname003 = jedis.get("user:003:name");
System.out.println(uname001);
System.out.println(uname002);
System.out.println(uname003);
}
/**
* 将对象缓存到String数据结构中
*/
public static void objectTest() throws Exception {
ProduceInfo p = new ProduceInfo();
p.setName("Iphone8plus");
p.setPrice(7999.0);
p.setProcuctDesc("看视频");
// 将对象序列化为byte数组
ByteArrayOutputStream ba = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(ba);
// 用对象序列化的方式将ProduceInfo序列化写入流中
oos.writeObject(p);
// 将ba流转换为字节数组
byte[] pBytes = ba.toByteArray();
// 将序列化好的数据缓存到redis中
jedis.set("product:001".getBytes(), pBytes);
// 读取刚刚缓存的数据
byte[] pBytesRes = jedis.get("product:001".getBytes());
// 反序列化
ByteArrayInputStream bi = new ByteArrayInputStream(pBytesRes);
ObjectInputStream oi = new ObjectInputStream(bi);
ProduceInfo pRes = (ProduceInfo) oi.readObject();
System.out.println(pRes);
}
/**
* 将对象转换为json字符串缓存到redis
*/
public static void objectToJsonTest() {
ProduceInfo p = new ProduceInfo();
p.setName("Iphone4");
p.setPrice(4888.0);
p.setProcuctDesc("用来起啤酒盖儿");
// 将对象转换为json格式
Gson gson = new Gson();
String jsonProductInfo = gson.toJson(p);
// 缓存到redis
jedis.set("product:002", jsonProductInfo);
// 获取数据
String jsonRes = jedis.get("product:002");
// 将json字符串转换为对象
ProduceInfo produceInfo = gson.fromJson(jsonRes, ProduceInfo.class);
System.out.println(jsonRes);
System.out.println(produceInfo);
}
}
import java.io.Serializable;
public class ProduceInfo implements Serializable {
// 商品名称
private String name;
// 商品价格
private Double price;
// 商品描述
private String procuctDesc;
@Override
public String toString() {
return "商品信息:{" +
"商品名称='" + name + '\'' +
", 商品价格=" + price +
", 商品描述='" + procuctDesc + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getProcuctDesc() {
return procuctDesc;
}
public void setProcuctDesc(String procuctDesc) {
this.procuctDesc = procuctDesc;
}
}
3.29.19 Hash类型
3.29.19.1 使用String的问题
假设有User对象以JSON序列化的形式存储到Redis中,User对象有id,username、password、age、name等属性,存储的过程如下:
保存、更新:
User对象 à json(string)à redis
如果在业务上只是更新age属性,其他的属性并不做更新我应该怎么做呢?如果仍然采用上边的方法在传输、处理时会造成资源浪费,下边讲的hash可以很好的解决这个问题。
3.29.19.2 Redis Hash介绍
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
3.29.19.2 命令
-
hset
在redis中,命令是不区分大小写,但是key区分大小写
129.0.0.1Redis.assets6379> hset m1 k1 v1 (integer) 1 129.0.0.1Redis.assets6379> HSET m1 k1 v1 (integer) 0
-
hget
129.0.0.1Redis.assets6379> hget m1 k1 "v1" 129.0.0.1Redis.assets6379> hget m1 (error) ERR wrong number of arguments for 'hget' command 129.0.0.1Redis.assets6379>
-
hdel
129.0.0.1Redis.assets6379> hdel m1 (error) ERR wrong number of arguments for 'hdel' command 129.0.0.1Redis.assets6379> hdel m1 k1 (integer) 1
-
批量操作
129.0.0.1Redis.assets6379> hmset m1 k1 v1 k2 v2 OK 129.0.0.1Redis.assets6379> hmget m1 k1 k2 1) "v1" 2) "v2" 129.0.0.1Redis.assets6379> hmget m1 k1 k2 k3 1) "v1" 2) "v2" 3) (nil)
-
增加数值
129.0.0.1Redis.assets6379> hincrby m2 k1 (error) ERR wrong number of arguments for 'hincrby' command 129.0.0.1Redis.assets6379> hincrby m2 k1 2 (integer) 3
3.29.19.3 其他命令(自学)
-
判断字段是否存在
HEXISTS key field
129.0.0.1Redis.assets6379> hexists user age 查看user中是否有age字段 (integer) 1 129.0.0.1Redis.assets6379> hexists user name 查看user中是否有name字段 (integer) 0
HSETNX key field value
当字段不存在时赋值,类似HSET,区别在于如果字段已经存在,该命令不执行任何操作。
129.0.0.1Redis.assets6379> hsetnx user age 30 如果user中没有age字段则设置age值为30,否则不做任何操作 (integer) 0
-
只获取字段名或字段值
HKEYS key
HVALS key
129.0.0.1Redis.assets6379> hmset user age 20 name lisi OK 129.0.0.1Redis.assets6379> hkeys user 1) "age" 2) "name" 129.0.0.1Redis.assets6379> hvals user 1) "20" 2) "lisi"
-
获取字段数量
HLEN key
129.0.0.1Redis.assets6379> hlen user (integer) 2
3.29.19.4 应用
商品id、商品名称、商品描述、商品库存、商品好评
定义商品信息的key:
商品1001的信息在 redis中的key为:itemsRedis.assets1001
存储商品信息
192.168.101.3Redis.assets7003> HMSET itemsRedis.assets1001 id 3 name apple price 999.9 OK
获取商品信息
192.168.101.3Redis.assets7003> HGET itemsRedis.assets1001 id "3" 192.168.101.3Redis.assets7003> HGETALL itemsRedis.assets1001 1) "id" 2) "3" 3) "name" 4) "apple" 5) "price" 6) "999.9"
3.29.19.5 案例练习
import redis.clients.jedis.Jedis;
import java.util.Map;
public class BuyCart {
private static Jedis jedis = new Jedis("node02", 6379);
// 添加商品
public static void addProductCart() {
jedis.hset("cart:user001", "T恤", "2");
jedis.hset("cart:user002", "手机", "5");
jedis.hset("cart:user002", "电脑", "1");
jedis.close();
}
// 查询购物车信息
public static void getProductInfo() {
String pForUser001 = jedis.hget("cart:user001", "T恤");
System.out.println(pForUser001);
Map<String, String> pForUser002 = jedis.hgetAll("cart:user002");
for (Map.Entry<String, String> entry : pForUser002.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
jedis.close();
}
// 修改购物车信息
public static void editProductInfo() {
jedis.hset("cart:user001", "T恤", "1");
jedis.hincrBy("cart:user002", "电脑", 2);
jedis.close();
}
public static void main(String[] args) {
// addProductCart();
// getProductInfo();
editProductInfo();
}
}
3.29.20 List类型
List是有序可重复的集合
3.29.20.1 ArrayList与LinkedList的区别
ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
LinkedList使用双向链接方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快,然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。
总结
arrayList在进行增删改时很麻烦
linkedList则无该问题,redis的list类型存储时采用linkedlist
redis存储list类型可以实现队列和堆栈,队列是先进先出,而堆栈是先进后出。
3.29.20.2 命令
-
从左边存值(堆栈)
129.0.0.1Redis.assets6379> lpush list1 1 2 3 4 5 6 (integer) 6
-
从右边存值(队列)
129.0.0.1Redis.assets6379> rpush list1 a b c d (integer) 10
-
查看List值
129.0.0.1Redis.assets6379> lrange list1 0 3 1) "6" 2) "5" 3) "4" 4) "3"
如果查看全部,使用以下命令:
129.0.0.1Redis.assets6379> lrange list1 0 -1 1) "6" 2) "5" 3) "4" 4) "3" 5) "2" 6) "1" 7) "a" 8) "b" 9) "c" 10) "d"
-
从两端弹出值
129.0.0.1Redis.assets6379> lpush list1 1 2 3 4 5 6 (integer) 6 129.0.0.1Redis.assets6379> lrange list1 0 -1 1) "6" 2) "5" 3) "4" 4) "3" 5) "2" 6) "1" 129.0.0.1Redis.assets6379> lpop list1 "6" 129.0.0.1Redis.assets6379> lrange list1 0 -1 1) "5" 2) "4" 3) "3" 4) "2" 5) "1" 129.0.0.1Redis.assets6379> rpop list1 "1" 129.0.0.1Redis.assets6379> lrange list1 0 -1 1) "5" 2) "4" 3) "3" 4) "2"
-
获取列表的长度
129.0.0.1Redis.assets6379> llen list1 (integer) 4
3.29.20.3 其他命令(自学)
-
删除列表中指定的值
LREM key count value
LREM命令会删除列表中前count个值为value的元素,返回实际删除的元素个数。根据count值的不同,该命令的执行方式会有所不同:
当count>0时, LREM会从列表左边开始删除。
当count<0时, LREM会从列表后边开始删除。
当count=0时,LREM删除所有值为value的元素。
-
获得/设置指定索引的元素值
LINDEX key index
LSET key index value
129.0.0.1Redis.assets6379> lindex lRedis.assetslist 2 "1" 129.0.0.1Redis.assets6379> lset lRedis.assetslist 2 2 OK 129.0.0.1Redis.assets6379> lrange lRedis.assetslist 0 -1 1) "6" 2) "5" 3) "2" 4) "2"
-
只保留列表指定片段,指定范围和LRANGE一致
LTRIM key start stop
129.0.0.1Redis.assets6379> lrange lRedis.assetslist 0 -1 1) "6" 2) "5" 3) "0" 4) "2" 129.0.0.1Redis.assets6379> ltrim lRedis.assetslist 0 2 OK 129.0.0.1Redis.assets6379> lrange lRedis.assetslist 0 -1 1) "6" 2) "5" 3) "0"
-
向列表中插入元素
LINSERT key BEFORE|AFTER pivot value
该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。
129.0.0.1Redis.assets6379> lrange list 0 -1 1) "3" 2) "2" 3) "1" 129.0.0.1Redis.assets6379> linsert list after 3 4 (integer) 4 129.0.0.1Redis.assets6379> lrange list 0 -1 1) "3" 2) "4" 3) "2" 4) "1"
-
将元素从一个列表转移到另一个列表中
RPOPLPUSH source destination
129.0.0.1Redis.assets6379> rpoplpush list newlist "1" 129.0.0.1Redis.assets6379> lrange newlist 0 -1 1) "1" 129.0.0.1Redis.assets6379> lrange list 0 -1 1) "3" 2) "4" 3) "2"
3.29.20.4 案例练习
import redis.clients.jedis.Jedis;
import java.util.Random;
import java.util.UUID;
public class TaskProducer {
private static Jedis jedis = new Jedis("node02", 6379);
public static void main(String[] args) throws Exception {
Random r = new Random();
while (true) {
int nextInt = r.nextInt(1000);
Thread.sleep(2000 + nextInt);
String taskId = UUID.randomUUID().toString();
jedis.lpush("task-queue1", taskId);
System.out.println("生成一条任务信息:" + taskId);
}
}
}
import redis.clients.jedis.Jedis;
import java.util.Random;
public class TaskConsumer {
private static Jedis jedis = new Jedis("node02", 6379);
public static void main(String[] args) throws Exception {
Random r = new Random();
while (true) {
Thread.sleep(2000);
// 从task-queue1任务队列里取出一个任务,放到暂存队列里
String taskId = jedis.rpoplpush("task-queue1", "temp-queue1");
// 模拟处理任务
if (r.nextInt(19) % 9 == 0) {
// 任务处理失败,需要把任务信息从暂存队列里弹出来再放到任务队列里等待继续消费
jedis.rpoplpush("temp-queue1", "task-queue1");
System.out.println("任务处理失败:" + taskId);
} else {
jedis.rpop("temp-queue1");
System.out.println("任务处理成功:" + taskId);
}
}
}
}
3.29.21 Set类型
Set类型的数据是有序且不可重复。
3.29.21.1 命令
-
添加元素
129.0.0.1Redis.assets6379> sadd set1 1 2 3 3 4 5 5 (integer) 5
-
删除元素
129.0.0.1Redis.assets6379> sadd set1 1 2 3 3 4 5 5 (integer) 5 129.0.0.1Redis.assets6379> srem set1 3 (integer) 1 129.0.0.1Redis.assets6379> smembers set1 1) "1" 2) "2" 3) "4" 4) "5"
-
查看元素
129.0.0.1Redis.assets6379> smembers set1 1) "1" 2) "2" 3) "4" 4) "5"
-
判断元素是否存在
129.0.0.1Redis.assets6379> sismember set1 6 (integer) 0
3.29.21.2 运算命令
-
差集运算
129.0.0.1Redis.assets6379> sadd set3 2 3 4 (integer) 3 129.0.0.1Redis.assets6379> sadd set4 1 2 3 (integer) 3 129.0.0.1Redis.assets6379> sdiff set4 set3 1) "1" 129.0.0.1Redis.assets6379> sdiff set3 set4 1) "4"
-
交集运算
129.0.0.1Redis.assets6379> sinter set3 set4 1) "2" 2) "3"
-
并集运算
129.0.0.1Redis.assets6379> sunion set3 set4 1) "1" 2) "2" 3) "3" 4) "4"
3.29.21.3 其他命令(自学)
-
获得集合中元素的个数
SCARD key
129.0.0.1Redis.assets6379> smembers setA 1) "1" 2) "2" 3) "3" 129.0.0.1Redis.assets6379> scard setA (integer) 3
-
从集合中弹出一个元素
SPOP key
129.0.0.1Redis.assets6379> spop setA "1“
注意:由于集合是无序的,所有SPOP命令会从集合中随机选择一个元素弹出
3.29.22 SortedSet类型zset
Zset在设置时,会给设置一个分数,通过分数,可以进行排序。
3.29.22.1 命令
-
添加元素
129.0.0.1Redis.assets6379> zadd zset1 1 haha 2 hehe 0 heihei (integer) 3
-
删除元素
129.0.0.1Redis.assets6379> zrem zset1 haha (integer) 1
-
获得排名在某个范围的元素列表
129.0.0.1Redis.assets6379> zrange zset1 0 3 1) "heihei" 2) "hehe" 129.0.0.1Redis.assets6379> zrevrange zset1 0 3 1) "hehe" 2) "heihei" 129.0.0.1Redis.assets6379> zrevrange zset1 0 3 withscores 1) "hehe" 2) "2" 3) "heihei" 4) "0"
3.29.22.2 其他命令(自学)
-
获得指定分数范围的元素
ZRANGEBYSCORE key min max [WITHSCORES]
[LIMIT offset count]
129.0.0.1Redis.assets6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES 1) "wangwu" 2) "94" 3) "lisi" 4) "97" 129.0.0.1Redis.assets6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2 1) "wangwu" 2) "lisi"
-
增加某个元素的分数,返回值是更改后的分数。
ZINCRBY key increment member
129.0.0.1Redis.assets6379> ZINCRBY scoreboard 4 lisi "101“
-
获得集合中元素的数量
ZCARD key
129.0.0.1Redis.assets6379> ZCARD scoreboard (integer) 3
-
获得指定分数范围内的元素个数
ZCOUNT key min max
129.0.0.1Redis.assets6379> ZCOUNT scoreboard 80 90 (integer) 1
-
按照排名范围删除元素
ZREMRANGEBYRANK key start stop
129.0.0.1Redis.assets6379> ZREMRANGEBYRANK scoreboard 0 1 (integer) 2 129.0.0.1Redis.assets6379> ZRANGE scoreboard 0 -1 1) "lisi"
-
按照分数范围删除元素
ZREMRANGEBYSCORE key min max
129.0.0.1Redis.assets6379> zadd scoreboard 84 zhangsan (integer) 1 129.0.0.1Redis.assets6379> ZREMRANGEBYSCORE scoreboard 80 100 (integer) 1
-
获取元素的排名
从小到大
ZRANK key member
129.0.0.1Redis.assets6379> ZRANK scoreboard lisi (integer) 0
从大到小
ZREVRANK key member
129.0.0.1Redis.assets6379> ZREVRANK scoreboard zhangsan (integer) 1
3.29.22.3 应用:商品销售排行榜
根据商品销售量对商品进行排行显示,定义sorted set集合,商品销售量为元素的分数。
定义商品销售排行榜key:itemsRedis.assetssellsort
写入商品销售量:
商品编号1001的销量是9,商品编号1002的销量是10
192.168.101.3Redis.assets7007> ZADD itemsRedis.assetssellsort 9 1001 10 1002
商品编号1001的销量加1
192.168.101.3Redis.assets7001> ZINCRBY itemsRedis.assetssellsort 1 1001
商品销量前10名:
192.168.101.3Redis.assets7001> ZRANGE itemsRedis.assetssellsort 0 9 withscores
3.29.22.4 案例练习
import redis.clients.jedis.Jedis;
import java.util.Random;
public class LolBoxPlayer {
private static Jedis jedis = new Jedis("node02", 6379);
public static void main(String[] args) throws Exception {
Random r = new Random();
String[] heros = {"盖伦","轮子妈","蒙多","亚索","木木","刀妹","提莫","炼金"};
while (true) {
int index = r.nextInt(heros.length);
String hero = heros[index];
Thread.sleep(500);
// 给英雄每次出场的分数加1,如果初次出场,zincrby方法会自动创建
jedis.zincrby("hero:ccl", 1, hero);
System.out.println(hero + "出场了");
}
}
}
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import java.util.Set;
public class LolBoxViewer {
private static Jedis jedis = new Jedis("node02", 6379);
public static void main(String[] args) throws Exception {
// 计数器
int i = 1;
while (true) {
Thread.sleep(3000);
System.out.println("第" + i + "次查看榜单");
// 从redis获取榜单排名信息,取5名
Set<Tuple> heros = jedis.zrevrangeWithScores("hero:ccl", 0, 4);
for (Tuple hero: heros) {
System.out.println(hero.getElement() + "---------------" + hero.getScore());
}
i ++;
System.out.println("");
}
}
}
第五节 Keys 命令
3.29.23 Keys 命令
- Keys * 查看所有的key
3.29.24 设置Key的生存时间
Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。
EXPIRE key seconds 设置key的生存时间(单位:秒)key在多少秒后会自动删除
TTL key 查看key**剩余的生存时间
PERSIST key 清除生存时间
PEXPIRE key milliseconds 生存时间设置单位为:毫秒
例子:
设置test的值为1
192.168.101.3Redis.assets7002> set test 1
OK
获取test的值
192.168.101.3Redis.assets7002> get test
"1"
设置test的生存时间为5秒
192.168.101.3Redis.assets7002> EXPIRE test 5
(integer) 1
查看test的生成时间,还有1秒删除
192.168.101.3Redis.assets7002> TTL test
(integer) 1
192.168.101.3Redis.assets7002> TTL test
(integer) -2
获取test的值,已经删除
192.168.101.3Redis.assets7002> get test
(nil)
3.29.24.1 其他命令(自学)
-
keys
返回满足给定pattern 的所有key
redis 129.0.0.1Redis.assets6379> keys mylist* 1) "mylist" 2) "mylist5" 3) "mylist6" 4) "mylist7" 5) "mylist8"
-
exists
确认一个key 是否存在
redis 129.0.0.1Redis.assets6379> exists HongWan (integer) 0 redis 129.0.0.1Redis.assets6379> exists age (integer) 1
从结果来看数据库中不存在HongWan 这个key,但是age 这个key 是存在的
-
del
删除一个key
redis 129.0.0.1Redis.assets6379> del age (integer) 1 redis 129.0.0.1Redis.assets6379> exists age (integer) 0
-
rename
重命名key
redis 129.0.0.1Redis.assets6379[1]> keys * 1) "age" redis 129.0.0.1Redis.assets6379[1]> rename age age_new OK redis 129.0.0.1Redis.assets6379[1]> keys * 1) "age_new"
age 成功的被我们改名为age_new 了
-
type
返回值的类型
redis 129.0.0.1Redis.assets6379> type addr string redis 129.0.0.1Redis.assets6379> type myzset2 zset redis 129.0.0.1Redis.assets6379> type mylist list
这个方法可以非常简单的判断出值的类型
第六节 3.29.25 Redis持久化
3.29.26 rdb方式
RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
save 900 1
save 300 10
save 60 10000
save开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系,“save 900 1”表示15分钟(900秒钟)内至少1个键被更改则进行快照,“save 300 10”表示5分钟(300秒)内至少10个键被更改则进行快照。
Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。
问题总结:
通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。
3.29.29 AOF方式
aof是默认不开启的,需要手动设置。
如果rdb方式和aof方式同时使用的话,那么默认从aof文件中加载数据。
第七节 3.29.28 Redis主从复制(重要)
为了高可用,引入Redis的主从复制的概念。
3.29.29 准备工作
完成主从复制,最少需要两台服务器,讲学方便,在一台服务器中演示即可。
但是一台服务器中需要启动两个Redis
第一步:复制一个Redis
[root@redis01 redis0707]# cp bin/ bin2 –r
第二步:修改端口
将bin2目录下的redis.conf文件中的端口修改为6380
3.29.30 主机配置
无需配置
3.29.30.1 从机配置
3.29.31 主从启动及操作
从机是只读的。
3.29.32 Redis的事务功能
了解(自学)
第八节 Redis集群
3.29.33 Redis集群
3.29.34 Redis-Cluster架构图
1、 集群通信是通过“ping-pong”机制进行通信;
2、 客户端不需要将所有的节点都连接上,只需要连接其中一个节点即可。
3、 集群中存储数据是存储到一个个的槽中,集群中槽的个数是固定的:16384,槽的编号是【0-16383】。在集群中存储数据时,会根据key进行计算,计算出一个结果,然后将这个结果和16384取余,余数就是这个key将要存储的槽的编号。
注意:槽的编号之间不能断开。
槽的计算会将数据保存的很平均,不会产生一个槽满一个槽空的情况。
3.29.35 Redis-Cluster投票:容错
什么时候整个集群不可用(cluster_stateRedis.assetsfail)?
aRedis.assets如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态…
bRedis.assets如果集群超过半数以上master挂掉,无论是否有slave集群进入fail状态.
3.29.36 集群搭建
第九节 Jedis连接集群
3.29.41 Jedis连接集群
public static void main(String[] args) {
//节点设置
HashSet<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("node01", 7001));
nodes.add(new HostAndPort("node02", 7002));
nodes.add(new HostAndPort("node03", 7003));
nodes.add(new HostAndPort("node04", 7004));
nodes.add(new HostAndPort("node05", 7005));
nodes.add(new HostAndPort("node06", 7006));
//创建JedisCluster实例
JedisCluster cluster = new JedisCluster(nodes);
cluster.set("ruhua", "rightgirl");
String s = cluster.get("ruhua");
System.out.println(s);
cluster.close();
}
改为6380
[外链图片转存中…(img-qcCSovxf-1594184214485)]
3.29.30 主机配置
无需配置
3.29.30.1 从机配置
3.29.31 主从启动及操作
[外链图片转存中…(img-jdzPaVnu-1594184214487)]
从机是只读的。
3.29.32 Redis的事务功能
了解(自学)
第八节 Redis集群
3.29.33 Redis集群
3.29.34 Redis-Cluster架构图
[外链图片转存中…(img-hEQr6Wiy-1594184214488)]
1、 集群通信是通过“ping-pong”机制进行通信;
2、 客户端不需要将所有的节点都连接上,只需要连接其中一个节点即可。
3、 集群中存储数据是存储到一个个的槽中,集群中槽的个数是固定的:16384,槽的编号是【0-16383】。在集群中存储数据时,会根据key进行计算,计算出一个结果,然后将这个结果和16384取余,余数就是这个key将要存储的槽的编号。
注意:槽的编号之间不能断开。
槽的计算会将数据保存的很平均,不会产生一个槽满一个槽空的情况。
3.29.35 Redis-Cluster投票:容错
什么时候整个集群不可用(cluster_stateRedis.assetsfail)?
aRedis.assets如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态…
bRedis.assets如果集群超过半数以上master挂掉,无论是否有slave集群进入fail状态.
3.29.36 集群搭建
第九节 Jedis连接集群
3.29.41 Jedis连接集群
public static void main(String[] args) {
//节点设置
HashSet<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("node01", 7001));
nodes.add(new HostAndPort("node02", 7002));
nodes.add(new HostAndPort("node03", 7003));
nodes.add(new HostAndPort("node04", 7004));
nodes.add(new HostAndPort("node05", 7005));
nodes.add(new HostAndPort("node06", 7006));
//创建JedisCluster实例
JedisCluster cluster = new JedisCluster(nodes);
cluster.set("ruhua", "rightgirl");
String s = cluster.get("ruhua");
System.out.println(s);
cluster.close();
}