一. 概述

  • Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。

  • Redis 与其他 key - value 缓存产品有以下三个特点:

    • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
    • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
    • Redis支持数据的备份,即master-slave模式的数据备份。

Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis 支持哪些数据结构

STRING:字符串、整数或浮点数
LIST:列表,可存储多个相同的字符串
SET:集合,存储不同元素,无序排列
HASH:散列表,存储键值对之间的映射,无序排列
ZSET:有序集合,存储键值对,有序排列

redis 最适合的场景

Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?

  • 如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:
    • Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
    • Redis支持数据的备份,即master-slave模式的数据备份。
    • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

1. 会话缓存(Session Cache)

最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

2. 全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。
再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

3. 队列

Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。

4. 排行榜/计数器

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的。

验证码

防止流量攻击

二、 Redis命令

Redis命令用于在redis服务上执行操作。要在redis服务上执行命令需要一个redis客户Redis客户端

常用命令

keys:返回满足的所有键,可以模糊匹配比如 keys abc* 代表abc开头的key
exists key:是否存在指定的key,存在返回1,不存在返回0
expire key second:设置某个key的过期时间时间为秒
del key:删除某个key
ttl key:查看剩余时间,当key不存在时,返回-2;存在但没有设置剩余生存时间时,返回-1,否则,以秒为单位,返回key的剩余生存时间。
pttl key:以毫秒为单位查看剩余时间
persist key:取消过去时间
PEXPIRE key milliseconds修改key的过期时间为毫秒
select: 选择数据库数据库为0-15 默认一共16个数据库,设计成多个数据库实际上是为了数据库安全和备份
move key dbindex:将当前数据中的key转移到其他数据库
randomkey:随机返回一个key
rename key key2:重命名key
echo: 打印命令
dbsize:查看数据库的key数量
info:查看数据库信息
config get:实时传储收到的请求,返回相关的配置
flushdb:清空当前数据库
flushall:清空所有数据库

三、 5中基本数据类型

1. string命令

127.0.0.1:6379[1]> set name sky       # 设置
OK
127.0.0.1:6379[1]> APPEND name hello  # 在尾部添加
(integer) 8
127.0.0.1:6379[1]> get name           # 获取
"skyhello"
127.0.0.1:6379[1]> STRLEN name        # 获取字符串长度
(integer) 8

127.0.0.1:6379> set view 0      # 设置访问量
OK
127.0.0.1:6379> incr view       # 增加 view++
(integer) 1
127.0.0.1:6379> decr view       # 减少 view--
(integer) 0
127.0.0.1:6379> incrby view 10  # 按步长增加 view=view+10
(integer) 10
127.0.0.1:6379> decrby view 10  # 按步长减少 view=view-10
(integer) 0

127.0.0.1:6379> set name sky,hilohijsk.  # 设置name的值
OK
127.0.0.1:6379> getrange name 0 4        # 截取字符串[0,4]
"sky,h"
127.0.0.1:6379> getrange name 0 -1       # 获取全部的字符串 和 get key是一样的 
"sky,hilohijsk."
127.0.0.1:6379> setrange name 2 empe     # 替换指定位置开始的字符串
(integer) 14
127.0.0.1:6379> get name
"skempelohijsk."

# setex (set with expire)    # 设置过期时间 
# setnx (set if not exist)   # 不存在在设置 (在分布式锁中会常常使用!) setex key3 30 hello       # 设置key3 的值为 hello,30秒后过期 
setnx mykey redis   # 如果mykey不存在,创建mykey 
(integer) 1

mset mget
# 同时设置和获取多个值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3  # 同时设置多个值
OK
127.0.0.1:6379> mget k1 k2 k3           # 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 a1 k4 v4  # msetnx是一个原子性的操作,要么一起功,要么一起失败!
(integer) 0
127.0.0.1:6379> get k4
(nil)

# 设置对象
127.0.0.1:6379> set user:1 {name:sky,age:14}  # 设置一个user:1 对象,值为json字符来保存一个对象!

# 这里的key是一个巧妙的设计: user:{id}:{filed}
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2 
OK
127.0.0.1:6379> mget user:1:name user:1:age 
1) "zhangsan"
2) "2"

# 先get然后再set
127.0.0.1:6379> getset name sky  # 若不存在该值,则返回nil
(nil)
127.0.0.1:6379> getset name aa   # 如果存在值,获取原来的值,并设置新的值 
"sky"
127.0.0.1:6379> get name 
"aa"

2. List命令

在redis里面,我们可以把list玩成 ,栈、队列、阻塞队列!所有的list命令都是用l开头的

