redis的数据结构以及使用场景
为什么会有redis的出现?
NoSQL,泛指非关系型的数据库。什么是关系型数据库,什么又是非关系型数据库呢。这得先从数据结构谈起。因为数据结构的不同大致可以分为三类:1.结构化数据 2.非结构化数据 3.半结构化数据。
结构化数据指的是由二维表结构来逻辑表达和实现的数据,严格遵循数据格式与长度规范,也称作为行数据,特点为:数据以行为单位,一行数据表示一个实体的信息,每一行数据的属性是相同的。
因此关系型数据库MySql之类的完美契合结构化数据的特点,关系型数据库也是关系型数据最主要的存储与管理引擎。
那么问题来了,关系型数据库的优缺点在哪里呢。
优点:
1.易理解
2.操作方便,支持join等复杂查询,Sql + 二维关系是关系型数据库最无可比拟的优点,这种易用性非常3.贴近开发者
4.数据一致性,支持ACID(原子性,一致性,隔离性,持久性)特性,可以维护数据之间的一致性
5.数据稳定,数据持久化到磁盘,没有丢失数据风险,支持海量数据存储
6.服务稳定,通常很少出现宕机异常
缺点:
1.高并发下IO压力大,
2.为维护索引付出的代价大。为了提供丰富的查询能力,通常热点表都会有多个二级索引,一旦有了二 级索引,数据的新增必然伴随着所有二级索引的新增,数据的更新也必然伴随着所有二级索引的更新,3.这不可避免地降低了关系型数据库的读写能力,且索引越多读写能力越差。
4.为维护数据一致性付出的代价大,事务是有隔离级别的,事务隔离级别越低,可能出现的并发异常越 多,但是通常而言能提供的并发能力越强。只要提供的隔离级别越高,那么读写性能必然越差。
这上面反映出的一个问题就是关系型数据库在高并发下的能力是有瓶颈的,尤其是写入/更新频繁的情况下,出现瓶颈的结果就是数据库CPU高、Sql执行慢、客户端报数据库连接池不够等错误,因此例如万人秒杀这种场景,我们绝对不可能通过数据库直接去扣减库存。通常在企业规模不断扩大的情况下,不会一味指望通过增强数据库的能力来解决数据存储问题,而是会引入其他存储,也就是我们说的NoSql。
常见的NoSql分别有Redis,Memcache和MongoDb。NoSql是对关系型数据库的一种补充,这意味着NoSql与关系型数据库并不是对立关系,二者各有优劣,取长补短,在合适的场景下选择合适的存储引擎才是正确的做法。
这里我们就详细介绍一下redis的数据结构以及对应的使用场景。
当前的 Redis 支持 6 种数据类型,它们分别是字符串(String)、列表(List)、集合(set)、哈希结构(hash)、有序集合(sorted set)和基数(HyperLogLog)。
1.String数据类型
string是redis最基本的类型,一个 key 对应一个 value。value其实不仅是String,也可以是数字。string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象,但是string 类型的值最大能存储 512MB。
应用场景:String是最常用的一种数据类型,普通的key/ value 存储都可以归为此类,即可以完全实现目前 Memcached(memcached是一套分布式的快取系统,与redis相似) 的功能,并且效率更高。还可以享受Redis的定时持久化,操作日志及 Replication等功能。一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
普通字符串的基本操作:
set key value #设置 key-value 类型的值
get key # 根据 key 获得对应的 value
exists key # 判断某个 key 是否存在
strlen key # 返回 key 所储存的字符串值的长度。
del key # 删除某个 key 对应的值
mset key1 value1 key2 value2 # 批量设置 key-value 类型的值
mget key1 key2 # 批量获取多个 key 对应的 value
计数器:
set number 1
incr number # 将 key 中储存的数字值增一
get number
decr number # 将 key 中储存的数字值减一
get number
数据过期:
expire key 60 # 数据在 60s 后过期
setex key 60 value # 数据在 60s 后过期
ttl key # 查看数据还有多久过期
2. list
1.介绍:list 即是链表.链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList,但是 C 语言并没有实现链表。Redis 的 list 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。临时存储。先进先出。使用双向链表:
2.用命令: rpush,lpop,lpush,rpop,lrange、llen等。
3.应用场景:发布与订阅或者说消息队列、慢查询。
队列:
rpush myList value1 # 向 list 的头部(右边)添加元素
结果:(integer) 1
rpush myList value2 value3 # 向list的头部(最右边)添加多个元素
结果:(integer) 3
lpop myList # 将 list的尾部(最左边)元素取出
结果:“value1”
lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
结果:1) “value2”
2) “value3”
lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
结果:1) “value2”
2) “value3”
通过rpush/rpop实现栈:
rpush myList2 value1 value2 value3
(integer) 3
rpop myList2 # 将 list的头部(最右边)元素取出
“value3”
通过 lrange
查看对应下标范围的列表元素:
rpush myList value1 value2 value3
结果:(integer) 3
lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
结果:1) “value1”
2) “value2”
lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
结果:1) “value1”
2) “value2”
3) “value3”
通过 lrange 命令,可以基于 list 实现分页查询。
通过llen查看链表长度:
llen myList
结果:(integer) 3
3.Hash
1.介绍 :hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field 和 value 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。
2.常用命令:hset,hmset,hexists,hget,hgetall,hkeys,hvals`等。
3.应用场景:系统中对象数据的存储。
下面我们看看它的使用:
hset Key name “guide” description “dev” age “24”
结果:OK
hexists Key name # 查看 key 对应的 value中指定的字段是否存在。
结果:(integer) 1
hget Key name # 获取存储在哈希表中指定字段的值。
结果:“guide”
hget Key age
结果:“24”
hgetall Key # 获取在哈希表中指定 key 的所有字段和值
结果:1) “name”
2) “guide”
3) “description”
4) “dev”
5) “age”
6) “24”
hkeys Key # 获取 key 列表
结果:1) “name”
2) “description”
3) “age”
hvals Key # 获取 value 列表
结果:1) “guide”
2) “dev”
3) “24”
hset Key name “GuideGeGe” # 修改某个字段对应的值
hget Key name # 查看
结果:“GuideGeGe”
4.set
1.介绍 :set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。
2. 常用命令:sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。
3. 应用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
下面我们看看它的使用:
sadd mySet value1 value2 # 添加元素进去
结果:(integer) 2
sadd mySet value1 # 不允许有重复元素
结果:(integer) 0
smembers mySet # 查看 set 中所有的元素
结果:1) “value1”
2) “value2”
scard mySet # 查看 set 的长度
结果:(integer) 2
sismember mySet value1 # 检查某个元素是否存在set 中,只能接收单个元素
结果:(integer) 1
sadd mySet2 value2 value3
结果:(integer) 2
sinterstore mySet3 mySet mySet2 # 获取 mySet 和 mySet2 的交集并存放在 mySet3 中
结果:(integer) 1
smembers mySet3
结果:1) “value2”
5. sorted set
- 介绍:和 set 相比,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
- 常用命令:zadd,zcard,zscore,zrange,zrevrange,zrem等。
- 应用场景:需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 为权重
结果:(integer) 1
zadd myZset 2.0 value2 1.0 value3 # 一次添加多个元素
结果:(integer) 2
zcard myZset # 查看 sorted set 中的元素数量
结果:(integer) 3
zscore myZset value1 # 查看某个 value 的权重
结果:“3”
zrange myZset 0 -1 # 顺序输出某个范围区间的元素,0 -1 表示输出所有元素
结果:1) “value3”
2) “value2”
3) “value1”
zrange myZset 0 1 # 顺序输出某个范围区间的元素,0 为 start 1 为 stop
结果:1) “value3”
2) “value2”
zrevrange myZset 0 1 # 逆序输出某个范围区间的元素,0 为 start 1 为 stop
结果:1) “value1”
2) “value2”