008ConsurrentHashMap的实际使用-对直播并发技术的思考

在这里插入图片描述

1.使用场景引入

      现在直播非常之火,2020直播带货更是成为了风口,抖音,快手,淘宝等等,每个人都可以直播,当进入直播间的时候,需要查询主播的信息,这个时候直接访问数据库是不可行的,肯定会对数据进行缓存.

2.缓存中存在的问题

      使用缓存之后,能解决大部分的并发问题,但是也会存在一些问题,比如发生缓存失效问题,又或者说某个人的数据过期时间到了,所以缓存中的数据被删除了,然后这个时候访问又突增,直接访问数据库就会导致数据库崩溃,从而影响到整个系统,发生雪崩.

3.对于查询数据库的代码加锁

      在查询缓存后找不到数据,会去数据中查,这个时候为了保证数据库的安全,我们可能会对其加锁.这里简单的写了一个伪代码.

public String selectById(String userId) {
		//1.先去缓存中查询数据
		System.out.println("缓存Miss...");
		//2.缓存中没有去数据库中查询
		synchronized (this) {
			System.out.println("在mysql中查询数据...");
    		System.out.println("将数据添加到缓存中...");
            return "查询的数据";
		}
	}
4.简单加锁存在的问题.

      其实大家都知道加锁不好,会影响性能,但是其实也是相对来说的,还是拿直播行业来说,比如我们进入了一个大咖的直播间,人很多,那这个时候出现卡顿,观众会潜意识的认为是可以接受的,毕竟人多嘛,但是如果直播间人少,几个人还出现卡顿,那么观众就会认为是你这个系统垃圾,在上面代码中,任何人去请求任何一个主播的数据都会用到同一把锁,无疑会大大降低用户体验,那可以怎么进行改进呢?

5.降低锁的细粒度,对同一个主播进行加锁.

      对同一个主播进行加锁,不同的主播用到不同的锁,就可以提升性能,提高用户体验,那要怎么实现呢?
      这样就来到了今天的主角–ConcurrentHashMap,我们可以用该集合去实现降低锁的细粒度.

    ConcurrentHashMap<String,Lock> map = new ConcurrentHashMap<String,Lock>();
	public String selectById(String userId) {
		//1.先去缓存中查询数据
		System.out.println("缓存Miss...");
		//2.缓存中没有去数据库中查询
		ReentrantLock lock = new ReentrantLock();
		if(!map.contains(userId)) {
			map.put(userId, lock);
		}else{
			lock= map.get(userId);
        }
		try {
			lock.lock();
			System.out.println("在mysql中查询数据...");
			System.out.println("将数据添加到缓存中...");
		}catch(Exception e) {
			e.printStackTracle();
		}finally {
			lock.unlock();
		}
		return "查询的数据";			
	}
6.继续优化

      对同一个主播加锁之后,比如现在对于某个主播有一百个去查询数据库得请求,有一个获取锁对象,去查询数据库数据,然后将数据放在缓存中,其他99个请求在阻塞等待,那么其他99个还需要去查询数据库嘛? 不需要,第一个获取锁对象得已经将数据放在了缓存中,所以我们可以使用DCL(Double check lock)双重判断,继续去优化.

    ConcurrentHashMap<String,Lock> map = new ConcurrentHashMap<String,Lock>();
	public String selectById(String userId) {
		//1.先去缓存中查询数据
		System.out.println("缓存Miss...");
		//2.缓存中没有去数据库中查询
		ReentrantLock lock = new ReentrantLock();
		if(!map.contains(userId)) {
			map.put(userId, lock);
		}else{
			lock= map.get(userId);
        }
		try {
			lock.lock();
			System.out.println("在缓存中查询数据,如果查到,直接返回...");
			System.out.println("在mysql中查询数据...");
			System.out.println("将数据添加到缓存中...");
		}catch(Exception e) {
			e.printStackTracle();
		}finally {
			lock.unlock();
		}
		return "查询的数据";			
	}
7.补充.

      看到网上说如果很多请求同时落到数据库上,就会导致数据库宕机,其实这个说法是不对得,因为我们必然会用数据库连接池,而连接池得作用就是限制并发得数量,所以连接池本身就可以限制高并发,那么为什么我们还要在代码中加锁去限制访问请求呢?
      答案就是资源隔离,假如数据库连接池有200个连接对象,而这两百个连接对象是针对于所有得接口得,我们得系统不可能只有一个查询接口,假如某个接口请求增加,一下子来了300个,那连接池得所有连接对象都被同一个接口占用,那其他查询或者更新接口怎么办呢?就会导致请求得不到响应,造成整个系统问题.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员bling

义父,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值