0.序章
Redis是一个内存数据库(或者说内存数据结构)服务器,可以存储5种不同键值对的数据。
热点数据,共享session,为的是服务无状态
0.1 什么时候使用Redis缓存
0.1.1 什么数据可以放缓存
-
不需要实时更新但是又极其消耗数据库的数据。比如网站上商品销售排行榜,这种数据一天统计一次就可以了,用户不会关注其是否是实时的。
-
需要实时更新,但是更新频率不高的数据。比如一个用户的订单列表,他肯定希望能够实时看到自己下的订单,但是大部分用户不会频繁下单。
-
在某个时刻访问量极大而且更新也很频繁的数据。这种数据有一个很典型的例子就是秒杀,在秒杀那一刻,可能有N倍于平时的流量进来,系统压力会很大。但是这种数据使用的缓存不能和普通缓存一样,这种缓存必须保证不丢失,否则会有大问题。
0.1.2 什么数据不能放缓存
钱、密钥、业务关键性核心数据等不能存放缓存
0.1.3 为什么使用缓存
假如系统中有2千万用户信息,用户信息基本固定,一旦录入很少变动,那么你每次加载所有用户信息时,如果都要请求数据库,数据库编译并执行你的查询语句,这样效率就会低下很多
针对这种信息不经常变动并且数据量较大的情况,通常做法,就是把他加入缓存,每次取数前先去判断,如果缓存不为空,那么就从缓存取值,如果为空,再去请求数据库,并将数据加入缓存,
0.1.4 为什么使用redis
在项目中使用redis,主要是从两个角度去考虑:性能和并发
- 性能:我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UjIFCIH8-1640245210931)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809104528887.png)]
- 并发:在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mqgtsw9H-1640245210934)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809104552923.png)]
0.1.5 redis和数据库双写一致性问题
分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。
0.1.6 如何应对缓存穿透和缓存雪崩问题
缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。
(一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
(二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
(三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
缓存雪崩:缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。
(一)给缓存的失效时间,加上一个随机值,避免集体失效。
(二)使用互斥锁,但是该方案吞吐量明显下降了。
(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点
0.1.7 如何解决redis的并发竞争key问题
分析:这个问题大致就是,同时有多个子系统去set一个key。
(1)如果对这个key操作,不要求顺序
这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
(2)如果对这个key操作,要求顺序
假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.
期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iWKAtJCm-1640245210944)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809110016238.png)]
假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。
第一章 Redis初步
五种数据结构(数据类型) String List Set Hash Zset.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqjHVDtv-1640245210945)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210806111439777.png)]
第二章 使用redis构建web应用
web服务器对对请求进行相应典型步骤:
-
服务器对客户端发来的请求(request)进行解析。
-
请求被转发给一个预定义的处理器(handler )。
-
处理器可能会从数据库中取出数据。
-
处理器根据取出的数据对模板(template)进行渲染(render)。
-
处理器向客户端返回渲染后的内容作为对请求的响应(response)。
1 登录cookie
用关系数据库表中负责存储用户登录信息的条目( entry)。除了用户登录信息之外,Fake WebRetailer 还可以将用户的访问时长和已浏览商品的数量等信息存储到数据库里面,这样便于将来通过分析这些信息来学习如何更好地向用户推销商品。
一般来说,用户在决定购买某个或某些商品之前,通常都会先浏览多个不同的商品,而记录用户浏览过的所有商品以及用户最后一次访问页面的时间等信息,通常会导致大量的数据库写入。
2.购物车cookie
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h5PbD0RV-1640245210948)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809092544308.png)]
3.缓存生成的网页
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KnjUU0qg-1640245210951)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809093300687.png)]
4. 缓存数据库行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TfrkKNqx-1640245210953)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809094043798.png)]
5.分析网页访问记录
第三章 JAVA使用redis
1、连接到redis服务器
import redis.clients.jedis.Jedis;
public class RedisJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
// 如果 Redis 服务设置了密码,需要下面这行,没有就不需要
// jedis.auth("123456");
System.out.println("连接成功");
//查看服务是否运行
System.out.println("服务正在运行: "+jedis.ping());
}
}
2、Redis Java String(字符串) 实例
import redis.clients.jedis.Jedis;
public class RedisStringJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
//设置 redis 字符串数据
jedis.set("runoobkey", "www.runoob.com");
// 获取存储的数据并输出
System.out.println("redis 存储的字符串为: "+ jedis.get("runoobkey"));
}
}
3、Redis Java List(列表) 实例
import java.util.List;
import redis.clients.jedis.Jedis;
public class RedisListJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
//存储数据到列表中
jedis.lpush("site-list", "Runoob");
jedis.lpush("site-list", "Google");
jedis.lpush("site-list", "Taobao");
// 获取存储的数据并输出
List<String> list = jedis.lrange("site-list", 0 ,2);
for(int i=0; i<list.size(); i++) {
System.out.println("列表项为: "+list.get(i));
}
}
}
4、Redis Java Keys 实例
import java.util.Iterator;
import java.util.Set;
import redis.clients.jedis.Jedis;
public class RedisKeyJava {
public static void main(String[] args) {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("localhost");
System.out.println("连接成功");
// 获取数据并输出
Set<String> keys = jedis.keys("*");
Iterator<String> it=keys.iterator() ;
while(it.hasNext()){
String key = it.next();
System.out.println(key);
}
}
}
第四章 老汤真实案例
浦口、建邺装备柜redis实战
装备柜首页使用ecahrts展示装备流转数据,其数据属于更新不频繁的数据,因此可以使用redis进行缓存。下面代码部分只展示其中一个图表的数据存到redis。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4WLCdpFO-1640245210954)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210809163206463.png)]
private static final Integer BASE_COUNT_EXPIRATION = 5;
@Override
public Map<String, List<Object>> getEquipmentTransStatistic(SysUser user) {
String key = EQUIP_TRANS_COUNT_INFO + user.getDeptId();
LOGGER.info("装备流转信息的key:{}", key);
Map<String, List<Object>> statistics;
if (redisCache.hasKey(key)) {
LOGGER.info("从Redis取出装备流转统计信息");
Map<String, List<Object>> objects = redisCache.getCacheMap(key);
statistics = new HashMap<>();
statistics.put("date", objects.get("date"));
statistics.put("take", objects.get("take"));
statistics.put("put", objects.get("put"));
} else {
statistics = getEquipmentTransStatisticFromDB(user);
LOGGER.info("将装备流转统计信息存入Redis");
redisCache.setCacheMap(key, statistics);
redisCache.expire(key, BASE_COUNT_EXPIRATION, TimeUnit.HOURS);
}
return statistics;
}
查询数据先从redis中查询,如果redis里面有数据,就从redis里面查询,如果redis里面没有数据,就从数据库中查询数据,在查询数据后并把数据存到redis中,在redis中的有效期为5小时。5小时后,会清除掉redis中的数据。
不过正是因为有五小时的有效期,当redis中有数据的时候就从redis里面查,而不从数据库中查询。就算立即更改数据库中的信息,查询出的值也不会立即改变。
第五章 redis常见面试题解析
1.说一说你在项目中的redis应用场景
- 五大value类型, hash string set zset list
- 基本上就是缓存
- 为的是服务无状态
- 延申思考:看你的项目有哪些数据结构和对象,在单机里需要单机锁,在多机里需要分布式锁,抽出来放进redis中
- 无锁化
2.redis是单线程还是多线程
- 无论是什么版本,工作线程只有一个
- 6.x高版本出现了Io多线程
- 真正理解内核,从内核把数据搬运到程序里,把搬回来的数据做计算是第二步,netty
Redis之Java开发
第一章 缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E6Np6rfg-1640245210955)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210831112728464.png)]
数据结构
Hash
hash是一个String类型的field和value的映射表
使用场景:存储部分变更数据,比如用户信息、热门商品
List
redis里面是一个双向链表,和java中的linkedList比较像,但不是arraylist。
Set
set是一个集合,集合的概念就是一堆不重复的组合。
使用场景:实现如同共同关注、共同喜好、二度好友
java中,可以使用intersect取交集,可以使用union取并集(qq共同好友,qq推荐可能认识好友)
Zset
使用场景:需要返回一些排序上的
reverseRange
count
Geo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nr7Jy9Bf-1640245210956)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210831194530489.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-20CPhS79-1640245210958)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210906094147050.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67h4IYlC-1640245210959)(C:\Users\ASVS\AppData\Roaming\Typora\typora-user-images\image-20210906100843649.png)]