`

关于缓存系统的mutex模式实现

 
阅读更多

这篇blog是在上一家互联网公司某产品开发过程中的一些经验总结,整理一下分享上来。

 

关于mutex模式:

对于高并发大访问量的应用,一般都会在数据库访问前加一层缓存系统。

但是如果某一时刻某个缓存的key失效,而reload该key缓存的时间又比较长,导致大量的请求直接访问数据库,则会直接将数据库击垮。

 

解决方案:

当在缓存中获取某个key为null时,add一个mutex key,并进行数据的reload工作,数据reload后删除该mutex key。

如果另外一个线程访问该key也为null,则也会add一个mutex key,但发现add不进去(已经包含了该mutex key)。这时设置这个线程sleep一段时间后,再重试(可以设置重试次数)。如果全部重试完还是没有reload完数据则响应一个异常状态或响应304给终端。

 

程序部分伪码实现(缓存系统用的是Memcached):

//根据业务生成一个Cache Key.
		String categoryListMemcachedKey = genCategoryListCacheKey(parentId);
		MemCache memcached=MemCache.getInstance();
		//该业务Key不存在或失效
		if(memcached.get(categoryListMemcachedKey)==null){
			//设置该业务键的mutex key.
			String mutexKey=categoryListMemcachedKey+"|mutex";
			if(memcached.add(mutexKey,1*60*1000)==true){//reload in 1min
				//categoryListStr代表数据库查询后构建的结果
				categoryListStr=dbResult;
				if("".equals(categoryListStr)){//set ""相当于没set,高并发请求仍旧会击跨数据库.
					memcached.set(categoryListMemcachedKey,"nodata",new Date(1000*60*1));//防止假数据一直存在缓存中,设置过期时间1min.
				}else{
					memcached.set(categoryListMemcachedKey,categoryListStr);
				}
				//删除mutex key
				memcached.delete(mutexKey);
			}else{//某线程正在load数据
				try {
					Thread.sleep(50);//当前线程睡眠50ms
					categoryListStr=(String)retry(memcached,categoryListMemcachedKey,3);//重试3次
					if(categoryListStr==null){categoryListStr = "nodata";}
				} catch (Throwable e) {
					LogFactory.getLog(MemcachedCacheManager.class).error("Thread sleep error:",e);
				}
			}
		}else{//缓存中已存在该业务key
			System.out.println("There has "+categoryListMemcachedKey+" in memcached!");
			categoryListStr=(String)memcached.get(categoryListMemcachedKey);
		}

上面代码中的重试方法(递归实现):

/** 重试  */
	public static Object retry(MemCache memcached,String key,int tryNum){
		System.out.println("In retry:"+key+" tryNum:"+tryNum+"!");
		if(memcached.get(key)==null){//重试还是取不到数据
			System.out.println("Retry no data!");
			if(tryNum==0){//已达到重试上限
				System.out.println("There is no tryNum "+key+"!");
				return null;//返回null
			}else{
				try {
					Thread.sleep(50);//睡眠50ms
				} catch (Throwable e) {
					e.printStackTrace();
				}
				return retry(memcached,key,--tryNum);//递归重试
			}
		}else{
			System.out.println("Retry have data!");
			return memcached.get(key);
		}
	}

 

参考资料:

关于mutex设计模式:http://timyang.net/programming/memcache-mutex/  这是新浪某大牛的blog,也是最初看到探讨mutex模式的文章。

 

分享到:
评论

相关推荐

    golang design pattern go 设计模式实现.zip

    "golang design pattern go 设计模式实现.zip" 文件提供了23种Go语言设计模式的实现,这些模式主要来自于经典的《设计模式:可复用面向对象软件的基础》一书,即通常所说的GOF(Gang of Four)设计模式。 1. **单例...

    C#缓存线程模板

    在IT行业中,缓存是一种非常...理解并熟练应用这些知识点,可以帮助开发者构建高效、稳定且可扩展的缓存系统,提高应用程序的整体性能。在C#中,利用.NET Framework提供的并发工具和设计模式,可以轻松实现这些功能。

    构建高效可伸缩的缓存demo

    在构建高效可伸缩的缓存系统中,我们需要考虑的关键因素包括性能、容错性、扩展性和资源管理。本demo提供了实现这些目标的实例代码和测试案例,让我们深入探讨其中涉及的技术点。 首先,缓存的主要目的是提高数据...

    多进程同步-生产者消费者模式-C实现

    这个模式广泛应用于各种系统设计中,如数据库缓存、消息队列等。理解并熟练掌握生产者消费者模式对于提高系统并发性能和正确性至关重要。 通过分析和实践《深入理解计算机系统》中的例子,你可以更深入地理解多进程...

    Oracle数据库latch和mutex等待事件全面解析

    - AWR报告提供了关于系统性能的重要信息,包括等待时间统计。 - 可以通过分析AWR报告来识别Latch和Mutex的竞争情况。 **3.4 使用Oracle提供的工具** - **Oracle提供的工具**: - Oracle提供了多种工具,如SQL ...

    backQueueTest主备缓存队列

    在IT行业中,主备缓存队列是一种常见的设计模式,用于确保服务的高可用性和数据的一致性。这种模式在处理高并发、大数据量的场景下尤为重要,它能够有效地提高系统的稳定性和性能。"backQueueTest主备缓存队列"的...

    设计模式C++学习之单例模式(Singleton)

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。在C++中,实现单例模式有多种方法,我们将会深入...在实际开发中,应根据需求灵活选择和组合设计模式,以实现高效、健壮的系统。

    单例模式-基本代码.rar_C++_Hikvision

    此外,单例模式还可以用于日志管理、线程池、缓存管理等场景,确保在整个应用程序中这些关键资源的统一管理和访问。在Hikvision的测试场景下,单例模式可以帮助开发者更有序地控制门禁系统的各个部分,简化代码结构...

    缓存:LRU,LFU,FIFO缓存C ++实现

    缓存是一种重要的计算机科学概念,用于提升数据访问速度和系统性能。在软件设计中,缓存策略的选择至关重要,因为不同的策略适用于不同的场景。本篇文章将详细介绍三种常见的缓存替换策略:LRU(Least Recently Used...

    .net类的简单设计教务管理系统

    例如,使用Mutex或Semaphore来管理并发访问,或者使用System.Web.Caching来缓存频繁访问的数据,避免频繁的数据库查询。 最后,系统的部署和安全性也是重要环节。IIS作为.NET应用程序的常用服务器,提供了安全配置...

    行业分类-设备装置-双模式读写锁.zip

    在实际应用中,双模式读写锁广泛应用于数据库系统、缓存管理、文件系统等领域。例如,在数据库中,当多个查询请求同时到来时,读写锁可以允许这些查询并行执行,而当有更新数据的操作时,会阻塞新的读写请求,直到...

    Linux C 实现日志打印功能

    4. **同步与异步**:日志打印可能涉及多线程环境,同步日志确保在同一时刻只有一个线程写入,可以使用互斥锁`mutex`来实现。而异步日志则将日志放入队列,由单独的线程处理,提高性能但可能会丢失部分日志。 5. **...

    卡拉OK点歌系统(C#)

    以下是关于这个系统的一些关键知识点和实现细节: 1. **用户界面**:用户界面是系统与用户交互的窗口,设计上应简洁易用,提供歌曲搜索、分类浏览、歌曲添加、删除和排序等功能。C#中的Windows Forms或WPF可以用于...

    Go Design Patterns(pdf+epub+mobi+code_files).zip

    Go的包级别变量可以用来实现简单的单例,但考虑到并发安全,通常会使用sync.Once和sync.Mutex来确保线程安全。 2. **工厂模式**:工厂模式提供了一种创建对象的方式,而无需暴露实例化过程。在Go中,可以使用函数...

    计算机操作系统实验代码(6个实验).zip

    你将学习到文件的i节点、块分配、文件的缓存策略等,并可能需要实现一个简单的文件系统模拟器。 实验六:死锁预防和检测 在这一实验中,你将深入探讨死锁问题,学习银行家算法或其他死锁预防策略,以及死锁检测和...

    VC++数据库管理系统

    VC++中的数据库访问组件通常提供缓存机制,减少对数据库的直接访问,提高系统性能。此外,合理的数据库设计和查询优化也能显著提升系统效率。 七、安全性与备份恢复 数据库的安全性是系统设计的重要考量因素。VC++...

    坦克大战 C#可视化实现

    C#的Thread类和Mutex、Semaphore等同步机制可以帮助我们处理并发问题,确保数据的一致性和安全性。 七、资源管理与优化 游戏中的图片、声音等资源需有效管理。C#的ResourceManager类可用于加载和管理资源,减少内存...

    计算机操作系统期末考试题答案.pdf

    根据所提供的文件片段,我们可以从中提取关于计算机操作系统的多个知识点,并且用中文进行详细阐述。 1. 同步机制:文件中提到了互斥锁(mutex)和信号量(semaphore)的概念。互斥锁是一种简单的同步机制,用于...

    操作系统实验:生产者消费者

    在实际的系统设计中,如数据库、网络服务或者内存缓存等,这种模式广泛应用。 在本实验中,使用Microsoft Visual C++(简称VC)作为编程环境,我们需要理解以下几个关键知识点: 1. **线程与并发**:线程是操作...

    ecryptfs内核1

    在Linux内核中,ecryptfs是一个加密文件系统,它提供了透明的...总的来说,ecryptfs内核模块的实现涉及了文件系统挂载、内存管理、同步控制以及用户可配置的加密策略等多个核心领域,展示了Linux内核的强大和灵活性。

Global site tag (gtag.js) - Google Analytics