Redis 代表REmote DIctionary Server。它由 Salvatore Sanfilippo 在 2006 年用 C 语言编写。它是一个 NoSQL 高级键值数据存储。Redis 中的读写 *** 作非常快,因为它将所有数据存储在内存中。数据也可以存储在磁盘上,也可以写回内存。
它通常被称为数据结构服务器,因为键可以包含字符串、散列、列表、集合、排序集合、位图和超日志。
NoSQL 意味着not only SQL。NoSQL 数据库不定义像表这样的数据库结构,也不支持查询,比如 SQL SELECT。数据主要存储为 JSON 对象或键值对。
Redis 有什么作用?由于 Redis 将其数据存储在内存中,因此它最常用作缓存;
访问数据库的效率比较低,在请求与数据库之间加上 redis 缓存,由于缓存里面的数据在内存中,请求在一定程度上避免了和磁盘直接打交道,提升了数据返回的运行效率;
应用场景:
配合关系型数据库做高速缓存
1、高频次,热门数据的访问,降低数据库的 IO;
2、分布式架构,当做 session 共享;
3、多样化的数据结构支持持久化数据;
虽然使用缓存可以大大的提升系统的访问效率,但是同时也伴随着其他的问题需要解决,那就是缓存穿透、缓存击穿以及缓冲雪崩的情况是存在的;
存在的问题就是,为了提升访问效率添加的缓存在某些特定的场景之下,缓存失效了(缓存中有需要访问数据缓存才不会失效,其余的都相当于是缓存失效了,虽然片面但是可以这样理解)
,使得整个系统的性能遭受到了一定的影响;
缓存穿透:缓存以及数据库中都没有某一个数据,使得每次的 web 请求都是直接访问到数据库的,缓存失去了原本的提升效率的作用;
缓存击穿:缓存中的某个 key 在某一瞬间失效了,但是数据库还存在这个 key ,突然大量的请求前来,直接访问了数据库,导致缓存失去了原本提升效率的作用;
缓存雪崩:缓存中大量的 key 都失效了,导致请求直接访问到了数据库,使得数据库的压力增大了;
所有的所有所有导致的各种缓存问题,根本原因就是,缓存中没有用户要访问的数据或者,缓存中用户需要的数据过期了
,使得,请求与底层的数据库直接打交道,缓存失去了作用,导致的问题;
缓存穿透解决方式:
1、对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟,这样可以防止攻击用户反复用同一个id暴力攻击。
2、设置可访问的名单(白名单):
使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。
3、采用布隆过滤器:布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力。
4、进行实时监控:当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。
缓存击穿解决办法:
1、预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长。
2、实时调整:现场监控哪些数据热门,实时调整key的过期时长。
3、加互斥锁:流程如下图所示
缓存击穿指在某一极小时间段内,对同一过期数据的大量访问;
(大量请求都访问某一个过期的key)
缓存雪崩指在某一极小时间段内,对很多已过期的不同数据的大量访问;
(大量请求都访问某一批过期的key)
与缓存击穿的图示基本一致,只是同一时间访问的 key 失效的数量的多少不一样而已
缓存中大量的 key 都失效了,导致了缓存雪崩;
Redis 常用命令与 Redis 有关的命令都是与 *** 作 key 与 value 的;
与 key 相关的命令|命令 |命令的作用 |
|--|--|
| <key> * | 查看当前库的所有 <key> |
| exist <key> | 判断某个 <key> 是不是存在 |
| type <key> | 判断 <key> 是什么类型 |
| del <key> | 删除某个指定的 <key> |
| unlink <key> | 异步的删除 <key> |
| expire <key> 10 | 为给定的<key>设置过期时间 |
| ttl <key> | 查看 <key> 的状态 -1 表示永不过期 ,-2 表示已经过期了|
| select | 切换数据库 |
| dbsize | 查看数据库的容量 |
| flushdb | 清空当前库 |
| flushall | 清空所有库 |
Redis - 数据类型 - 底层的数据结构
Redis 基本的数据类型有如下几种,在后序的版本中,可能添加新的疏浚类型,同一个数据类型实现的数据结构也是不一样的,比如 String 类型实现它的数据类型有 embstr、raw、int 三种,Redis 底层根据不同的情况选用不同的数据结构,减少内存空间的浪费;
对于不同的编码方式,也就是不同的编码方式对应的数据结构简单说明表:
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象;
什么是二进制安全
二进制安全是指,在传输数据时,保证二进制数据的信息安全,也就是不被篡改、破译等,如果被攻击,能够及时检测出来。
二进制安全包含了密码学的一些东西,比如加解密、签名等。
举个例子: 把数据11110000加密成10001000,然后传送,就是一种二进制安全的做法
String 常用的命令
| set <key> <value> | 给具体的 <key> 设置 <value> |
| get <key> | 查询对应的键值 |
| strlen <key> | 获取键对应的值的长度 |
| setnx <key> <value> | 当键<key>不存在的时候,设置 <value> |
| incr <key> | 将 key 中存储的数值加一;只能对数字值 *** 作,如果为空,新增值为 1 ;|
| decr <key> | 将 key 存储的数字减一 ,只能对数字 *** 作,如果为空新增加的数字为 -1|
| incrby <key> <步长> |将 key 中存储的数字值按照步长进行增加,自定义步长|
| mset <key1><value1><key2><value2> |同时设置一个或多个 key-value 对|
| mget <key1><key2><key3> | 同时获取多个 key 对应的 value |
| msetnx <k1> <value1> <k2> <value2> | 同时设置一个或者多个 key - value 对,要进行赋值 |的键值对是不存在的,存在的话不能设置成功;|
| getrange <key> <起始位置> <结束位置> | 获取起始结束位置的 key 对应的 value
| setex <key> <过期时间> <value> | 设置键值对的过期时间|
| getset <key> <value> | 以新换旧,设置了新值同时获得旧值 |
底层的数据结构 - SDS(simple dynamic string)的实现
简单的动态字符串
底层是 C++ 实现的一种简单动态字符串的数据结构,类似于 Java 的ArrayList ,采用分配冗余空间的形式,减少内存的频繁分配;
capacity 的长度一般长于字符串长度 len ,字符串小于 1 M的时候,扩容加倍现有的空间,超过 1 M,每次扩容只会扩容 1M 的空间;
最大的字符串长度是 512M;
Redis - List单键多数值;
常用命令// 在每个命令前面加上 l 表示的是关于 list 的 *** 作
lpush/rpush <key> <value1> <value2> <value3> 从左边/右边插入一个或多个值。
lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值光键亡。
rpoplpush <key1> <key2>从<key1> 列表右边吐出一个值,插到<key2>列表左边。
lrange <key> <start> <stop> 按照索引下标获得元素(从左到右)
lrange mylist 0 -1 0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex <key> <index> 按照索引下标获得元素(从左到右)
llen <key> 获得列表长度
linsert <key> before <value> <newvalue> 在<value>的后面插入<newvalue>插入值
lrem <key> <n> <value> 从左边删除n个value(从左到右)
lset<key> <index> <value> 将列表key下标为index的值替换成value
底层的数据结构 - 双向链表 LinkedList 的实现
双向链表,实现的性能比较高,对于两端之间的数据 *** 作的性能是比较好的,但是中间结点的 *** 作就会慢一些;
每一个 key 对应的是一个双向链表,可以对一个 key 添加多个 value ;
列表元素比较少的时候,使用 ziplist ,压缩了空间,所有的元素是连续放置在一个内存空间中的;
与 List 类似的功能,但是Set 是可以自动的去重的,就是存储的元素需要是唯一的;在希望存储数据,但是存储的数据必须唯一的时候,使用 Set 这种数据类型是合适的;
常用命令// 在每一个命令前面都加上一个 s 表示关于 Set 类型元素的 *** 作
sadd <key> <value1> <value2> ... <key> 中添加多个元素
smembers <key> 取出该集合的所有值。
sismember <key> <value> 判断集合<key>是否为含有该<value>值,有1,没有0
scard<key> 返回该集合的元素个数。
srem <key> <value1> <value2> .... 删除集合中的某个元素。
spop <key> 随机从该集合中吐出一个值。
srandmember <key> <n> 随机从该集合中取出n个值。不会从集合中删除 。
smove <source> <destination> value 把集合中一个值从一个集合移动到另一个集合
sinter <key1> <key2> 返回两个集合的交集元素。
sunion <key1> <key2> 返回两个集合的并集元素。
sdiff <key1> <key2> 返回两个集合的差集元素(key1中的,不包含key2中的)
底层数据结构 - Hash 的实现
Redis -Hash
常用命令
hset <key> <field> <value> 给<key>集合中的 <field>键赋值<value>
hget <key1> <field> 从<key1>集合<field>取出 value
hmset <key1> <field1> <value1> <field2> <value2>... 批量设置hash的值
hexists<key1> <field> 查看哈希表 key 中,给定域 field 是否存在。
hkeys <key> 列出该hash集合的所有field
hvals <key> 列出该hash集合的所有value
hincrby <key> <field> <increment> 为哈希表 key 中的域 field 的值加上增量 1 -1
hsetnx <key> <field> <value> 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
底层数据结构 - Hash 的实现
key 是普通的数据;
value 是一种 Redis Hash 的这种数据结构类似于 Map
Redis Hash 是一个 String 类型的 field(字段) 和 value(值) 的映射表;
下面是 Hash 的一个应用(在 value 里面的存储是 Hash 结构)
:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)