Redis1

Redis

Redis介绍

什么是Redis

Redis是用C语言开发的一个开源的高性能键值对(key-value )内存数据库 ,它是一种NoSQL
数据库

它是【单进程单线程】的内存数据库,所以说不存在线程安全问题

它可以支持并发10WQPS,所以说性能非常优秀。之所以单进程单线程性能还这么好,是因为底层采用了【IO多路复用(NIO思想)】

相比Memcache这种专业缓存技术,Redis有更优秀的读写性能,及丰富的数据类型

它提供了五种数据类型来存储【值】:字符串类型(string)、散列类型(hash)、列表类型(list)、集合类型(set)、有序集合类型(sortedset、zset)

Redis官网

官网地址:http://redis.io/

中文官网地址:http://www.redis.cn/

下载地址:http://download.redis.io/releases/

什么是NoSQL

NoSQL ,即Not-Only SQL (不仅仅是SQL ),泛指非关系型的数据库 。

什么是关系型数据库?数据结构是一种有行有列的数据库。

NoSQL 数据库是为了解决高并发、高可用、高可扩展、大数据存储 问题而产生的数据库解决方案。

NoSQL 可以作为关系型数据库的良好补充,但是不能替代关系型数据库 。

数据库类型 特性 优点 缺点
关系型数据库SQLite、Oracle、mysql 1、关系型数据库,是指采用了关系模型来组织
数据的数据库;
2、关系型数据库的最大特点就是事务的一致性;
3、简单来说,关系模型指的就是二维表格模型,
而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织
1、容易理解:二维表结构是非常贴近逻辑世界一个概念,关系模型相对网状、层次等其他模型来说更容易理解;
2、使用方便:通用的SQL语言使得操作关系型数据库非常方便;
3、易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率;
4、支持SQL,可用于复杂的查询。
1、为了维护一致性所付出的巨大代价就是其读写性能比较差;
2、固定的表结构;
3、高并发读写需求;
4、海量数据的高效率读写;
非关系型数据库
MongoDb、redis、HBase
1、使用键值对存储数据;
2、分布式;
3、一般不支持ACID特性;
4、非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合。
1、无需经过sql层的解析,读写性能很高;
2、基于键值对,数据没有耦合性,容易扩展;
3、存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,而关系型数据库则只支持基础类型。
1、不提供sql支持,学习和使用成本较高;
2、无事务处理,附加功能bi和报表等支持也不好;

MySQL(关系型数据库)—> NoSQL —> NewSQL(TiDB)

NoSQL数据库分类

键值(Key-Value)存储数据库

相关产品:Tokyo Cabinet/Tyrant 、Redis、Voldemort 、Berkeley DB

典型应用:内容缓存,主要用于处理大量数据的高访问负载。

数据模型:一系列键值对

优势:快速查询

劣势:存储的数据缺少结构化

列存储数据库

相关产品:Cassandra 、HBase、 Riak

典型应用:分布式的文件系统

数据模型:以列簇式存储,将同一列数据存在一起

优势:查找速度快,可扩展性强,更容易进行分布式扩展

劣势:功能相对局限

文档型数据库

相关产品:CouchDB 、MongoDB

典型应用:Web 应用(与Key-Value 类似,Value 是结构化的)

数据模型:一系列键值对

优势:数据结构要求不严格

劣势:文档型数据库一般都是以行为基础得,添加更新删除等操作,因为文档型数据的一般存储结构都是键值对。所以对列的概念很弱,比如有一些文档数据库,是无法对单列操作的,有时候想修改一列的值,只能所有数据全部重新添加一遍。不过其实这也是文档数据库的特色

图形(Graph )数据库

相关数据库:Neo4J 、InfoGrid 、Infinite Graph

典型应用:社交网络

数据模型:图结构

优势:利用图结构相关算法

劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案

Redis发展历史

2008 年,意大利的一家创业公司Merzia 推出了一款基于MySQL 的网站实时统计系统LLOOGG ,然而没过多久该公司的创始人Salvatore Sanfilippo便对MySQL 的性能感到失望,于是他决定亲自为LLOOGG 量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。

不过Salvatore Sanfilippo 并不满足只将Redis 用于LLOOGG 这一款产品,而是希望更多的人使用它,于是在同一年 Salvatore Sanfilippo将Redis开源发布 ,并开始和Redis 的另一名主要的代码贡献者PieterNoordhuis 一起继续着Redis的开发,直到今天 。

Salvatore Sanfilippo 自己也没有想到,短短的几年时间,Redis 就拥有了庞大的用户群体。Hacker News 在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、街旁网、知乎网,国外如GitHub、Stack Overflow、Flickr等都是Redis的用户。

VMware 公司从2010年 开始赞助Redis 的开发,Salvatore Sanfilippo 和Pieter Noordhuis 也分别在3月和5月加入VMware,全职开发Redis。

