01… redis缓存入门
简介
背景
我们现在的项目架构中,基本上是Web服务器(Tomcat)和数据库独立部署,独占服务器资源,随着用户数的增长,并发读写数据库,会加大数据库访问压力,导致性能的下降,严重时直接导致系统宕机,例如:
此时,我们可以在Tomcat同服务器上中增加本地缓存,并在外部增加分布式缓存,缓存热门数据。也就是通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。例如:
基于这样的一种架构设计,于是类似redis的一些分布式数据库就诞生了。
Redis 概述
Redis是一个key-value存储系统(官网:http://redis.io),是一个分布式缓存数据库。在DB-Engines.com的数据库排行中, Redis上升排行第七,如图所示:
版本及参考说明
Redis的次版本号(第一个小数点后的数字)为偶数的版本是稳定版本(2.4、2.6等),奇数为非稳定版本(2.5、2.7),一般推荐在生产环境使用稳定版本。最新版本6.2.2,新增了stream的处理方式,性能更高。Redis官方是不支持windows平台的,windows版本是由微软自己建立的分支,基于官方的Redis源码上进行编译、发布、维护的,所以windows平台的Redis版本要略低于官方版本。
Redis 相关参考网址如下所示:
Bootnb 相关:https://www.runoob.com/redis/redis-tutorial.html
Redis 官网:https://redis.io/
源码地址:https://github.com/redis/redis
Redis 在线测试:http://try.redis.io/
Redis 命令参考:http://doc.redisfans.com/
Redis初始操作
启动redis服务
Docker 环境下的启动(docker环境启动多个需要运行多个容器):
docker start redis01 #底层也是通过redis-server启动,start单词后的redis01为容器名
docker中查看redis服务
docker ps
查看启动的redis进程信息
ps -ef|grep redis
root 3511 1 0 16:29 ? 00:00:01 redis-server *:6379
root 3515 1 0 16:29 ? 00:00:01 redis-server 127.0.0.1:6380
进入redis容器
docker exec -it redis01 bash #redis01容器名
登录redis服务
登录本地redis
redis-cli
或者
redis-cli -p 6379
或者
redis-cli -p 6379 -a password #-a后面为password,此操作需要开启redis.conf文件中的 requirepass选项
登录远程redis
redis-cli -h ip -p6379 -a password
直接进入容器,登录redis服务的方式
docker exec -it redis01 redis-cli
查看redis信息
首先登录redis,然后输入info指令,例如
127.0.0.1:6379> info #查看当前redis节点的详细配置信息
清空redis屏幕
清除redis屏幕内容
127.0.0.1:6379> clear
退出redis服务
退出redis服务,例如
127.0.0.1:6379> exit
关闭redis服务
关闭redis服务,例如:
127.0.0.1:6379> shutdown
系统帮助
可以基于help指令查看相关指令帮助,例如:
127.0.0.1:6379> help
redis-cli 2.8.19
Type: "help @<group>" to get a list of commands in <group>
"help <command>" for help on <command>
"help <tab>" to get a list of possible help topics
"quit" to exit
127.0.0.1:6379> help type
TYPE key
summary: Determine the type stored at key
since: 1.0.0
group: generic
Redis数据存储操作
基于查看redis中key
127.0.0.1:6379> keys *
(empty list or set)
基于key/value形式存储数据
127.0.0.1:6379> set test1 666
ok
127.0.0.1:6379> set test2 888
ok
127.0.0.1:6379> keys *
1) "test1"
2) "test2"
基于key获取redis中存储的数据
127.0.0.1:6379> get test1
"666"
127.0.0.1:6379> get test2
"888"
127.0.0.1:6379> get test3
(nil)
127.0.0.1:6379>
清除redis中的数据
清除当前数据库数据
127.0.0.1:6379> flushdb
ok
清除所有数据库数据
127.0.0.1:6379> flushall
ok
Key有效时间设计
实际工作中我们经常要控制redis中key的有效时长,例如秒杀操作的计时,缓存数据的有效时长等。
Expire(设置生效时长-单位秒)
语法: EXPIRE key seconds
127.0.0.1:6379> set bomb tnt
OK
127.0.0.1:6379> expire bomb 10
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 5
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) 2
127.0.0.1:6379> ttl bomb
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379>
其中 , TTL查看key的剩余时间,
当返回值为-2时,标识键被删除。
当key不存在时,返回-2.
当key存在但没有设置剩余生存时间时,返回-1
Persist(取消时长设置)
通过persist让对特定的key设置的生效时长失效。
语法:PERSIST key
127.0.0.1:6379> set bomb tnt
ok
127.0.0.1:6379> expire bomb 60
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 49
127.0.0.1:6379> persist bomb
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) -1
127.0.0.1:6379>
其中,设置新的数据时需要重新设置该key的生存时间,重新设置值也会清除生存时间。
pexpire (单位毫秒)
pexpire 让key的生效时长以毫秒作为计量单位,这样可以做到更精确的时间控制。例如,可应用于场景。
语法:PEXPIRE key milliseconds
127.0.0.1:6379> set bomb tnt
ok
127.0.0.1:6379> pexpire bomb 10000
(integer) 1
127.0.0.1:6379> ttl bomb
(integer) 7
127.0.0.1:6379> ttl bomb
(integer) 3
127.0.0.1:6379> ttl bomb
(integer) -2
127.0.0.1:6379>
02… Redis常用数据类型
简介
概述
Redis作为一种key/value结构的数据存储系统,为了便于对数据进行管理,提供了多种数据类型。然后,基于指定类型存储我们项目中产生的数据,例如用户的登陆信息,购物车信息,商品详情信息等等。
常用数据类型
Redis中基础数据结构包含字符串,散列,集合,有序集合。工作中具体使用哪种类型要结合具体场景。
String类型操作实践
字符串类型是redis中最简单的数据类型,它存储的值可以是字符串,其最大字符串长度支持到512M。基于此类型,可以实现博客字数统计,将日志不断追加到指定key,实现一个分布式自增id,实现一个博客的点赞操作等。
incr/incrby
当存储的字符串是整数时,redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。
语法:INCR key
127.0.0.1:6379> set num 1
(integer) 1
127.0.0.1:6379> incr num
(integer 2
127.0.0.1:6379> keys *
"num"
127.0.0.1:6379> incr num
127.0.0.1:6379>
说明,如果num不存在,则自动会创建,如果存在自动+1.
指定增长系数
语法:INCRBY key increment
127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 3
(integer) 8
127.0.0.1:6379> incrby num 2
(integer) 10
127.0.0.1:6379>
decr/decrby
减少指定的整数
DECR key 按照默认步长(默认为1)进行递减
DECRBY key decrement 按照指定步长进行递减
127.0.0.1:6379> incr num
(integer) 11
127.0.0.1:6379> decr num
(integer) 10
127.0.0.1:6379> decrby num 3
(integer) 7
append
向尾部追加值,如果键不存在则创建该键,其值为写的value,即相当于SET key value。 返回值是追加后字符串的总长度。
语法: AOOEND key value
127.0.0.1:6379> keys *
"num"
"test1"
"test2"
"test3"
127.0.0.1:6379> get test
"666"
127.0.0.1:6379> append test "666"
(integer) 6
127.0.0.1:6379> get test
"666666"
127.0.0.1:6379>
strlen
字符串长度,返回数据的长度,如果键不存在则返回0。注意,如果键值为空串,也返回0。
语法:STRLEN key
127.0.0.1:6379> get test
"666666"
127.0.0.1:6379> strlen test
(integer) 6
127.0.0.1:6379> strlen tnt
(integer) 0
127.0.0.1:6379> set tnt ""
ok
127.0.0.1:6379> strlen tnt
(integer) 0
127.0.0.1:6379> exists tnt
(integer) 1
127.0.0.1:6379>
mset/mget
同时设置/获取多个键值
语法:MSET key value key value …
MGET key key …
127.0.0.1:6379> flushall
ok
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> mset a 1 b 2 c 3
ok
127.0.0.1:6379> mget a b c
1) "1"
2) "2"
3) "3"
127.0.0.1:6379>
小节面试分析
- 博客的字数统计如何实现?(strlen)
- 如何将审计日志不断追加到指定 key? (appen)
- 你如何实现一个分布式自增id?(incr -雪花算法)
- 如何实现一个一个博客的点赞操作?(incr ,decr)
Hash类型应用实践
Redis散列类型相当于Java中的HashMap,实现原理跟HashMap一致,一般用于存储对象信息,存储了字段(field)和字段值的映射,一个散列类型可以包含最多232-1个字段。
hse/hget
语法结构
HSET key field value
HGET key field
HMSET key field value file value
HMGET key field field
HGETALL key
HSET和HGET赋值和取值
127.0.0.1:6379> hset user username lihua
(integer)1
127.0.0.1:6379> hget user username
"lihua"
127.0.0.1:6379> hset user username lili
(integer)0
127.0.0.1:6379> keys user
1)"user"
127.0.0.1:6379> hgetall user
1)"username"
2)"lili"
127.0.0.1:6379>
127.0.0.1:6379> hset user age 18
(integer) 1
127.0.0.1:6379> hset user address "xi'an"
(integer) 1
127.0.0.1:6379> hgetall user
1)"username"
2)"chen"
3)"age"
4)"18"
5)"address"
6)"xi'an"
127.0.0.1:6379>
HSET命令不区分插入和更新操作,当执行插入操作时,HSET命令返回1 ,当执行更新操作时返回0。
hincrby
127.0.0.1:6379> hdecrby article total 1 #执行会出错
127.0.0.1:6379> hincrby article total -1 #没有hdecrby自减命令
127.0.0.1:6379> hget article total
hmset/hget
HMSET和HMGET设置和获取对象属性
127.0.0.1:6379> hmset person username tony age 18
ok
127.0.0.1:6379> hmget person age username
1)"18"
2)"tony"
127.0.0.1:6379> hgetall person
1)"username"
2)"tony"
3)"age"
4)"18"
127.0.0.1:6379>
注意:上面HMGET字段顺序可以自定义
hexists
查看属性是否存在
127.0.0.1:6379> hexists killer
(error) ERR wrong number of arguments for 'hexists' command
127.0.0.1:6379> hexists killer a
(integer)0
127.0.0.1:6379> hexists user username
(integer)1
127.0.0.1:6379> hexists user age
(integer)1
127.0.0.1:6379>
hdel
删除属性
127.0.0.1:6379> hdel user age
(integer)1
127.0.0.1:6379> hgetall user
1)"lihua"
2)"lili"
127.0.0.1:6379> hgetall person
1)"username"
2)"tony"
3)"age"
4)"18"
127.0.0.1:6379>
hkeys/hvals
只获取字段名HKEYS或字段值HVALS
127.0.0.1:6379> hkeys person
1)"username"
2)"age"
127.0.0.1:6379> hvals person
1)"tony"
2)"18"
hlen
查看元素个数
127.0.0.1:6379> hlen user
(integer)1
127.0.0.1:6379> hlen person
(integer)2
127.0.0.1:6379>
小节面试分析
发布一篇博客需要写内存吗?(需要,hmset)
浏览博客内容会怎么做?(hmget)
如何判定一篇博客是否存在?(hexists)
删除一篇博客如何实现?(hdel)
分布式系统中你登录成功以后是如何存储用户信息的?(hmset)
List类型应用实践
lpush
在key对应list的头部添加字符串元素
redis 127.0.0.1:6379> lpush mylist "world"
(integer) 1
redis 127.0.0.1:6379> lpush mylist "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist 0 -1
1)"hello"
2)"world"
redis 127.0.0.1:6379>
其中,Redis Lrange 返回列表中指定区间内的元素,区间以偏移量 START 和END 指定。其中0表示的是第一个元素,1表示列表的第二个元素,以此类推。你也可以使用负数下标,以**-1表示列表的最后一个元素,-2**表示列表的倒数第二个元素
rpush
在key对应的list的尾部添加字符串元素
redis 127.0.0.1:6379> rpush wolist "NiHAO"
(integer) 1
redis 127.0.0.1:6379> rpush wolist "SHiJIe"
(integer) 2
redis 127.0.0.1:6379> lrange wolist 0 -1
1)"NiHao"
2)"SHiJIe"
redis 127.0.0.1:6379>
del
清空集合元素,例如
redis 127.0.0.1:6379> del mylist
linsert
在key对应list的特定位置之前或之后添加字符串元素
redis 127.0.0.1:6379> rpush wolist2 "HI"
(integer) 1
redis 127.0.0.1:6379> rpush wolist2 "HAO"
(integer) 2
redis 127.0.0.1:6379> linsert wolist2 before "HAO" "NI"
(integer) 3
redis 127.0.0.1:6379> lrange wolist3 0 -1
1) "HI"
2) "NI"
3) "HAO"
redis 127.0.0.1:6379>
lset
设置list中指定下标的元素值(一般用于修改操作)
redis 127.0.0.1:6379> rpush wolist3 "YI"
(integer) 1
redis 127.0.0.1:6379> rpush wolist3 "ER"
(integer) 2
redis 127.0.0.1:6379> rpush wolist3 "SAN"
(integer) 3
redis 127.0.0.1:6379> rpush wolist3 "SI"
(integer) 4
redis 127.0.0.1:6379> lset wolist3 0 "LING"
OK
redis 127.0.0.1:6379> lset wolist3 -1 "WU"
OK
redis 127.0.0.1:6379> lrange wolist3 0 -1
1) "LING"
2) "ER"
3) "SAN"
4) "WU"
redis 127.0.0.1:6379>
lrem
从key对应list中删除count 个 和 value相同的元素, count >0 时,按从头到尾的顺序删除
redis 127.0.0.1:6379> rpush wolist4 "NI"
(integer) 1
redis 127.0.0.1:6379> rpush wolist4 "HAO"
(integer) 2
redis 127.0.0.1:6379> rpush wolist4 "HAO"
(integer) 3
redis 127.0.0.1:6379> rpush wolist4 "HAO"
(integer) 4
redis 127.0.0.1:6379> rpush wolist4 "MA"
(integer) 5
redis 127.0.0.1:6379> lrem wolist4 2 "HAO"
(integer) 2
redis 127.0.0.1:6379> lrange wolist4 0 -1
1) "NI"
2) "HAO"
3) "MA"
redis 127.0.0.1:6379> rpush wolist4 "MA"
count <0时,按从尾到头的顺序删除
redis 127.0.0.1:6379> rpush wolist5 "NI"
(integer) 1
redis 127.0.0.1:6379> rpush wolist5 "NI"
(integer) 2
redis 127.0.0.1:6379> rpush wolist5 "HAO"
(integer) 3
redis 127.0.0.1:6379> rpush wolist5 "MA"
(integer) 4
redis 127.0.0.1:6379> rpush wolist5 "NI"
(integer) 5
redis 127.0.0.1:6379> rpush wolist5 "NI"
(integer) 6
redis 127.0.0.1:6379> lrem wolist5 -2 "NI"
(integer) 2
redis 127.0.0.1:6379> lrange wolist 0 -1
1) "NI"
2) "NI"
3) "HAO"
4) "MA"
count =0时,删除全部
redis 127.0.0.1:6379> lrem wolist5 0 "NI"
(integer) 2
redis 127.0.0.1:6379> lrange wolist 0 -1
1) "HAO"
2) "MA"
redis 127.0.0.1:6379>
ltrim
保留指定key的值范围内的数据
redis 127.0.0.1:6379> rpush mylist8 "one"
(integer) 1
redis 127.0.0.1:6379> rpush mylist8 "two"
(integer) 2
redis 127.0.0.1:6379> rpush mylist8 "three"
(integer) 3
redis 127.0.0.1:6379> rpush mylist8 "four"
(integer) 4
redis 127.0.0.1:6379> rpush mylist8 1 -1
ok
redis 127.0.0.1:6379> lrange mylist8 0 -1
1) "two"
2) "three"
3) "four"
redis 127.0.0.1:6379>
lpop
从list的头部删除元素,返回删除元素
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
redis 127.0.0.1:6379> lpop mylist
"hello"
redis 127.0.0.1:6379> lrange mylist 0 -1
1) "world"
redis 127.0.0.1:6379>
rpop
从list的尾部删除元素,并返回删除元素:
redis 127.0.0.1:6379> lrange mylist2 0 -1
1) "hello"
2) "world"
redis 127.0.0.1:6379> rpop mylist2
"world"
redis 127.0.0.1:6379> lrange mylist2 0 -1
1) "hello"
redis 127.0.0.1:6379>
llen
返回key对应list的长度:
redis 127.0.0.1:6379> llen mylist5
(integer) 2
redis 127.0.0.1:6379>
lindex
返回名称为key的list中index位置的元素:
redis 127.0.0.1:6379> lrange mylist5 0 -1
1) "three"
2) "foo"
redis 127.0.0.1:6379> lindex mylist5 0
"three"
redis 127.0.0.1:6379> lindex mylist5 1
"foo"
redis 127.0.0.1:6379>
rpoplpush
从第一个list的尾部一出元素并添加到第二个list的头部,最后返回被移除的元素值,整个操作是原子的。
如果第一个list是空或者不存在返回nil:
rpoplpush lst1 lst1
rpoppush lst1 lst2
小节面试分析
- 如何基于redis实现一个队列结构? (lpush/rpop)
- 如何基于redis实现一个栈结构?(lpush/lpop)
- 如何基于redis实现一个阻塞式队列?(lpush/brpop)
- 如何实现秒杀活动的公平性?(先进先出-FIFO)
- 通过list结构实现一个消息队列(顺序)吗?(可以,FIFO->lpush,rpop)
- 用户注册时的右键发送功能如何提高其效率?(邮件发送是要调用第三方服务,底层通过队列优化其效率,队列一般是list结构)
- 如何动态更新商品的销量列表?(卖的好的排名靠前一些,linsert)
- 商家的粉丝列表使用说明结构实现呢?(list结构)
Set类型应用实践
Redis的Set类似JAVA中的HashSet,是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,Redis中Set集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1).
sadd
添加元素,重复元素添加失败,返回0
127.0.0.1:6379> sadd name tony
(integer) 1
127.0.0.1:6379> sadd name jone
(integer) 1
127.0.0.1:6379> sadd name rose
(integer) 1
127.0.0.1:6379> sadd name rose
(integer) 0
smembers
获取集合中成员,例如:
127.0.0.1:6379> smembers name
1) "tony"
2) "jone"
3) "rose"
spop
移除并返回集合中的一个随机元素
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"
3) "rabbitmq"
4) "nginx"
127.0.0.1:6379> spop internet
"rabbitmq"
127.0.0.1:6379> spop internet
"nginx"
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"
scard
获取集合中的成员个数
127.0.0.1:6379> scard name
(integer) 3
smove
移动一个元素到另外一个集合
127.0.0.1:6379> sadd internet amoeba nginx redis
(integer) 3
127.0.0.1:6379> sdd name ap ad adc
(integer) 3
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "redis"
3) "nginx"
127.0.0.1:6379> smembers bigdata
1) "hadopp"
2) "spark"
3) "rabbitmq"
127.0.0.1:6379> smove name internet adc
(integer) 1
127.0.0.1:6379> smembers internet
1) "amoeba"
2) "nginx"
3) "adc"
4) "redis"
127.0.0.1:6379> smembers name
1) "ad"
2) "ap"
127.0.0.1:6379>
sunion
实现集合的并集操作
127.0.0.1:6379> sunion internet name
1) "amoeba"
2) "nginx"
3) "adc"
4) "redis"
5) "ap"
6) "ad"
小节面试分析
- 朋友圈的点赞功能你如何实现?(sadd,srem,smembers,scard)
- 如何实现一个网站投票统计程序?
- 你知道微博中的关注如何实现吗?