了解Redis
Posted hetaoyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了了解Redis相关的知识,希望对你有一定的参考价值。
原文来自https://blog.csdn.net/weixin_43224539/article/details/98735629
什么是Redis?
Redis是一个开源的底层用C语言编写的key-value型存储数据库,可用于缓存,事件发布订阅(网络新闻),高速队列等场景
支持什么数据类型?
支持String、List、Set、Hash、ZSet(sorted set),这些数据类型都支持add/remove、push/pop及取交集、并集和差集的丰富操作,这些操作都是原子性的,与此同时
Redis还支持各种排序
再谈使用场景?
1、缓存数据
热点数据,对经常需要查询使用 但 变动不是很频繁的数据
2、消息队列
相当于消息订阅系统,比如ActiveMQ、RocketMQ。如果对数据有较高一致性要求时,还是建议使用MQ)
3、计数器
数据统计的需求非常普遍,比如统计点击率、点赞率,收藏数,分享数等,redis具有原子性,可以避免并发问题
4、电商网站信息
大型电商平台初始化页面数据的缓存。比如去哪儿网购买机票的时候首页的价格和你点进去的价格会有差异。
5、热点数据
比如新闻网站实时热点、微博热搜等,需要频繁更新。总数据量比较大的时候直接从数据库查询会影响性能
6、会话缓存
此外,还可以考虑使用 Redis 进行会话缓存。例如,将 web session 存放在 Redis 中。
7、访问频率
出于减轻服务器的压力或防止恶意的洪水攻击的考虑,需要控制访问频率,例如限制 IP 在一段时间的最大访问量
单节点服务器通常是这样的
加入缓存是这样的
数据库层与缓存进行交互,如果缓存中有数据直接返回给客户端,如果没有才会从mysql中去查询,从而减小了数据库的压力,提升了访问的效率
那么问题来了,既然Redis是一块缓存数据库,为什么会这么快?
1、Redis是纯内存操作,需要的时候需要我们手动持久化到硬盘中
2、Redis是单线程,从而避开了多线程场景下上下文频繁切换的操作
3、Redis的数据结构简单,对数据的操作也比较简单
4、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis自己实现了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
5、使用多路I/O复用模型,非阻塞I/O
(什么是多路I/O复用?I/O多路复用技术是为了解决进程或线程阻塞到某个I/O系统调用而出现的技术,可以监视多个描述符,一旦某个描述符就位(一般是读就绪或者写就绪,就是这个文件描述符进行读写操作之前),就能通知程序进行相应的读写操作)
Redis的性能极高,读的速度是110000次/s,写的速度是81000次/s,支持事务,备份,丰富的数据类型
再来谈谈Redis的缺点?
1、由于是内存数据库,所以单台机器存储的数据量是有限的,需要开发者提前预估,需要及时删除不必要的数据
2、当修改Redis的数据之后需要将持久化到硬盘的数据重新加入到内容中,时间比较久,这个时候Redis是无法正常运行的
对比Redis和Memcached?
网络模型
Memcached是多线程,非阻塞IO复用的网络模型
Redis使用单线程的I/O复用模型,单线程可以将速度优势发挥到最大
支持数据类型?
Memcached使用key-value型存储和访问数据,在内存中维护一张巨大的HashTable,使得对数据查询的时间复杂度降低到了O(1),保证对数据的高性能访问
Redis与Memcached相比,补偿了仅用key-value型数据存储的不足,还提供了String、List、Set、ZSet、Hash等数据结构的存储
再谈谈内存管理机制?
♦Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。
♦Memcached使用预分配的内存池的方式,使用slab和大小不同的chunk来管理内存,Item根据大小选择合适的chunk存储,内存池的方式可以省去申请/释放内存的开销,并且能减小内存碎片产生,但这种方式也会带来一定程度上的空间浪费,并且在内存仍然有很大空间时,新的数据也可能会被剔除
♦Redis的内存管理主要通过源码中zmalloc.h和zmalloc.c两个文件来实现的。Redis为了方便内存的管理,在分配一块内存之后,会将这块内存的大小存入内存块的头部
♦Redis使用现场申请内存的方式来存储数据,并且很少使用free-list等方式来优化内存分配,会在一定程度上存在内存碎片,Redis跟据存储命令参数,会把带过期时间的数据单独存放在一起,并把它们称为临时数据,非临时数据是永远不会被剔除的,即便物理内存不够,导致swap也不会剔除任何非临时数据(但会尝试剔除部分临时数据),这点上Redis更适合作为存储而不是cache。
数据存储及持久化
♦Memcached不支持内存数据的持久化操作,所以重启之后之前的数据就不存在了所有数据都以 in - memory的形式存储
♦Redis支持内存持久化操作,所以重启之后之前的数据还在,Redis还提供了两种不同的持久化方法来将数据存储到硬盘里面,一种是“快照”,可以将存在于某一时刻的所有数据都写在硬盘里面,
另一种方法叫“只追加文件”,它在执行写命令的时候将被执行的写命令复制到硬盘里面
数据一致性问题
♦Memcached提供了cas命令,可以保证多个并发访问操作同一数据一致型的问题
♦没有提供cas命令,并不能保证这点,不过Redis提供了事务功能,可以保证一串命令的原子性,中间不会被任何操作打断
集群管理不同
♦Redis虽然支持数据的持久化,但是全内存毕竟才是其高性能的本质,作为基于内存的存储系统来说,机器物理内存的大小就是系统能够容纳的最大数据量。如果需要处理的数据量超过了
单台机器的物理内存大小,就需要构建分布式集群来扩展存储能力,Redis更偏向于在服务器端构建分布式存储
♦Memcached是全内存的数据缓冲系统,Memcached只能采用客户端实现分布式存储,需要将数据拿到客户端来进行类似的修改再Set回去,这大大增加了网络IO的次数和数据体积
通过一张图了解下Redis内部内存管理中是如何描述这些不同数据类型的:
首先Redis内部使用一个redisObject对象来表示所有的key和value,redisObject最主要的信息如上图所示:
type代表一个value对象具体是何种数据类型,
encoding是不同数据类型在redis内部的存储方式,
比如:type=string代表value存储的是一个普通字符串,那么对应的encoding可以是raw或者是int,如果是int则代表实际redis内部是按数值型类存储和表示这个字符串的,当然前提是这个字符串本身可以用数值表示,比如:"123" "456"这样的字符串
redis数据类型的应用
String(字符串)
string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
Hash(哈希)
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
类似Java里面的Map<String,Object>
Redis的Hash内部存储方式,用户ID为key,value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题
List(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)
它的底层实际是个链表
Set(集合)
Redis的Set是string类型的无序集合。它是通过HashTable实现实现的
zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。
redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
redis发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
使用Redis
使用redis
Redis 键(key)
设置键值对:set myKey abc
取出键值对: get mykey
查询所有key:keys *
判断某个key是否存在: exists key名 返回1-yes 返回 0 -no
移除key: move key db 返回1-yes 返回 0 -no
得到key的value : get key
expire key 秒钟:为给定的key设置过期时间
ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期,即已被删除
type key 查看你的key是什么类型
String类型:
设置键值对:set key value
将新的字符串value加入到原来key指向的字符串来: append key value
将key指向字符串长度:strlen key
在原字段上加1: incr key
在原字段上减1decr key
在原字段上加上整数(increment) :incrby key increment
在原字段上减去整数(increment)decrby key increment
mset:同时设置一个或多个 key-value 对。
mget:获取所有(一个或多个)给定 key 的值。
msetnx:同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在,否则不生效 ,或者要么都不存在,直接生成
设置字符串键值对
链表(List)
链表结构是 Redis 中一个常用的结构,它可以存储多个字符串,而且它是有序的
Redis 链表是双向的
使用链表结构就意味着读性能的丧失
链表只能从单个方向中去遍历所要节点
优势:在于插入和删除的便利
链表的数据节点是分配在不同的内存域的,并不连续,需要经常查找的,使用它性能并不佳,它只能从左到右或者从右到左的查找
和比对。
lpush: 从链表的左边插入
rpush: 从链表的右边插入
lrange :查看链表的节点值
lrange list start end end=-1代表全部 0 -1 表示全部
lpop:删除左边的第一个节点,并且将其返回
rpop:删除右边第一个节点,并且将其返回
lindex,按照索引下标获得元素(从上到下)
lindex: lindex key index,索引从零开始,通过索引获取列表中的元素
llen :求链表的长度,返回长度
lrem key 删N个value
* 从left往right删除2个值等于v1的元素,返回的值为实际删除的数量 如lrem list 2 3 就是删除两个3
* LREM list3 0 值,表示删除全部给定的值。零个就是全部值
lrem list 个数 节点值
ltrim key 开始index 结束index,截取指定范围的值后再赋值给key
ltrim:截取指定索引区间的元素,格式是ltrim list的key 起始索引 结束索引
【start end】 开区间 会把截取的内容保存在原来的list中,默认截取位置从0 开始
rpoplpush 源列表 目的列表
移除列表的最后一个元素,并将该元素添加到另一个列表并返回
移除源列表的最后一个元素并且将其左插到目的列表中
lset key index value 如:lset list 0 a 将第一个元素修改为a
给index出的索引修改值
linsert key before/after 值1 值2
在值1 前面/后面 插入值2
Hash(哈希)
Redis的hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象
同时设置多个field =>value: hmset key field1 value1 field2 value2
获取对应field的value: hget key field
Set集合
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
添加一个 string 元素到 key 对应的 set 集合中
sadd key member 添加成功返回1,若元素已存在返回0,若 key 对应的 set 不存在则返回错误
查看set元素: smembers key
一个set中相同元素只能有一个,集合元素唯一性
zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复
添加元素到集合,元素在集合中存在则更新对应score: zadd key score member
按照score排序:zrangebyscore (按照字母A-Z排序)
以上是关于了解Redis的主要内容,如果未能解决你的问题,请参考以下文章