Redis应用场景

内存数据库 (登录信息、购物车信息、用户浏览记录等)

缓存服务器 (商品数据、广告数据等等)(最多使用 )

解决分布式集群架构中的session 分离问题(session 共享)

任务队列(秒杀、抢购、12306等等)

分布式锁的实现

支持发布订阅的消息模式

应用排行榜(有序集合)

网站访问统计

数据过期处理(可以精确到毫秒)

Redis单机版安装配置

Redis下载

官网地址:http://redis.io/
中文官网地址:http://www.redis.cn/
下载地址:http://download.redis.io/releases/

Redis安装环境

Redis没有官方的Windows版本,所以建议在Linux系统上安装运行,我们使用CentOS 7 (Linux操作系统的一个系列)作为安装环境。

Redis安装

第一步:安装C 语言需要的GCC 环境

1
2
yum install -y gcc-c++  
yum install -y wget

第二步:下载并解压缩Redis 源码压缩包

1
2
wget http://download.redis.io/releases/redis-5.0.4.tar.gz
tar -zxf redis-5.0.4.tar.gz

第三步:编译Redis 源码,进入redis-3.2.9 目录,执行编译命令

1
2
cd redis-5.0.4
make

第四步:安装Redis ,需要通过PREFIX 指定安装路径

1
make install PREFIX=/kkb/redis

注:如果曾经安装过redis,则不仅要卸载redis,还要卸载redis在/kkb/redis中的文件

rm -rf redis

卸载Redis

1
rm -rf redis-5.0.4

Redis启动

前端启动

启动命令:redis-server,直接运行bin/redis-server 将以前端模式启动

1
./redis-server

关闭命令:ctrl+c

启动缺点:客户端窗口关闭则redis-server 程序结束,不推荐使用此方法

启动图例:

后端启动(守护进程启动)

第一步:拷贝redis-5.0.4/redis.conf 配置文件到Redis 安装目录的bin目录redis/bin

1
cp /root/redis-5.0.4/redis.conf /kkb/redis/bin/

第二步:修改redis.conf

1
vim redis.conf
1
2
3
4
5
6
7
8
# 将`daemonize`由`no`改为`yes`
daemonize yes
 
# 默认绑定的是回环地址,默认不能被其他机器访问
# bind 127.0.0.1
 
# 是否开启保护模式,由yes该为no
protected-mode no

第三步:启动服务

直接在/kkb/redis/bin目录下

1
./redis-server redis.conf

查看redis进程

1
ps -ef | grep redis

image-20200401091511962

后端启动的关闭方式

在/kkb/redis/bin目录下

1
./redis-cli shutdown

其他命令说明

redis-server :启动redis 服务

redis-cli :进入redis 命令客户端

redis-benchmark :性能测试的工具

redis-check-aof :aof 文件进行检查的工具

redis-check-dump :rdb 文件进行检查的工具

redis-sentinel :启动哨兵监控服务

Redis客户端

Redis命令行客户端

命令格式

在/kkb/redis/bin目录下

1
./redis-cli -h 127.0.0.1 -p 6379

image-20200401173857193

参数说明
1
2
-h:redis服务器的ip地址
-p:redis实例的端口号
默认方式

如果不指定主机和端口也可以

  • 默认主机地址是127.0.0.1
  • 默认端口是6379
1
./redis-cli

Java客户端Jedis

Jedis介绍

Redis不仅使用命令来操作,而且可以使用程序客户端操作。现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等

在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和
Redisson。在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。

Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<dependencies>
<!-- redis自身的依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<!-- 单元测试Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Maven的JDK编译级别-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>

单实例连接

如果想连接Redis,就必须先创建Jedis,Jedis可以理解为一个客户端,Jedis中传入端口号和ip地址

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testJedis() {
//创建一个Jedis的连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
//执行redis命令
jedis.set("mytest", "hello world, this is jedis client!");
//从redis中取值
String result = jedis.get("mytest");
//打印结果
System.out.println(result);
//关闭连接
jedis.close();
}

连接池连接

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testJedisPool() {
//创建一连接池对象
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//从连接池中获得连接
Jedis jedis = jedisPool.getResource();
String result = jedis.get("mytest") ;
System.out.println(result);
//关闭连接
jedis.close();
//关闭连接池
jedisPool.close();
}

连接redis集群

创建JedisCluster类连接redis集群。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testJedisCluster() throws Exception {
//创建一连接,JedisCluster对象,在系统中是单例存在
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.242.129", 7001));
nodes.add(new HostAndPort("192.168.242.129", 7002));
nodes.add(new HostAndPort("192.168.242.129", 7003));
nodes.add(new HostAndPort("192.168.242.129", 7004));
nodes.add(new HostAndPort("192.168.242.129", 7005));
nodes.add(new HostAndPort("192.168.242.129", 7006));
JedisCluster cluster = new JedisCluster(nodes);
//执行JedisCluster对象中的方法,方法和redis一一对应。
cluster.set("cluster-test", "my jedis cluster test");
String result = cluster.get("cluster-test");
System.out.println(result);
//程序结束时需要关闭JedisCluster对象
cluster.close();
}

