Redis是一种基于键值对的NoSQL数据库,redis中的值可以是string,hash,list,set,zset等多种数据结构组成,并且因为Redis将所有的数据都放在内存中,因此读写效率很高。
- Redis特性
速度快、基于键值对的数据结构服务器、功能丰富、持久化、主从复制、高可用与分布式 - Redis应用场景
缓存、排行榜系统(按照发布时间进行排行)、计数器应用(播放量浏览量加1的操作)、社交网络等数据量大的场景、消息队列系统。
基本指令
keys * //查看所有键O(N)
dbsize//查看键总数,O(1)
exist key//是否存在key
del key//删除键
expore key seconds//设置键过期时间
ttl key//返回键剩余过期时间
type key//键的数据类型
string,list这样的数据类型都是对外的数据结构,在redis中每种数据类型都有自己底层的内部编码实现,并且有多种,会根据不同是场景选择最适合的编码实现。
redis单线程
- redis是将所有的数据放在内存中,所以处理很快。
- 非阻塞I/O,采用epoll作为I/O多路复用技术的实现,再加上redis中的事件处理模型将epoll中的连接、读写、关闭都作为事件处理,不会在网络I/O上浪费太多的时间。
3.单线程避免了CPU上下文切换锁带来的消耗。
基本数据类型
字符串string
字符串类型中的键key都是字符串类型,但值可以是多种类型:简单的字符串、复杂的字符串(JSON,XML)等、数字、二进制(图片,音频)等,但是值的最大值不能超过512MB。
常用命令
- 设置值
set key value [ex seconds] [px milliseconds] [nx|xx]
ex seconds:为键设置秒级过期时间
px milliseconds:为键设置毫秒级过期时间
nx:键必须不存在才可以设置成功,用于添加
xx:键必须存在才可以设置成功,用于更新
setnx键必须不存在才可以设置成功。如果有多个客户端执行setnx key value,根据setnx特性只有一个客户端可以设置成功,因此可以作为分布式锁的一种实现方案。
- 获取值
get key
mset key value key value...:批量设置值
mget key value key value...:批量获取值
单次获取设置值:n次网络时间+n次命令时间
批量获取设置值:1次网络时间+n次命令时间 - 计数
incr:自增
decr:自减
incrby:自增指定数字
decrby:自减指定数字
incrbyfloat:自增浮点数
内部编码
int:8个字节的长整形
embstr:小于等于39个字节的字符串
raw:大于39个字节的字符串
Redis会根据当前值的类型和长度决定使用哪种内部编码实现
应用场景
- 缓存功能
将Redis作为缓存层,MySQL作为存储层。如果从Redis中没有获取到信息则将数据回写到Redis中并设置过期时间。
如果mysql中数据库名为vs,用户表为user,对应的键可以设置为vs:user:name。 - 计数功能
因为Redis是单线程的并不会使用到CAS这样的机制来导致CPU开销。 - 共享Session
在一个分布式web服务器上,用户的session信息会保存在单个服务器中,但因为负载均衡的存在,分布式会将用户的访问均衡到不同服务器因此导致session信息失效需要重新登陆。
可以采用Redis对session信息统一管理。
哈希hash
哈希类型在Redis中value值又是一个filed-value的键值对类型。
基本命令
hset
hget
hdel
hmget
hmset
hvals:获取xxc全部value值,即txc,23
hgetall:获取所有的filed-value值。O(n)的操作,如果哈希元素很多,可能会阻塞Redis,可以使用hscan命令,渐进式遍历哈希类型。
内部编码
1.ziplist(压缩列表):当哈希类型元素个数小于默认配置512个时,并同时所有值都小于默认配置64个字节时会使用ziplist作为hash的内部编码实现。
2.hashtable(哈希表):当ziplist无法满足时会使用哈希表作为内部实现。
应用场景
保存用户个人信息
哈希类型保存用户个人信息更加直观。
哈希类型是稀疏的,而关系型数据库是完全固定结构化的。例如哈希类型时每个键都可以有不同的field,而关系型数据库只要添加了新的列,所有行都要为其赋值即使是NULL。
关系型数据库可以做复杂的关系查询,如果用Redis去模拟关系型复杂查询比较困难。
目前已经有三种方法保存用户信息。
1.字符串string类型:简单直观,每个属性都支持更新操作,但是占用内存大,同时用户内聚性差。
2.序列化字符串类型:将用户信息序列化后用一个键保存(set user:1 serialize(userInfo)),合理化的序列化可以提高内存的使用效率,但是序列化和反序列化都需要开销,并且每次更新操作都要取出全部的数据。
3.哈希类型:每个用户属性使用一对filed-value来保存,但是只用一个键保存。简单直观,合理使用可以减小内存空间。控制底层内部编码的转化,hashtable会消耗更多内存。
列表list
列表类型用来存储多个有序的字符串。插入有序,可以重复。
基本命令
rpush:右边插入元素 rpop
lpush:左边插入元素 lpop
阻塞操作 blpop brpop
blpop key [key...] timeout
1.当列表元素为空,如果timeout = 3,则要等待3秒才能返回。如果timeout=0会一直阻塞等待直到列表中添加了值。
如果在此期间向列表中添加了数据,客户端会立即返回。
2.当列表不为空时,不会阻塞,立即返回结果。
3.如果是多个键,brpop会从左到右遍历键一旦有一个键能弹出元素客户端立即返回。
brpop list:1 list:2 list:3 0
此时另外一个客户端依次向list:2 list:3中插入元素,会立即先返回list:2 再返回list:3的值。
4.如果多个客户端对同一个键执行brpop,会按照顺序最先执行命令的客户端最先获取值。
内部编码
ziplist(压缩列表)、linkedlist(链表)、Redis 3.2后提供了quicklist结合了两者的优势。
应用场景
- 消息队列
可以把消息队列比作一个容器,当我们需要消息时则取出消息使用,主要是通过异步处理来提高系统的性能和削峰。
Redis的lpush和brpop即可实现消息队列。生产者客户端使用lpush从左侧插入元素,多个消费者客户端使用brpop阻塞式的从右侧获得队列。
2.文章列表
因为list是有序的,可以按照顺序获取。
集合set
集合也是用来保存多个字符串,但不能有重复元素,元素也是无序的。
Redis除了对集合内的元素进行增删改查,还支持多个集合取交集、并集、差集。
应用场景
用户标签
有序集合Zset
有序集合不可以有重复元素,但可以按照指定的分数对元素进行排序。
zadd key score member [score member...]
应用场景
维护一个排行榜系统:按照点赞数、浏览数等排行
Bitmaps
Bitmaps可以实现对位的操作,可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存放0或者1.
应用场景
记录独立用户是否访问过网站,被访问的用户记做1,没有访问的用户记作0,用偏移量作为用户的id。
事务与Lua
Redis提供了简单的事务功能,将一组需要执行的命令放在multi和exec两个命令之间。multi代表事务开始,exec代表事务结束,他么之间的指令时按原子顺序执行的。但事务并不支持回滚,当一个事务中一个命令执行错误,前面执行成功的指令并不会回滚。
Lua脚本语言在Redis是原子执行的,执行过程中不会插入其他命令。
发布订阅
Redis主要提供了发布消息、订阅频道、取消订阅等命令。
publish channel message:向channel通道发布message消息。
subscribe channel [channel...]:订阅一个通道或多个通道
新开启的订阅客户端无法接受该频道之前的消息,因为redis不会对发布的消息进行持久化。
JAVA客户端Jedis
1.使用Jedis直接连接redis是采用的TCP连接,简单方便但是开销较大。
2.也可以采用Jedis连接池的形式,不需要每次连接都生成Jedis对象。