每天一点新知识,迟来的redis
为什么使用redis
在项目中使用redis,主要是从两个角度去考虑:性能和并发;
性能:在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应;
并发:大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库
使用redis有什么优缺点
优点:
1、性能好,速度快:由于是全内存操作,所以读写性能很好,可以达到10w/s~80w/s的频率。
2、支持多种数据类型:string(字符串),hash(哈希),list(列表),set(集合),zset(sorted set:有序集合),redis相对简单,直接支持list的存储(采用双向链表或者压缩链表的存储方式)。
3、持久化存储:作为内存数据库,最担心的就是宕机数据会丢失,redis使用rdb和aof持久化存储,主从数据同时,生成rdb文件,并利用缓冲区添加新的数据更新操作做对应的同步。(数据存于内存中,磁盘仅用于持久)
4、支持事务,操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。
5、多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。
缺点:
分析:大家用redis这么久,这个问题是必须要了解的,基本上使用redis都会碰到一些问题,常见的主要四个问题如下:
1、缓存雪崩:
原因:
redis容量依附机器内存的大小,这就决定了数据不能全部缓存起来,内存价格贵且有限,所以redis需要设置过期时间,采用的是惰性删除和过期删除两种策略对过期键删除。
雪崩就是指缓存中大批量热点数据过期后系统涌入大量查询请求,因为大部分数据在Redis层已经失效,请求渗透到数据库层,大批量请求犹如洪水一般涌入,引起数据库压力造成查询堵塞甚至宕机。
如何解决:
1、将缓存失效时间散开,给过期时间加随机值,大幅度减少缓存在同一时间过期
2、事发前:实现Redis的高可用(主从架构+Sentinel 或者Redis Cluster),尽量避免Redis挂掉这种情况发生;事发中:万一Redis真的挂了,我们可以设置本地缓存(ehcache)+限流(hystrix),尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的);事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
2、缓存穿透:
原因:
数据库不存在数据,缓存的时候查不到数据则不存入缓存中,这导致不存在的数据每次获取都要查询数据库,失去了缓存的意义,使用场景为黑客用参数攻击数据库。
如何解决:
1、请求不合法或者参数不合法,就不让请求到数据库层
2、请求数据库的值为空时,把空对象存到缓存中,下次直接读取空对象,同时将空对象设置为较短的过期时间
3、缓存击穿:
原因:
热点数据频繁请求,在redis失效的瞬间,大量请求击穿缓存,直接请求数据库,就像屏障上凿了一个洞。
解决:
1、热点数据设置为永不过期
2、使用redis or zookeeper实现互斥锁,等待第一个请求缓存构建完成后在释放锁,进而其他请求才能通过key访问数据
public String get(key) {
String value = redis.get(key);
if (value == null) { //代表缓存值过期
//设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(key_mutex);
} else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
get(key); //重试
}
else {
return value;
}
}
4、缓存和数据库双写一致性:
原因:因为读缓存和写数据库是同时进行的,不能保证顺序,所以会出现数据不一致的情况。
1、删除缓存中数据后,新数据没有没来的及更新到数据库中,另一线程读缓存数据为空,然后读取数据库旧数据存入缓存,则此时缓存中数据为脏数据。
2、先写库,没删缓存,写库线程宕机,没有删除缓存,也会产生数据不一致的情况。
解决:
1、延时双删:读请求结束后,写请求可以删除读请求产生的缓存脏数据
具体步骤:
- 先删缓存
- 写数据库
- 休眠***毫秒
- 再删缓存
2、异步更新缓存
5、缓存的并发竞争key问题
不要求顺序的话:可以用分布式锁,大家同时抢锁,谁抢到谁先set
要求顺序的话:
单线程redis为什么速度快?
- 纯内存
- 单线程,避免频繁的上下文操作
- 非阻塞I/O多路复用
五种数据类型
string:最常规的set/get操作,value可以是String也可以是数字,一般做一些复杂的计数功能的缓存
代码示例:
- key配置
2.set(key,value,second)
emo1:
emo2:多个餐厅多个日期多个用户多个餐次多个餐桌【key】…下预定了什么菜品【value】
emo3:
扩展:sprintf() 把百分号(%)符号替换成一个作为参数进行传递的变量:
$number = 9;
$str = "RUNOOB";
$txt = sprintf("%s 每天有 %u 万人在访问!", $str, $number);
echo $txt;// RUNOOB 每天有 9 万人在访问!
- get(key)
emo1:
emo2:
emo3:
hash哈希:value存放的是结构化的对象或二维数组,比如单点登录的时候,可以用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果
代码示例:
-
key配置
-
set (hash的key,项的key,项的value)
-
get(hash的key,项的key)
list列表:消息队列,先进先出,也可以利用lrange命令,做基于redis的分页功能,性能极佳
set集合:全局去重,另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。
sorted set有序集合:多了一个权重参数score,集合中的元素能够按score进行排列,可做排行榜应用,取TOP N操作
redis删除策略和内存淘汰机制
redis采用的是定期删除+惰性删除:定期删除是默认每隔100ms抽查key是否过期,过期删除key,这样就会造成很多其他的key到了过期时间,但还是没有被删除,所以用到惰性删除,获取key的时候判断是否设置过期时间,如果设置过期时间,并且已过期,则删除该key
这两者都没删除掉的过期key,要用到内存淘汰机制,在redis.conf中配置:
# maxmemory-policy volatile-lru
redis持久化方式
-
RDB
redis数据都是存放在内存中,然后不定期通过异步方式将数据存放磁盘中(半持久化模式)
将内存中的数据以快照方式存放二进制文件中,默认名称为dump.rdb,每隔一段时间将全部的redis数据存磁盘,造成性能问题,并且如果redis意外宕机,就可能会丢失最后一次快照后的数据修改
我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000 -
AOF
也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。aof 比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)
appendonly yes //启用aof持久化方式
appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用
appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐
appendfsync no //完全依赖os,性能最好,持久化没保证
redis虚拟内存
硬盘:存储资料和软件等数据的设备,有容量大,断电数据不丢失的特点,也被人们称之为“数据仓库”。
内存:1. 负责硬盘等硬件上的数据与CPU之间数据交换处理;2. 缓存系统中的临时数据。3. 断电后数据丢失。
1、数据库比如Mysql,将数据存在硬盘中,在mysql的安装目录下,有一个mysql.ini文件,里面配置data的存储目录:datadir =C:\application\mysql-5.6.24-win32\data
2、redis是一个内存数据库,所有数据基本上都存在于内存当中,会定时以追加或者快照的方式刷新到硬盘中,由于redis是一个内存数据库, 所以读取写入的速度是非常快的, 所以经常被用来做数据、页面等的缓存
redis和memcached有什么区别
1)存储方式:
- memcache全部存储在内存中
- redis部分存储在硬盘中
2)数据类型:
- memcache支持string类型
- redis支持多种类型
3)使用底层模型不同:
- redis构建自己的VM机制
4)其他
- redis比memcache快
- redis支持持久化