Jedis整合spring

配置spring配置文件applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 连接池配置-->
<bean id="jedisPoolConfig"
class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数-->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数-->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目-->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒)-->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间-->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值且空闲连接>最大空闲连接数时直接释放-->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis单机通过连接池-->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool"
destroy-method="close">
<constructor-arg name="poolConfig"
ref="jedisPoolConfig" />
<constructor-arg name="host" value="192.168.10.135" />
<constructor-arg name="port" value="6379" />
</bean>
<!-- redis集群-->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg index="0">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructorarg>
<constructor-arg index="1" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructorarg>
<constructor-arg index="1" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructorarg>
<constructor-arg index="1" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructorarg>
<constructor-arg index="1" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructorarg>
<constructor-arg index="1" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructorarg>
<constructor-arg index="1" value="7006"></constructor-arg>
</bean>
</set>
/constructor-arg>
<constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
</bean>
</beans>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.kkb.redis.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application.xml")
public class TestJedis2 {
@Autowired
private JedisPool jedisPool;
@Resource
private JedisCluster cluster;
@Test
public void testJedisPool() {
// 从连接池中获得连接
Jedis jedis = jedisPool.getResource();
String result = jedis.get("mytest");
System.out.println(result);
// 关闭连接
jedis.close();
}
@Test
public void testJedisCluster() throws Exception {
// 执行JedisCluster对象中的方法,方法和redis一一对应。
cluster.set("cluster-test", "my jedis cluster test");
String result = cluster.get("cluster-test");
System.out.println(result);
}
}

Redis数据类型

官方命令大全网址:http://www.redis.cn/commands.html

Redis 中存储数据是通过key-value 格式存储数据的,其中value 可以定义五种数据类型:

String(字符类型)

Hash(散列类型)

List(列表类型)

Set(集合类型)

SortedSet(有序集合类型,简称zset)

注意:在redis 中的命令语句中,命令是忽略大小写的,而key是不忽略大小写的。

image-20200401103034769

string类型命令

赋值

语法

1
SET key value

示例

1
2
127.0.0.1:6379> set test 123  
OK

取值

语法

1
GET key
1
127.0.0.1:6379> get test "123“

示例

1
2
127.0.0.1:6379> get test
"123“
取值并赋值

语法

1
GETSET key value

示例

1
2
3
4
127.0.0.1:6379> getset s2 222
"111"
127.0.0.1:6379> get s2
"222"

数值增减

重要,在redis使用场景中,商品id或者其的分布式系统下的如果要生成唯一id使用这个命令是很不错的

因为redis是单线程的,它不存在id重复的问题,其次在内存操作,性能有所保证,而且可以指定增减数值

注意事项

1、当value为整数数据时,才能使用以下命令操作数值的增减。

2、数值递增都是【原子】操作。

3、redis中的每一个单独的命令都是原子性操作。当多个命令一起执行的时候,就不能保证原子性,不过我们可以使用事务和lua脚本来保证这一点。

非原子性操作示例

1
2
3
int i = 1;
i++;
System.out.println(i)
递增数字

语法(increment)

1
INCR key

示例

1
2
3
4
5
6
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> incr num
(integer) 3
增加指定的整数

语法

1
INCRBY key increment

示例

1
2
3
4
5
6
127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 2
(integer) 7
127.0.0.1:6379> incrby num 2
(integer) 9
递减数值

语法

1
DECR key

示例

1
2
3
4
5
6
127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> incr num
(integer) 3
减少指定的整数

语法

1
DECRBY key decrement

示例

1
2
3
4
5
6
7
8
127.0.0.1:6379> decr num
(integer) 6
127.0.0.1:6379> decr num
(integer) 5
127.0.0.1:6379> decrby num 3
(integer) 2
127.0.0.1:6379> decrby num 3
(integer) -1

注:

分布式数据库中的订单ID如何保证唯一且连续

这时候就不能使用普通数据库的自增主键来进行存储,可以在应用层中使用一些算法或者手段来生成订单ID然后插入到数据库中

这样的方案有很多,redis的incr命令、snowflake雪花算法等都可以实现这样的操作

仅当不存在时赋值

使用该命令可以实现【分布式锁】的功能,后续讲解

语法

1
setnx key value

示例

1
2
3
4
5
6
7
8
redis> EXISTS job                # job 不存在
(integer) 0 #如果不存在则返回0
redis> SETNX job "programmer"    # job 设置成功
(integer) 1 #成功返回1,失败返回0
redis> SETNX job "code-farmer"   # 尝试覆盖job ,失败
(integer) 0
redis> GET job                   # 没有被覆盖
"programmer"

