redis学习教程
redis的基本介绍
redis可以直接从官网下载
外文官网https://redis.io/
中文官网http://www.redis.cn/
什么是redis
Redis:
RE
moteDI
ctionaryS
erver(远程字典服务器)圓
是完全开源免费的,用C语言编写的,遵守BSD协议,
是一个高性能的(key/value)分布式内存数据库,基于内存运行
并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,
也被人们称为数据结构服务器
redis的特点
reids主要有一下三大特点:
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
- Redis支持数据的备份,即master-slave模式的数据备份
redis的作用
- 内存存储和持久化: redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务
- 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合里面
- 模拟类似于HttpSession这种需要设定过期时间的功能发布、 订阅消息系统
- 定时器、计数器
为什么要使用redis/缓存
主要从“高性能”和“高并发”这两点来看待这个问题。
- 高性能:
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可! - 高并发:
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
五种数据结构
Redis除了存储键之外还可以存储常见的5种数据类型,分别是:String、List、Set、Zset、Hash。
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
String字符串 | 可以是字符串、整数或浮点数 | 对整个字符串或字符串的一部分进行操作对整数或浮点数进行自增或自减操作 |
List列表 | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行push和pop操作,读取单个或多个元素;根据值查找或删除元素 |
Set集合 | 包含字符串的无序集合集合中每个字符串都是独一无二的 | 添加、获取、删除元素。检查一个元素是否存在于集合中计算交集、并集、差集 |
Hash散列 | 包含键值对的无序散列表 | 添加、获取、删除单个元素、获取所有键值对元素 |
Zset有序集合 | 和散列一样,用于存储键值对字符串成员与浮点数分数之间的有序映射元素的排列顺序由分数的大小决定 | 添加、获取、删除单个元素、根据分值范围或成员来获取元素 |
Redis Desktop Manager(可视化数据操作redis)
Redis Desktop Manager是一个redis操作的可视化功能,类似于Navicat操作数据库形式,官网上也有下载连接,不过是收费的,我们可以在github上下载,下面是地址:
https://github.com/uglide/RedisDesktopManager/releases
下载完连接上redis后我们可以看到如下界面
db0到db15指我们拥有16个数据空间。
命令操作
本文中仅列举几个常见的例子作为样例,如果对其他命令感兴趣的可以直接在官网中查看和测试。
当我们右键点击
会发现有Console,这个也就是我们通过可视化功能进行redis数据库操作的地方,类似于Navicat中写sql语句的地方。接下来我们写几个命令测试,我们直接使用官网上的几个命令
String相关
APPEND value(追加一个值到key上)
如果 key 已经存在,并且值为字符串,那么这个命令会把 value 追加到原来值(value)的结尾。 如果 key 不存在,那么它将首先创建一个空字符串的key,再执行追加操作,这种情况 APPEND 将类似于 SET 操作。
时间复杂度:O(1)。均摊时间复杂度是O(1), 因为redis用的动态字符串的库在每次分配空间的时候会增加一倍的可用空闲空间,所以在添加的value较小而且已经存在的 value是任意大小的情况下,均摊时间复杂度是O(1) 。
返回值:
Integer reply:返回append后字符串值(value)的长度。
redis> EXISTS mykey
(integer) 0
redis> APPEND mykey "Hello"
(integer) 5
redis> APPEND mykey " World"
(integer) 11
redis> GET mykey
"Hello World"
GET key 返回key的value值
返回key的value。如果key不存在,返回特殊值nil。如果key的value不是string,就返回错误,因为GET只处理string类型的values。
返回值
simple-string-reply:key对应的value,或者nil(key不存在时)
redis:0>get mykey
"Hello World"
redis:0>get mylist
null
redis:0>get myset
"WRONGTYPE Operation against a key holding the wrong kind of value"
注意上述代码在使用的时候,mykey是string类型的,myset是set类型的,mylist不存在
STRLEN key 获得指定key的value长度
返回key的string类型value的长度。如果key对应的非string类型,就返回错误。
返回值
integer-reply:key对应的字符串value的长度,或者0(key不存在)
redis:0>STRLEN mykey
"11"
redis:0>STRLEN mylist
"0"
redis:0>STRLEN myset
"WRONGTYPE Operation against a key holding the wrong kind of value"
结果原因如上相同
应用场景
String是常用的一种数据类型,普通的key/value存储都可以归为此类,value不仅是String,还可以是数字。比如想知道什么时候封锁一个IP地址(访问超过几次),INCRBY命令让这些变得很容易,通过原子递增保持计数。
LIST相关
LPUSH key value (从队列的左边入队一个或者多个元素)
将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在,那么在进行 push 操作前会创建一个空列表。 如果 key 对应的值不是一个 list 的话,那么会返回一个错误。
可以使用一个命令把多个元素 push 进入列表,只需在命令末尾加上多个指定的参数。元素是从最左端的到最右端的、一个接一个被插入到 list 的头部。 所以对于这个命令例子 LPUSH mylist a b c,返回的列表是 c 为第一个元素, b 为第二个元素, a 为第三个元素。
返回值
integer-reply: 在 push 操作后的 list 长度。
redis> LPUSH mylist "world"
(integer) 1
redis> LPUSH mylist "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "hello"
2) "world"
redis>
运行截图:
存储截图:
下面是LPUSH放多个值
redis:0>LPUSH mylist “it” “is” “ok"
(integer) 5
redis:0>LRANGE mylist 0 -3
1) "“ok”"
2) "“is”"
3) "“it”"
redis:0>
LRANGE 从列表中指定返回元素
返回存储在 key 的列表里指定范围内的元素。 start 和 end 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。
偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如, -1 表示列表的最后一个元素,-2 是倒数第二个,以此类推。
需要注意的是,如果你有一个list,里面的元素是从0到100,那么 LRANGE list 0 10 这个命令会返回11个元素,即最右边的那个元素也会被包含在内。 在你所使用的编程语言里,这一点可能是也可能不是跟那些求范围有关的函数都是一致的。
超过范围的下标
当下标超过list范围的时候不会产生error。 如果start比list的尾部下标大的时候,会返回一个空列表。 如果stop比list的实际尾部大的时候,Redis会当它是最后一个元素的下标。
还是使用之前的mylist做测试
redis:0>LRANGE mylist 1 3
1) "“is”"
2) "“it”"
3) "hello"
redis:0>LRANGE mylist 1 -2
1) "“is”"
2) "“it”"
3) "hello"
redis:0>
LPOP 从左边弹出一个元素
移除并且返回 key 对应的 list 的第一个元素。
返回值
bulk-string-reply: 返回第一个元素的值,或者当 key 不存在时返回 nil。
redis:0>LPOP mylist
"“ok”"
redis:0>LPOP mylist
"“is”"
redis:0>LPOP mylist
"“it”"
redis:0>LPOP mylist
"hello"
redis:0>LPOP mylist
"world"
redis:0>LPOP mylist
null
当list中的元素全部被移除,那么list的key也直接不存在了
LPUSH、LPOP、LRANGE对list的操作都有一个对应的RPUSH、RPOP的操作,使用方法相同,不过是一个从左一个从右。
应用场景
微博TimeLine
消息队列
SET相关
SADD key member 添加一个或多个元素到set集合中
添加一个或多个指定的member元素到集合的 key中.指定的一个或者多个元素member 如果已经在集合key中存在则忽略.如果集合key 不存在,则新建集合key,并添加member元素到集合key中.
如果key 的类型不是集合则返回错误.
返回值
integer-reply:返回新成功添加到集合里元素的数量,不包括已经存在于集合中的元素.
redis:0> SADD myset "Hello"
(integer) 1
redis:0> SADD myset "World"
(integer) 1
redis:0>SADD myset "hello" "world"
"2"
redis:0>SADD myset "hello" "world"
"0"
redis:0>SMEMBERS myset
1) "world"
2) "hello"
3) "Hello"
4) "World"
redis:0>
SISMEMBER key member 确定一个给定的值是不是在set中
返回成员 member 是否是存储的集合 key的成员.
返回值
integer-reply,详细说明:
- 如果member元素是集合key的成员,则返回1
- 如果member元素不是key的成员,或者集合key不存在,则返回0
redis:0>SISMEMBER myset "hello"
"1"
redis:0>SISMEMBER myset "jingchu"
"0"
redis:0>SISMEMBER myset "Hello"
"1"
应用场景
- 共同好友、二度好友(并集、交集、差集运算)
- 利用唯一性,可以统计访问网站的所有独立IP
- 好友推荐的时候,可以根据tag求交集,交集大于threshold就可以推荐
Hash相关命令
HSET key field value 设置hash中field的值
设置 key 指定的哈希集中指定字段的值。
如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key 关联。
如果字段在哈希集中存在,它将被重写。
返回值
integer-reply:含义如下
- 1如果field是一个新的字段
- 0如果field原来在map里面已经存在
redis:0>HSET myhash field1 "Hello"
"1"
redis:0>HSET myhash field2 "World"
"1"
redis:0>HSET myhash field2 "World !!"
"0"
HGET key field 获取hash中field的值
返回 key 指定的哈希集中该字段所关联的值
返回值
bulk-string-reply:该字段所关联的值。当字段不存在或者 key 不存在时返回nil。
redis:0>HGET myhash filed1
null
redis:0>HGET myhash field1
"Hello"
HGETALL key 从hash中读取全部的域和值
返回 key 指定的哈希集中所有的字段和值。返回值中,每个字段名的下一个是它的值,所以返回值的长度是哈希集大小的两倍
返回值
array-reply:哈希集中字段和值的列表。当 key 指定的哈希集不存在时返回空列表。
redis:0>HGETALL myhash
1) "field1"
2) "Hello"
3) "field2"
4) "World !!"
HDEL key field 删除一个或多个Hash的field
从 key 指定的哈希集中移除指定的域。在哈希集中不存在的域将被忽略。
如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0。
返回值
integer-reply: 返回从哈希集中成功移除的域的数量,不包括指出但不存在的那些域
redis:0>HDEL myhash field1
"1"
redis:0>HDEL myhash field1
"0"
redis:0>HGETALL myhash
1) "field2"
2) "World !!"
zset相关命令
ZADD key 添加到有序set的一个或多个成员,或更新的分数,如果它已经存在
将所有指定成员添加到键为key有序集合(sorted set)里面。 添加时可以指定多个分数/成员(score/member)对。 如果指定添加的成员已经是有序集合里面的成员,则会更新改成员的分数(scrore)并更新到正确的排序位置。
如果key不存在,将会创建一个新的有序集合(sorted set)并将分数/成员(score/member)对添加到有序集合,就像原来存在一个空的有序集合一样。如果key存在,但是类型不是有序集合,将会返回一个错误应答。
分数值是一个双精度的浮点型数字字符串。+inf和-inf都是有效值。
返回值
Integer reply, 包括:
- 添加到有序集合的成员数量,不包括已经存在更新分数的成员。
如果指定INCR参数, 返回将会变成bulk-string-reply : - 成员的新分数(双精度的浮点型数字)字符串。
redis:0>ZADD myzset 1 "one"
"1"
redis:0>ZADD myzset 1 "one1"
"0"
redis:0>ZADD myzset 2 "two" 3 "three"
"2"
redis:0>ZRANGE myzset 0 -1 WITHSCORES
1) "one"
2) "1"
3) "one1"
4) "1"
5) "two"
6) "2"
7) "three"
8) "3"
redis:0>
ZRANGE key start stop 根据指定的index返回,返回sorted set的成员列表
返回存储在有序集合key中的指定范围的元素。 返回的元素可以认为是按得分从最低到最高排列。 如果得分相同,将按字典排序。
返回值
array-reply:给定范围内的元素列表(如果指定了WITHSCORES选项,将同时返回它们的得分)。
redis:0>ZRANGE myzset 0 -1
1) "one"
2) "one1"
3) "two"
4) "three"
redis:0>ZRANGE myzset 0 2
1) "one"
2) "one1"
3) "two"
ZREM ZREM key member 从排序的集合中删除一个或多个成员
当key存在,但是其不是有序集合类型,就返回一个错误。
返回值
integer-reply, 如下的整数:
- 返回的是从有序集合中删除的成员个数,不包括不存在的成员。
redis:0>ZREM myzset "two"
"1"
redis:0>ZRANGE myzset 0 -1
1) "one"
2) "one1"
3) "three"
redis:0>ZREM myzset "one" "three"
"2"
redis:0>ZRANGE myzset 0 -1
1) "one1"
redis:0>
java整合redisTemplate
笔者使用的使用的springboot项目做样例。使用redisTemplate分为以下几个步骤:
- 下载redis,这个可以直接通过官网下载,很容易就可以成功,笔者就不再多介绍了
- 在POM文件中引入redis的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 修改application.yml或者application.properties文件
#application.yml
spring:
redis:
host: 127.0.0.1 //主机地址,我的是在本地
#application.properties
spring-redis-host=127.0.0.1
- 在controller使用StringRedisTemplate或RedisTemplate操作redis
@Autowried
StringRedisTemplate stringRedistempalte;
@Autowried
Redistemplate redistemplate;
//StringRedisTemplate 的方法
stringRedisTemplate.opsForValue([string (字符串) ]
stringRedisTemplate.opsForlist()[list (列表) ]
stringRedisTemplate.opsForSet([Set (集合) ]
stringRedisTemplate.opsForHash()[Hash (散列) ]
stringRedisTemplate.opsForZSet()[ZSet (有序集合) ]
stringRedisTemplate.opsForValue([string (字符串) ]
//redistemplate方法
redistemplate.opsForlist()[list (列表) ]
redistemplate.opsForSet([Set (集合) ]
redistemplate.opsForHash()[Hash (散列) ]
redistemplate.opsForZSet()[ZSet (有序集合) ]
上述方法对应着各个类型的命令,凡是官方文档里拥有的命令应该都可以找到。大家可以自己尝试,在这里就一一不给出具体例子了。样例请看如下代码
//redis保存数据
stringRedisTemplate.opsForValue().append("msg","hello");
//读取数据
String msg = stringRedisTemplate.opsForValue().get("msg");
System.out.println(msg);
//list存储数据
stringRedisTemplate.opsForList().leftPush("mylist","1");
stringRedisTemplate.opsForList().leftPush("mylist","2");
stringRedisTemplate.opsForList().leftPush("mylist","3");
String mylist = stringRedisTemplate.opsForList().leftPop("mylist"); //删除并查询最顶层的list数据
System.out.println(mylist);