lpush list first    # 将一个值或者多个值,插入到列表头部 (左) 
rpush list second   # 将一个值或者多个值,插入到列表尾部 (右) 
lrange list 0 -1    # 获取list中值
lrange list 0 1     # 通过区间获取具体的值
lpop list           # 移除list的第一个元素 
rpop list           # 移除list的最后一个元素 
lindex list 1       # 通过下标获得 list 中的某一个值
Llen list           # 返回列表的长度
lrem list 2 one     # 移除list集合中指定个数(2个)的value,精确匹配 
ltrim mylist 1 2    # 通过下标截取指定的长度,这个list已经被改变了,只剩下截取的元素! 
rpoplpush list otherlist # 移除列表的后一个元素,将他移动到新的列表中!
exists list         # 判断这个列表是否存在 
lset list 0 item    # 如果不存在列表,更新会报错 
lset list 6 other   # 如果不存在当前下标(6),则会报错!
LINSERT list before "world" "other" # 将某个具体的value插入到列把你中某个元素的前面或者后面 
LINSERT mylist after "world" "other" 

3. Set命令

sadd myset "hello"	# set集合中添加元素
smembers myset		# 查看指定set的所有值 
sismember myset hello	# 判断某一个值是不是在set集合中! 
scard myset		# 获取set集合中的内容元素个数
srem myset hello	# 移除set集合中的指定元素 
spop myset  		# 随机删除一些set集合中的元素! 
smove myset set2 "hi" 	# 将一个指定的值,移动到另外一个set集合!
sinter set1 set2 	# 交集
sunion set1 set2	# 并集
adiff set1 set2    	# 差集

4. Hash命令

hash变更的数据 user name age,尤其是是用户信息之类的,经常变动的信息! hash 更适合于对象的 存储,String更加适合字符串存储!

hset myhash field1 hello	# set一个具体key-vlaue
hmset myhash field1 hello field2 world	# set多个 key-vlaue  
hget myhash field1  		# 获取一个字段值
hgetall myhash   		# 获取全部的数据
hdel myhash field1		# 删除hash指定key字段,对应的value值也就消失了 
hlen myhash  			# 获取hash表的字段数量 
hexists myhash field1		# 判断hash中指定字段是否存在! 
hkeys myhash		# 获得所有field 
hvals myhash  		# 只获得所有value 
hincrby myhash field3 3	# 指定增量3
hsetnx myhash field4 hello	# 如果不存在则可以设置,存在则不能 

5. Zset(有序集合)

在set的基础上,增加了一个值,set k1 v1 ---> zset k1 score1 v1

zadd myset 1 one		# 添加一个元素,1是score
zadd myset 2 two 3 three	# 添加多个值
zrange myset 0 -1		# 查看所有元素(从小到大排序)
zrevrange myset 0 -1  		# 查看所有元素(从大到小排序)
ZRANGEBYSCORE salary -inf +inf withscores # 显示全部的用户并且附带成绩
zrem salary hello   		# 移除有序集合中的指定元素 
zcard salary			# 获取有序集合中的个数 
zcount myset 1 3  		# 获取指定区间的成员数量 

四、 三种特殊数据类型

Geospatial 地理位置

Hyperloglog

优点:占用的内存是固定,2^64 不同的元素的技术,只需要废 12KB内存!如果要从内存角度来比较的话Hyperloglog 首选!
网页的 UV (一个人访问一个网站多次,但是还是算作一个人!)
传统的方式, set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断 ! 这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id;
0.81% 错误率! 统计UV任务,可以忽略不计的!

pfadd mykey a b c d e f g h i j   # 创建第一组元素 mykey 
pfcount mykey  # 统计mykey元素的基数数量
pfadd mykey2 i j z x c v b n m   # 创建第二组元素 mykey2  
pfmerge mykey3 mykey mykey2  # 合并两组 mykey mykey2 => mykey3 并集 

Bitmap

位存储
统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 两个状态的,都可以使用 Bitmaps
Bitmap 位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!
365 天 = 365 bit 1字节 = 8bit 46 个字节左右!

# 使用bitmap来记录周一到周日的打卡! 
setbit sign 0 1
setbit sign 1 0
setbit sign 2 0
setbit sign 3 1
setbit sign 4 1
setbit sign 5 0
setbit sign 6 0

getbit sign 3 # 查看第4天是否打卡

bitcount sign  # 统计这周的打卡记录

五、 Redis发布订阅

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端

批注 20200623 162452.jpg

命令
批注 20200623 162611.jpg

测试

  • 发布者
127.0.0.1:6379> publish sky hello
(integer) 1
  • 接收者
127.0.0.1:6379> subscribe sky
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "sky"
3) (integer) 1
1) "message"
2) "sky"
3) "hello"

hhhhh