其它命令

向尾部追加值

APPEND 命令,向键值的末尾追加value

如果键不存在则将该键的值设置为value ,即相当于SET key value 。返回值是追加后字符串的总长度。

语法

1
APPEND key value

示例

1
2
3
4
5
6
127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> append str " world!"
(integer) 12
127.0.0.1:6379> get str
"hello world!"
获取字符串长度

STRLEN 命令,返回键值的长度,如果键不存在则返回0

语法

1
STRLEN key

示例

1
2
3
4
5
6
127.0.0.1:6379> strlen str
(integer) 0
127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> strlen str
(integer) 5

同时设置/获取多个键值

语法

1
2
MSET key value [key value …]
MGET key [key …]

示例

1
2
3
4
5
6
7
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> mget k1 k3
1) "v1"
2) "v3"
应用场景之自增主键

需求:商品编号、订单号采用INCR命令生成。

设计:key 命名要有一定的设计

实现: 定义商品编号key为items:id

1
2
3
4
192.168.101.3:7003> INCR items:id
(integer) 2
192.168.101.3:7003> INCR items:id
(integer) 3

hash类型命令

hash 类型也叫散列类型 ,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:

image-20200401111454003

赋值

HSET 命令不区分插入和更新操作,当执行插入操作时HSET 命令返回1 ,当执行更新操作时返回0

设置一个字段值

语法

1
HSET key field value

示例

1
2
127.0.0.1:6379> hset user username zhangsan
(integer) 1
设置多个字段值

语法

1
HMSET key field value [field value ...]

示例

1
2
127.0.0.1:6379> hmset user age 20 username lisi
OK
当字段不存在时赋值

类似HSET ,区别在于如果字段存在,该命令不执行任何操作

语法

1
HSETNX key field value

示例

1
2
127.0.0.1:6379> hsetnx user age 30 # 如果user中没有age字段则设置age值为30,否则不做任何操作
(integer) 0

取值

获取一个字段值

语法

1
HGET key field

示例

1
2
127.0.0.1:6379> hget user username  
"zhangsan“
获取多个字段值

语法

1
HMGET key field [field ...]

示例

1
2
3
127.0.0.1:6379> hmget user age username  
1) "20"  
2) "lisi"
获取所有字段值

语法

1
HGETALL key

示例

1
2
3
4
5
127.0.0.1:6379> hgetall user
1) "age"
2) "20"
3) "username"
4) "lisi"

删除字段

可以删除一个或多个字段,返回值是被删除的字段个数

语法

1
HDEL key field [field ...]

示例

1
2
3
4
5
6
127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hdel user age name
(integer) 0
127.0.0.1:6379> hdel user age username
(integer) 1

增加数字

语法

1
HINCRBY key field increment

示例

1
2
3
4
127.0.0.1:6379> hincrby user age 2 # 将用户的年龄加2
(integer) 22
127.0.0.1:6379> hget user age # 获取用户的年龄
"22“

其它命令

判断字段是否存在

语法

1
HEXISTS key field

示例

1
2
3
4
127.0.0.1:6379> hexists user age #查看user中是否有age字段
(integer) 1
127.0.0.1:6379> hexists user name #查看user中是否有name字段
(integer) 0
只获取字段名或字段值

语法

1
2
HKEYS key
HVALS key

示例

1
2
3
4
5
6
7
8
127.0.0.1:6379> hmset user age 20 name lisi
OK
127.0.0.1:6379> hkeys user
1) "age"
2) "name"
127.0.0.1:6379> hvals user
1) "20"
2) "lisi"
获取字段数量

语法

1
HLEN key

示例

1
2
127.0.0.1:6379> hlen user  
(integer) 2
获取所有字段

获得hash 的所有信息,包括key 和value

语法

1
hgetall key

示例

1
2
3
4
5
127.0.0.1:6379> hgetall student
1) "username"
2) "zhangsan"
3) "password"
4) "123"

string类型和hash类型的区别

hash类型适合存储对象数据,特别是对象属性经常发生【增删改】操作的数据。

string类型也可以存储对象数据,将java对象转成json字符串进行存储,这种存储适合【查询】操作。

应用之存储商品信息

商品信息字段

1
【商品id、商品名称、商品描述、商品库存、商品好评】

定义商品信息的key

1
商品ID为1001的信息在Redis中的key为:[items:1001]

存储商品信息

1
2
192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9
OK

获取商品信息

1
2
3
4
5
6
7
8
9
192.168.101.3:7003> HGET items:1001 id
"3"
192.168.101.3:7003> HGETALL items:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "price"
6) "999.9"

list类型

ArrayList与LinkedList的区别

ArrayList 使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,
所以比较慢。

LinkedList 使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。

image-20200401113133808

image-20200401113139635

list类型介绍

Redis 的列表类型(list 类型,类似于LinkedList)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。

列表类型内部是使用双向链表(double linked list) 实现的,所以向列表两端添加元素的时间复杂度为0(1) ,获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。

LPUSH/RPUSH

L:左,R:右

语法

1
2
LPUSH key value [value ...]
RPUSH key value [value ...]

示例

1
2
3
4
127.0.0.1:6379> lpush list:1 1 2 3  
(integer) 3  
127.0.0.1:6379> rpush list:1 4 5 6  
(integer) 3
LRANGE

获取列表中的某一片段。将返回startstop之间的所有元素(包含两端的元素),索引从0开始。索引可以
是负数,如:“-1”代表最后边的一个元素

语法

1
LRANGE key start stop

示例

1
2
3
4
127.0.0.1:6379> lrange   list:1 0 2  
1) "2"  
2) "1"  
3) "4"
LPOP/RPOP

从列表两端弹出元素

从列表弹出一个元素,会分两步完成:

  • 第一步是将列表左边的元素从列表中移除
  • 第二步是返回被移除的元素值。

语法

1
2
LPOP key
RPOP key

示例

1
2
3
4
127.0.0.1:6379>lpop list:1  
"3“  
127.0.0.1:6379>rpop list:1  
"6“
LLEN

获取列表中元素的个数

语法

1
llen key

示例

1
2
127.0.0.1:6379> llen list:1  
(integer) 2

其它命令

LREM

删除列表中指定个数的值

LREM 命令会删除列表中前count 个值为value 的元素,返回实际删除的元素个数。根据count 值的不同,该命令的执行方式会有所不同

  • 当count>0时,LREM会从列表左边开始删除。
  • 当count<0时,LREM会从列表右边开始删除。
  • 当count=0时,LREM删除所有值为value的元素。

语法

1
LREM key count value

image-20200401140658996

image-20200401140750804

image-20200401141054455

image-20200401141156214

LINDEX

获得指定索引的元素值

语法

1
LINDEX key index

示例

1
2
127.0.0.1:6379>lindex l:list 2  
"1"

设置指定索引的元素值

语法

1
LSET key index value

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> lpush list:2 1 2 3 4 5 6 
(integer) 6
127.0.0.1:6379> lrange list:2 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> lset list:2 6 6
(error) ERR index out of range
127.0.0.1:6379> lset list:2 5 6
OK
127.0.0.1:6379> lrange list:2 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "6"

可以看出list的索引是从0开始而不是从1开始的

LTRIM

只保留列表指定片段,指定范围和LRANGE一致

语法

1
LTRIM key start stop

示例

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> lrange l:list 0 -1
1) "6"
2) "5"
3) "0"
4) "2"
127.0.0.1:6379> ltrim l:list 0 2
OK
127.0.0.1:6379> lrange l:list 0 -1
1) "6"
2) "5"
3) "0"
LINSERT

向列表中插入元素。

该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插入到该元素的前面还是后面。

语法

1
LINSERT key BEFORE|AFTER pivot value

示例

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> linsert list after 3 4
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "4"
3) "2"
4) "1"
RPOPLPUSH

将一个列表z中的第一个元素转移到另一个列表中

语法

1
RPOPLPUSH source destination

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
127.0.0.1:6379> lrange list:2 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> lset list:2 6 6
(error) ERR index out of range
127.0.0.1:6379> lset list:2 5 6
OK
127.0.0.1:6379> lrange list:2 0 -1
1) "6"
2) "5"
3) "4"
4) "3"
5) "2"
6) "6"
127.0.0.1:6379> lpush newlist:2 9 10 11
(integer) 3
127.0.0.1:6379> rpoplpush newlist:2 list:2
"9"
127.0.0.1:6379> lrange list:2 0 -1
1) "9"
2) "6"
3) "5"
4) "4"
5) "3"
6) "2"
7) "6"

应用之商品评论列表

需求

用户针对某一商品发布评论,一个商品会被不同的用户进行评论,存储商品评论时,要按时间顺序排序。

用户在前端页面查询该商品的评论,需要按照时间顺序降序排序。

分析

使用list存储商品评论信息,KEY是该商品的ID,VALUE是商品评论信息列表

实现

商品编号为1001 的商品评论key 【items: comment:1001 】

1
2
192.168.101.3:7001> LPUSH items:comment:1001   '{"id":1,"name":"商品不错,很
好!!","date":1430295077289}'

set类型

set类型介绍

set 类型即集合类型 ,其中的数据是不重复且没有顺序 。

集合类型和列表类型的对比

image-20200401125053234

集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis 内部是使用值
为空的散列表实现,所有这些操作的时间复杂度都为0(1) 。

Redis 还提供了多个集合之间的交集、并集、差集 的运算。

SADD/SREM

添加元素/删除元素

语法

1
2
SADD key member [member ...]
SREM key member [member ...]

示例

1
2
3
4
5
6
127.0.0.1:6379> sadd set a b c  
(integer) 3  
127.0.0.1:6379> sadd set a  
(integer) 0  
127.0.0.1:6379> srem set c d  
(integer) 1
SMEMBERS

获得集合中的所有元素

语法

1
SMEMBERS key

示例

1
2
3
127.0.0.1:6379> smembers set  
1)   "b"  
2)   "a”
SISMEMBER

判断元素是否在集合中

语法

1
SISMEMBER key member

示例

1
2
3
4
127.0.0.1:6379>sismember set a  
(integer)   1  
127.0.0.1:6379>sismember set h  
(integer)   0

集合运算命令

SDIFF

集合的差集运算A-B:属于A并且不属于B的元素构成的集合

image-20200401125342621

语法

1
SDIFF key [key ...]

示例

1
2
3
4
5
6
7
8
127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
1) "1"
127.0.0.1:6379> sdiff setB setA
1) "4"
SINTER

集合的交集运算A ∩ B:属于A且属于B的元素构成的集合

image-20200401125420951

语法

1
SINTER key [key ...]

示例

1
2
3
127.0.0.1:6379> sinter setA setB
1) "2"
2) "3"
SUNION

集合的并集运算A ∪B:属于A或者属于B的元素构成的集合

语法

1
SUNION key [key ...]
1
2
3
4
5
127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"

其它命令

SCARD

获得集合中元素的个数

语法

1
SCARD key

示例

1
2
3
4
5
6
127.0.0.1:6379> smembers setA
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> scard setA
(integer) 3
SPOP

从集合中弹出一个元素。

注意:由于集合是无序的,所有SPOP命令会从集合中随机选择一个元素弹出

语法

1
SPOP key

示例

1
2
127.0.0.1:6379> spop setA
"1“

zset类型 (sortedset)

zset介绍

在set 集合类型的基础上,有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。

在某些方面有序集合和列表类型有些相似:

1、二者都是有序的。

2、二者都可以获得某一范围的元素。

但是,二者有着很大区别

1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。

2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。

3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)

4、有序集合要比列表类型更耗内存。

ZADD

增加元素。

向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。

语法

1
ZADD key score member [score member ...]

示例

1
2
3
4
127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi
(integer) 0
ZRANGE/ZREVRANGE

获得排名在某个范围的元素列表。

  • ZRANGE:按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
  • ZREVRANGE:按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)

语法

1
2
ZRANGE key start stop [WITHSCORES]  
ZREVRANGE key start stop [WITHSCORES]

示例

1
2
3
4
5
6
7
8
127.0.0.1:6379> zrange scoreboard 0 2
1) "zhangsan"
2) "wangwu"
3) "lisi“
127.0.0.1:6379> zrevrange scoreboard 0 2
1) " lisi "
2) "wangwu"
3) " zhangsan “

如果需要获得元素的分数的可以在命令尾部加上WITHSCORES 参数

1
2
3
4
5
127.0.0.1:6379> zrange scoreboard 0 1 WITHSCORES
1) "zhangsan"
2) "80"
3) "wangwu"
4) "94"
ZSCORE

获取元素的分数

语法

1
ZSCORE key member

示例

1
2
127.0.0.1:6379> zscore scoreboard lisi
"97"
ZREM

删除元素。

移除有序集合key中的一个或多个成员,不存在的成员将被忽略。

当key存在但不是有序集类型时,返回一个错误。

语法

1
ZREM key member [member ...]

示例

1
2
127.0.0.1:6379> zrem scoreboard lisi
(integer) 1

其它命令

ZRANGEBYSCORE

获得指定分数范围的元素。

语法

1
ZRANGEBYSCORE key min max [WITHSCORES]

示例

1
2
3
4
5
6
7
8
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"
ZINCRBY

增加某个元素的分数。

返回值是更改后的分数。

语法

1
ZINCRBY key increment member

示例

1
2
127.0.0.1:6379> ZINCRBY scoreboard 4 lisi
"101“
ZCARD

获得集合中元素的数量。

语法

1
ZCARD key

示例

1
2
127.0.0.1:6379> ZCARD scoreboard  
(integer)   3
ZCOUNT

获得指定分数范围内的元素个数

语法

1
ZCOUNT key min max

示例

1
2
127.0.0.1:6379> ZCOUNT scoreboard 80 90  
(integer) 1
ZREMRANGEBYRANK

按照排名范围删除元素

语法

1
ZREMRANGEBYRANK key start stop

示例

1
2
3
4
127.0.0.1:6379> ZREMRANGEBYRANK scoreboard 0 1
(integer) 2
127.0.0.1:6379> ZRANGE scoreboard 0 -1
1) "lisi"
ZREMRANGEBYSCORE

按照分数范围删除元素

语法

1
ZREMRANGEBYSCORE key min max

示例

1
2
3
4
127.0.0.1:6379> zadd scoreboard 84 zhangsan
(integer) 1
127.0.0.1:6379> ZREMRANGEBYSCORE scoreboard 80 100
(integer) 1
ZRANK/ZREVRANK

获取元素的排名。

  • ZRANK:从小到大
  • ZREVRANK:从大到小

语法

1
2
ZRANK key member
ZREVRANK key member

示例

1
2
3
4
127.0.0.1:6379> ZRANK scoreboard lisi
(integer) 0
127.0.0.1:6379> ZREVRANK scoreboard zhangsan
(integer) 1

应用之商品销售排行榜

需求

根据商品销售量对商品进行排行显示

设计

定义商品销售排行榜(sorted set集合),Key为items:sellsort,分数为商品销售量。

写入商品销售量

商品编号1001 的销量是9 ,商品编号1002 的销量是10

1
192.168.101.3:7007>   ZADD items:sellsort 9 1001 10 1002

商品编号1001 的销量加1

1
192.168.101.3:7001>   ZINCRBY items:sellsort 1 1001

商品销量前10 名

1
192.168.101.3:7001>   ZREVRANGE items:sellsort 0 9 withscores

通用命令

keys

返回满足给定pattern 的所有key

语法

1
keys pattern

示例

1
2
3
4
5
6
redis 127.0.0.1:6379> keys mylist*
1) "mylist"
2) "mylist5"
3) "mylist6"
4) "mylist7"
5) "mylist8
del

语法

1
DEL key

示例

1
2
127.0.0.1:6379> del test  
(integer)   1
exists

确认一个key 是否存在

语法

1
exists key

示例:从结果来看,数据库中不存在HongWan 这个key ,但是age 这个key 是存在的

1
2
3
4
5
redis 127.0.0.1:6379> exists HongWan
(integer) 0
redis 127.0.0.1:6379> exists age
(integer) 1
redis 127.0.0.1:6379>
expire(重点)

Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。

语法

1
2
3
4
EXPIRE key seconds         设置key的生存时间(单位:秒)key在多少秒后会自动删除 
TTL key                 查看key生于的生存时间 
PERSIST key           清除生存时间     
PEXPIRE key milliseconds 生存时间设置单位为:毫秒

示例

1
2
3
4
5
6
7
8
9
10
11
12
192.168.101.3:7002> set test 1   设置test的值为1
OK
192.168.101.3:7002> get test 获取test的值
"1"
192.168.101.3:7002> EXPIRE test 5   设置test的生存时间为5秒
(integer) 1
192.168.101.3:7002> TTL test 查看test的生于生成时间还有1秒删除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test 获取test的值,已经删除
(nil
rename

重命名key

语法

1
rename oldkey newkey

示例:age 成功的被我们改名为age_new 了

1
2
3
4
5
6
7
redis 127.0.0.1:6379[1]> keys *
1) "age"
redis 127.0.0.1:6379[1]> rename age age_new
OK
redis 127.0.0.1:6379[1]> keys *
1) "age_new"
redis 127.0.0.1:6379[1]>
type

显示指定key的数据类型

语法

1
type key

示例

这个方法可以非常简单的判断出值的类型

1
2
3
4
5
6
7
redis 127.0.0.1:6379> type addr
string
redis 127.0.0.1:6379> type myzset2
zset
redis 127.0.0.1:6379> type mylist
list
redis 127.0.0.1:6379>

Redis消息模式

队列模式

使用list类型的lpush和rpop 实现消息队列

文章目录
  1. 1. Redis
    1. 1.1. Redis介绍
      1. 1.1.1. 什么是Redis
      2. 1.1.2. Redis官网
      3. 1.1.3. 什么是NoSQL
      4. 1.1.4. NoSQL数据库分类
        1. 1.1.4.1. 键值(Key-Value)存储数据库
        2. 1.1.4.2. 列存储数据库
        3. 1.1.4.3. 文档型数据库
        4. 1.1.4.4. 图形(Graph )数据库
      5. 1.1.5. Redis发展历史
      6. 1.1.6. Redis应用场景
    2. 1.2. Redis单机版安装配置
      1. 1.2.0.1. Redis下载
    3. 1.2.1. Redis安装环境
    4. 1.2.2. Redis安装
    5. 1.2.3. Redis启动
      1. 1.2.3.1. 前端启动
      2. 1.2.3.2. 后端启动(守护进程启动)
      3. 1.2.3.3. 后端启动的关闭方式
    6. 1.2.4. 其他命令说明
    7. 1.2.5. Redis客户端
      1. 1.2.5.1. Redis命令行客户端
        1. 1.2.5.1.1. 命令格式
        2. 1.2.5.1.2. 参数说明
        3. 1.2.5.1.3. 默认方式
  2. 1.3. Java客户端Jedis
    1. 1.3.1. Jedis介绍
    2. 1.3.2. 添加依赖
    3. 1.3.3. 单实例连接
    4. 1.3.4. 连接池连接
    5. 1.3.5. 连接redis集群
    6. 1.3.6. Jedis整合spring
  3. 1.4. Redis数据类型
    1. 1.4.1. string类型命令
      1. 1.4.1.1. 赋值
      2. 1.4.1.2. 取值
        1. 1.4.1.2.1. 取值并赋值
      3. 1.4.1.3. 数值增减
        1. 1.4.1.3.1. 递增数字
        2. 1.4.1.3.2. 增加指定的整数
        3. 1.4.1.3.3. 递减数值
        4. 1.4.1.3.4. 减少指定的整数
        5. 1.4.1.3.5. 仅当不存在时赋值
      4. 1.4.1.4. 其它命令
        1. 1.4.1.4.1. 向尾部追加值
        2. 1.4.1.4.2. 获取字符串长度
        3. 1.4.1.4.3. 应用场景之自增主键
    2. 1.4.2. hash类型命令
      1. 1.4.2.1. 赋值
        1. 1.4.2.1.1. 设置一个字段值
        2. 1.4.2.1.2. 设置多个字段值
        3. 1.4.2.1.3. 当字段不存在时赋值
      2. 1.4.2.2. 取值
        1. 1.4.2.2.1. 获取一个字段值
        2. 1.4.2.2.2. 获取多个字段值
        3. 1.4.2.2.3. 获取所有字段值
      3. 1.4.2.3. 删除字段
      4. 1.4.2.4. 增加数字
      5. 1.4.2.5. 其它命令
        1. 1.4.2.5.1. 判断字段是否存在
        2. 1.4.2.5.2. 只获取字段名或字段值
        3. 1.4.2.5.3. 获取字段数量
        4. 1.4.2.5.4. 获取所有字段
    3. 1.4.3. string类型和hash类型的区别
      1. 1.4.3.1. 应用之存储商品信息
    4. 1.4.4. list类型
      1. 1.4.4.1. ArrayList与LinkedList的区别
      2. 1.4.4.2. list类型介绍
        1. 1.4.4.2.1. LPUSH/RPUSH
        2. 1.4.4.2.2. LRANGE
        3. 1.4.4.2.3. LPOP/RPOP
        4. 1.4.4.2.4. LLEN
      3. 1.4.4.3. 其它命令
        1. 1.4.4.3.1. LREM
        2. 1.4.4.3.2. LINDEX
        3. 1.4.4.3.3. LTRIM
        4. 1.4.4.3.4. LINSERT
        5. 1.4.4.3.5. RPOPLPUSH
      4. 1.4.4.4. 应用之商品评论列表
      5. 1.4.4.5. set类型
        1. 1.4.4.5.1. set类型介绍
        2. 1.4.4.5.2. SADD/SREM
        3. 1.4.4.5.3. SMEMBERS
        4. 1.4.4.5.4. SISMEMBER
      6. 1.4.4.6. 集合运算命令
        1. 1.4.4.6.1. SDIFF
        2. 1.4.4.6.2. SINTER
        3. 1.4.4.6.3. SUNION
      7. 1.4.4.7. 其它命令
        1. 1.4.4.7.1. SCARD
        2. 1.4.4.7.2. SPOP
      8. 1.4.4.8. zset类型 (sortedset)
        1. 1.4.4.8.1. zset介绍
        2. 1.4.4.8.2. ZADD
        3. 1.4.4.8.3. ZRANGE/ZREVRANGE
        4. 1.4.4.8.4. ZSCORE
        5. 1.4.4.8.5. ZREM
      9. 1.4.4.9. 其它命令
        1. 1.4.4.9.1. ZRANGEBYSCORE
        2. 1.4.4.9.2. ZINCRBY
        3. 1.4.4.9.3. ZCARD
        4. 1.4.4.9.4. ZCOUNT
        5. 1.4.4.9.5. ZREMRANGEBYRANK
        6. 1.4.4.9.6. ZREMRANGEBYSCORE
        7. 1.4.4.9.7. ZRANK/ZREVRANK
      10. 1.4.4.10. 应用之商品销售排行榜
        1. 1.4.4.10.1. 写入商品销售量
      11. 1.4.4.11. 通用命令
        1. 1.4.4.11.1. keys
        2. 1.4.4.11.2. del
        3. 1.4.4.11.3. exists
        4. 1.4.4.11.4. expire(重点)
        5. 1.4.4.11.5. rename
        6. 1.4.4.11.6. type
  4. 1.5. Redis消息模式
    1. 1.5.1. 队列模式
|