In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces "how to use Java cache technology". In daily operation, I believe many people have doubts about how to use Java cache technology. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the doubts about "how to use Java cache technology". Next, please follow the editor to study!
Cache and its elimination algorithms
Why do we need caching?
A long time ago, when there was no cache. Users often request an object, which is fetched from the database, and then the object becomes larger and larger, and the user's request time is getting longer and longer, which makes the database very painful. He works all the time. So, this thing makes users and databases very angry, and then two things may happen:
1. Users are bored, complaining, or even not using the app (which happens in most cases).
two。 The database leaves the app in order to pack up and go home, and then there is trouble (there is no place to store data) (in rare cases)
God sent the cache.
A few years later, researchers at IBM (1960s) introduced a new concept called caching.
What is caching?
As mentioned at the beginning, caching is "a temporary place to store data (frequently used data), because the cost of fetching the original data is too high, so I can get it faster."
The cache can be thought of as a pool of data that is copied from the real data in the database and labeled (key ID) in order to retrieve it correctly. great
Programmer one already knows this, but he doesn't know the following caching terminology.
Hit:
When a customer initiates a request (we say he wants to view a product information), our application accepts the request, and if it is the first time to check the cache, we need to read the product information in the database.
If an entry is found through a tag in the cache, the entry is used and we call it a cache hit. Therefore, the hit rate is not difficult to understand.
Cache Miss:
But there are two points to note here:
1. If there is still room for the cache, then the missed objects will be stored in the cache.
2. If the cache is full and does not hit the cache, then according to a certain strategy, the old objects in the cache will be kicked out and the new objects will be added to the cache pool. These strategies are collectively referred to as alternative strategies (caching algorithms), which determine which objects should be proposed.
Storage cost:
When there is no hit, we take the data from the database and put it in the cache. The time and space needed to put this data into the cache is the storage cost.
Index cost:
The cost is similar to that of storage.
Invalidation:
When the data in the cache needs to be updated, it means that the data in the cache is invalid.
Alternative strategy:
When the cache misses, and the cache capacity is full, you need to kick out an old entry in the cache and add a new entry, and which entry should be kicked out is determined by the alternative strategy.
Optimal substitution strategy:
The best alternative strategy is to kick out the most useless entries in the cache, but the future cannot be predicted, so this strategy is impossible. But there are a lot of strategies to work towards this at present.
Cache algorithm
No one can tell which caching algorithm is better than other caching algorithms
Least Frequently Used (LFU):
Hello everyone, I am LFU, I will calculate how often they are used for each cached object. I will kick out the least commonly used cache objects.
Least Recently User (LRU):
I am the LRU caching algorithm, and I kick out the least recently used cached objects.
I always need to know when and which cache object is used. It's very difficult for anyone to understand why I always get rid of the least recently used objects.
The browser uses me (LRU) as the caching algorithm. The new object will be placed at the top of the cache, and when the cache reaches its capacity limit, I will kick out the object at the bottom, and the trick is: I will put the newly accessed cache object at the top of the cache pool.
Therefore, cache objects that are often read stay in the cache pool all the time. There are two ways to implement me, array or linked list.
My speed is very fast, and I can be adapted to the data access pattern. I have a big family who can improve me and even do better than me (I do get jealous sometimes, but it doesn't matter). Some members of my family include LRU2 and 2Q, who exist in order to improve LRU.
First in First out (FIFO):
I am a first-in-first-out, I am a low-load algorithm, and the management of cache objects is not demanding. I track all cache objects through a queue, with the most commonly used cache objects at the back and the earlier cache objects at the front, and when the cache capacity is full, the top cache objects are kicked out and the new cache objects are added. I'm fast, but I'm not applicable.
Second Chance:
Hello, everyone, I am second chance, I am modified through FIFO, is called second chance cache algorithm, I am better than FIFO is that I improve the cost of FIFO. I am also watching the front end of the queue as FIFO, but it is different when FIFO kicks out immediately. I will check whether the object that is about to be kicked out has a flag that has been used before (1 a bit). If it has not been used, I will kick him out; otherwise, I will clear the flag bit and add the cache object to the queue as a new cache object. As you can imagine, it's like a ring queue. When I met this object at the head of the team again, because he no longer had this mark, I immediately kicked him away. I am faster than FIFO in speed.
Other caching algorithms take into account the following points:
Cost: if cached objects have different costs, those objects that are difficult to obtain should be saved.
Capacity: if the cache objects are of different sizes, you should clear the large cache objects so that more small cache objects can come in.
Time: some caches also hold the expiration time of the cache. The computer will fail them because they are out of date.
Depending on the size of the cache object, regardless of other caching algorithms, it may be necessary.
Look at the cache elements (cache entities)
Public class CacheElement {private Object objectValue; private Object objectKey; private int index; private int hitCount; / / getters and setters}
This caching entity has cached key and value, and its data structure is used by all of the following caching algorithms.
Common code for caching algorithms
Public final synchronized void addElement (Object key, Object value) {int index; Object obj; / / get the entry from the table obj = table.get (key); / / If we have the entry already in our table / / then get it and replace only its value. Obj = table.get (key); if (obj! = null) {CacheElement element; element = (CacheElement) obj; element.setObjectValue (value); element.setObjectKey (key); return;}}
The above code will be used by all caching algorithm implementations. This code is used to check if the cache element is in the cache, and if so, we replace it, but what if we can't find the cache for this key? Then let's take a closer look at what will happen.
On-site visit
Today's topic is very special because we have special guests, in fact, they are the attendees we want to hear, but first, let's introduce our guest: Random Cache,FIFO Cache. Let's start with Random Cache.
Take a look at the implementation of random caching
Public final synchronized void addElement (Object key, Object value) {int index; Object obj; obj = table.get (key); if (obj! = null) {CacheElement element;// Just replace the value. Element = (CacheElement) obj; element.setObjectValue (value); element.setObjectKey (key); return;} / / If we haven't filled the cache yet, put it at the end. If (! isFull ()) {index = numEntries; + + numEntries;} else {/ / Otherwise, replace a random entry. Index = (int) (cache.length * random.nextFloat ()); table.remove (cache [index]. GetObjectKey ());} Cache [index] .setObjectValue (value); Cache [index] .setObjectKey (key); table.put (key, cache [index]);}
Take a look at the implementation of the FIFO buffer algorithm
Public final synchronized void addElement (Objectkey, Object value) {int index; Object obj; obj = table.get (key); if (obj! = null) {CacheElement element; / / Just replace the value. Element = (CacheElement) obj; element.setObjectValue (value); element.setObjectKey (key); return;} / / If we haven't filled the cache yet, put it at the end. If (! isFull ()) {index = numEntries; + + numEntries;} else {/ / Otherwise, replace the current pointer, / / entry with the new one. Index = current; / / in order to make Circular FIFO if (+ + current > = cache.length) current = 0; table.remove (key [index]. GetObjectKey ());} Cache [index] .setObjectValue (value); Cache [index] .setObjectKey (key); table.put (key, cache [index]);}
Take a look at the implementation of the LFU cache algorithm
Public synchronized Object getElement (Object key) {Object obj; obj = table.get (key); if (obj! = null) {CacheElement element = (CacheElement) obj; element.setHitCount (element.getHitCount () + 1); return element.getObjectValue ();} return null;} public final synchronized void addElement (Object key, Object value) {Object obj; obj = table.get (key) If (obj! = null) {CacheElement element; / / Just replace the value. Element = (CacheElement) obj; element.setObjectValue (value); element.setObjectKey (key); return;} if (! isFull ()) {index = numEntries; + + numEntries;} else {CacheElement element = removeLfuElement (); index = element.getIndex (); table.remove (element.getObjectKey ());} cache.setObjectValue (value) Cache [index] .setObjectKey (key); table.put [index] .setIndex (index); table.put (key, cache [index]);} public CacheElement removeLfuElement () {CacheElement [] elements = getElementsFromTable (); CacheElement leastElement = leastHit (elements); return leastElement;} public static CacheElement leastHit (CacheElement [] elements) {CacheElement lowestElement = null; for (int I = 0; I)
< elements.length; i++) { CacheElement element = elements[i]; if (lowestElement == null) { lowestElement = element; } else { if (element.getHitCount() < lowestElement.getHitCount()) { lowestElement = element; } } } return lowestElement; } 最重点的代码,就应该是 leastHit 这个方法,这段代码就是把hitCount 最低的元素找出来,然后删除,给新进的缓存元素留位置 看看LRU缓存算法实现 private void moveToFront(int index) { int nextIndex, prevIndex; if(head != index) { nextIndex = next[index]; prevIndex = prev[index]; // Only the head has a prev entry that is an invalid index // so we don't check. next[prevIndex] = nextIndex; // Make sure index is valid. If it isn't, we're at the tail // and don't set prev[next]. if(nextIndex >= 0) prev [nextIndex] = prevIndex; else tail = prevIndex; prev [index] =-1; next [index] = head; prev [head] = index; head = index;}} public final synchronized void addElement (Object key, Object value) {int index;Object obj; obj = table.get (key); if (obj! = null) {CacheElement entry / / Just replace the value, but move it to the front. Entry = (CacheElement) obj; entry.setObjectValue (value); entry.setObjectKey (key); moveToFront (entry.getIndex ()); return;} / / If we haven't filled the cache yet, place in next available / / spot and move to front. If (! isFull ()) {if (_ numEntries > 0) {prev [_ numEntries] = tail; next [_ numEntries] =-1; moveToFront (numEntries);} + + numEntries;} else {/ / We replace the tail of the list. Table.remove (cache [tail]. GetObjectKey ()); moveToFront (tail);} cache.setObjectValue (value); cache.setObjectKey (key); table.put (key, cache [head]);}
The logic of this code is like the description of the LRU algorithm, extracting the cache that is used again to the front, and deleting the last element each time.
Discussion on caching Technology
In general, the overall flow of Internet applications (websites or App) can be summarized as shown in figure 1, where users request from the interface (browser or App interface) to network forwarding, application services to storage (database or file system), and then return to the interface to present content.
With the popularity of the Internet, the content information is becoming more and more complex, the number of users and visits are increasing, our applications need to support more concurrency, at the same time, our application servers and database servers do more and more calculations. But often our application server resources are limited, and technological change is slow, and the number of requests per second that the database can accept is also limited (or the reading and writing of files is also limited). How can we effectively use limited resources to provide as much throughput as possible? An effective way is to introduce the cache to break the standard process, and the request in each link can directly obtain the target data from the cache and return it, so as to reduce the amount of calculation, effectively improve the response speed, and make the limited resources serve more users.
Cache feature
Caching is also a data model object, so it must have some characteristics:
hit rate
Hit rate = number of correct results returned / number of requests for cache. Hit rate is a very important issue in cache, and it is an important indicator to measure the effectiveness of cache. The higher the hit rate, the higher the cache usage.
Maximum element (or maximum space)
The maximum number of elements that can be stored in the cache, once the number of elements in the cache exceeds this value (or the space occupied by the cache data exceeds its maximum supporting space), then it will trigger the cache emptying strategy to set the maximum element value reasonably according to different scenarios can improve the cache hit ratio to a certain extent, so as to cache more effectively.
Emptying strategy
As described above, the storage space of the cache is limited. When the cache space is full, how to effectively improve the hit rate while stabilizing the service? This is handled by the cache emptying strategy, and the design of an emptying strategy suitable for its own data characteristics can effectively improve the hit rate. Common general strategies are:
FIFO (first in first out)
In the first-in-first-out strategy, the data that first enters the cache will be cleared first when there is not enough cache space (exceeding the maximum element limit) to make room for new data. The policy algorithm mainly compares the creation time of cache elements. This kind of strategy can be selected in scenarios where data effectiveness is required, and priority is given to ensuring the availability of the latest data.
LFU (less frequently used)
At least use the policy, regardless of whether it is out of date or not, according to the number of times the element is used, clear the less frequently used element to release space. The policy algorithm mainly compares the hitCount (number of hits) of elements. In the scenario of ensuring the validity of high-frequency data, this kind of strategy can be selected.
LRU (least recently used)
Using the least recent policy, regardless of whether it is out of date or not, clears the element that uses the timestamp farthest to free space based on the timestamp that was last used by the element. The policy algorithm mainly compares the last time the element was used by get. It is more suitable in hot data scenarios, and priority is given to ensuring the validity of hot data.
In addition, there are some simple strategies such as:
Based on the expiration time, clean up the elements with the longest expiration time
Clean up the most recent elements that are about to expire based on the expiration time
Random cleaning
Clean up according to the length of keywords (or element contents), etc.
Cache media
Although from the perspective of hardware media, it is nothing more than memory and hard disk, but technically, it can be divided into memory, hard disk files and database.
Memory: storing the cache in memory is the fastest option, without additional Imax O overhead, but the disadvantage of memory is that there is no persistent landing physical disk, and once an exception break down is applied and restarted, the data is difficult or impossible to recover.
Hard disk: generally speaking, many cache frameworks use a combination of memory and hard disk. When the memory allocation space is full or abnormal, the memory space data can be passively or actively persisted to the hard disk to achieve the purpose of freeing space or backing up data.
Database: as mentioned earlier, one of the purposes of the strategy to increase caching is to reduce the Imax O pressure on the database. Is it back to the old problem of using database as cache medium now? In fact, there are many types of databases, such as those special databases that do not support SQL but simply key-value storage structure (such as BerkeleyDB and Redis), the response speed and throughput are much higher than our commonly used relational databases and so on.
Cache classification and application scenarios
Cache has all kinds of characteristics, and there are differences between different media, so how do we classify cache in practical engineering? In the current application service framework, according to the coupling degree of the cache rain application, it is divided into local cache (local cache) and remote cache (distributed cache):
Local cache: refers to the cache component in the application. Its biggest advantage is that the application and cache are in the same process, the request cache is very fast, and there is no excessive network overhead. It is more appropriate to use local cache in scenarios where a single application does not need cluster support or nodes do not need to notify each other. At the same time, its disadvantage is that the cache should be coupled with the application, multiple applications can not share the cache directly, and each node of each application or cluster needs to maintain its own separate cache, which is a waste of memory.
Distributed cache: refers to the cache components or services that are separated from the application. Its biggest advantage is that it is an independent application, isolated from the local application, and multiple applications can directly share the cache.
At present, various types of caching are active in thousands of application services, and there is not a cache scheme that can solve all business scenarios or data types. We need to choose the most suitable cache scheme according to our own special scenarios and backgrounds. The use of cache is a necessary skill for programmers and architects. Good programmers can accurately judge what type of cache to use and how to use it according to data types and business scenarios, so as to achieve the best goal with the lowest cost and the fastest efficiency.
Local cache programming directly implements caching
In individual scenarios, we only need a simple function of caching data, without paying attention to more access, emptying policies and other in-depth features, direct programming to achieve caching is the most convenient and efficient.
a. Member variable or local variable implementation
Examples of simple code are as follows:
Public void UseLocalCache () {/ / A local cache variable Map localCacheStoreMap = new HashMap (); List infosList = this.getInfoList (); for (Object item:infosList) {if (localCacheStoreMap.containsKey (item)) {/ / cache hits use cache data / / todo} else {/ / cache misses IO to get data, and the result is stored in cache Object valueObject = this.getInfoFromDB () LocalCacheStoreMap.put (valueObject.toString (), valueObject);}} / / sample private List getInfoList () {return new ArrayList ();} / / sample database IO gets private Object getInfoFromDB () {return new Object ();}
The local variable map structure is used to cache part of the business data to reduce the frequent repetition of database I / O operations. The disadvantage is only within the scope of the class itself, and the cache cannot be shared between classes.
b. Static variable implementation
The most commonly used singleton is to implement static resource caching. The code example is as follows:
Public class CityUtils {private static final HttpClient httpClient = ServerHolder.createClientWithPool (); private static Map cityIdNameMap = new HashMap (); private static Map districtIdNameMap = new HashMap (); static {HttpGet get = new HttpGet ("http://gis-in.sankuai.com/api/location/city/all"); BaseAuthorizationUtils.generateAuthAndDateHeader (get, BaseAuthorizationUtils.CLIENT_TO_REQUEST_MDC, BaseAuthorizationUtils.SECRET_TO_REQUEST_MDC)) Try {String resultStr = httpClient.execute (get, new BasicResponseHandler ()); JSONObject resultJo = new JSONObject (resultStr); JSONArray dataJa = resultJo.getJSONArray ("data"); for (int I = 0; I
< dataJa.length(); i++) { JSONObject itemJo = dataJa.getJSONObject(i); cityIdNameMap.put(itemJo.getInt("id"), itemJo.getString("name")); } } catch (Exception e) { throw new RuntimeException("Init City List Error!", e); }} static { HttpGet get = new HttpGet("http://gis-in.sankuai.com/api/location/district/all"); BaseAuthorizationUtils.generateAuthAndDateHeader(get, BaseAuthorizationUtils.CLIENT_TO_REQUEST_MDC, BaseAuthorizationUtils.SECRET_TO_REQUEST_MDC); try { String resultStr = httpClient.execute(get, new BasicResponseHandler()); JSONObject resultJo = new JSONObject(resultStr); JSONArray dataJa = resultJo.getJSONArray("data"); for (int i = 0; i < dataJa.length(); i++) { JSONObject itemJo = dataJa.getJSONObject(i); districtIdNameMap.put(itemJo.getInt("id"), itemJo.getString("name")); } } catch (Exception e) { throw new RuntimeException("Init District List Error!", e); }} public static String getCityName(int cityId) { String name = cityIdNameMap.get(cityId); if (name == null) { name = "未知"; } return name; } public static String getDistrictName(int districtId) { String name = districtIdNameMap.get(districtId); if (name == null) { name = "未知"; } return name; } } O2O业务中常用的城市基础基本信息判断,通过静态变量一次获取缓存内存中,减少频繁的I/O读取,静态变量实现类间可共享,进程内可共享,缓存的实时性稍差。 这类缓存实现,优点是能直接在heap区内读写,最快也最方便;缺点同样是受heap区域影响,缓存的数据量非常有限,同时缓存时间受GC影响。主要满足单机场景下的小数据量缓存需求,同时对缓存数据的变更无需太敏感感知,如上一般配置管理、基础静态数据等场景。 Ehcache Ehcache是现在最流行的纯Java开源缓存框架,配置简单、结构清晰、功能强大,是一个非常轻量级的缓存实现,我们常用的Hibernate里面就集成了相关缓存功能。 主要特性: 快速,针对大型高并发系统场景,Ehcache的多线程机制有相应的优化改善。 简单,很小的jar包,简单配置就可直接使用,单机场景下无需过多的其他服务依赖。 支持多种的缓存策略,灵活。 缓存数据有两级:内存和磁盘,与一般的本地内存缓存相比,有了磁盘的存储空间,将可以支持更大量的数据缓存需求。 具有缓存和缓存管理器的侦听接口,能更简单方便的进行缓存实例的监控管理。 支持多缓存管理器实例,以及一个实例的多个缓存区域。 注意:Ehcache的超时设置主要是针对整个cache实例设置整体的超时策略,而没有较好的处理针对单独的key的个性的超时设置(有策略设置,但是比较复杂,就不描述了),因此,在使用中要注意过期失效的缓存元素无法被GC回收,时间越长缓存越多,内存占用也就越大,内存泄露的概率也越大。 分布式缓存memcached缓存 memcached是应用较广的开源分布式缓存产品之一,它本身其实不提供分布式解决方案。在服务端,memcached集群环境实际就是一个个memcached服务器的堆积,环境搭建较为简单;cache的分布式主要是在客户端实现,通过客户端的路由处理来达到分布式解决方案的目的。客户端做路由的原理非常简单,应用服务器在每次存取某key的value时,通过某种算法把key映射到某台memcached服务器nodeA上。 无特殊场景下,key-value能满足需求的前提下,使用memcached分布式集群是较好的选择,搭建与操作使用都比较简单;分布式集群在单点故障时,只影响小部分数据异常,目前还可以通过Magent缓存代理模式,做单点备份,提升高可用;整个缓存都是基于内存的,因此响应时间是很快,不需要额外的序列化、反序列化的程序,但同时由于基于内存,数据没有持久化,集群故障重启数据无法恢复。高版本的memcached已经支持CAS模式的原子操作,可以低成本的解决并发控制问题。 Redis缓存 Redis是一个远程内存数据库(非关系型数据库),性能强劲,具有复制特性以及解决问题而生的独一无二的数据模型。它可以存储键值对与5种不同类型的值之间的映射,可以将存储在内存的键值对数据持久化到硬盘,可以使用复制特性来扩展读性能,还可以使用客户端分片来扩展写性能。 个人总结了以下多种Web应用场景,在这些场景下可以充分的利用Redis的特性,大大提高效率。 在主页中显示最新的项目列表:Redis使用的是常驻内存的缓存,速度非常快。LPUSH用来插入一个内容ID,作为关键字存储在列表头部。LTRIM用来限制列表中的项目数最多为5000。如果用户需要的检索的数据量超越这个缓存容量,这时才需要把请求发送到数据库。 删除和过滤:如果一篇文章被删除,可以使用LREM从缓存中彻底清除掉。 排行榜及相关问题:排行榜(leader board)按照得分进行排序。ZADD命令可以直接实现这个功能,而ZREVRANGE命令可以用来按照得分来获取前100名的用户,ZRANK可以用来获取用户排名,非常直接而且操作容易。 按照用户投票和时间排序:排行榜,得分会随着时间变化。LPUSH和LTRIM命令结合运用,把文章添加到一个列表中。一项后台任务用来获取列表,并重新计算列表的排序,ZADD命令用来按照新的顺序填充生成列表。列表可以实现非常快速的检索,即使是负载很重的站点。 过期项目处理:使用Unix时间作为关键字,用来保持列表能够按时间排序。对current_time和time_to_live进行检索,完成查找过期项目的艰巨任务。另一项后台任务使用ZRANGE…WITHSCORES进行查询,删除过期的条目。 计数:进行各种数据统计的用途是非常广泛的,比如想知道什么时候封锁一个IP地址。INCRBY命令让这些变得很容易,通过原子递增保持计数;GETSET用来重置计数器;过期属性用来确认一个关键字什么时候应该删除。 特定时间内的特定项目:这是特定访问者的问题,可以通过给每次页面浏览使用SADD命令来解决。SADD不会将已经存在的成员添加到一个集合。 Pub/Sub:在更新中保持用户对数据的映射是系统中的一个普遍任务。Redis的pub/sub功能使用了SUBSCRIBE、UNSUBSCRIBE和PUBLISH命令,让这个变得更加容易。 队列:在当前的编程中队列随处可见。除了push和pop类型的命令之外,Redis还有阻塞队列的命令,能够让一个程序在执行时被另一个程序添加到队列。 缓存常见问题 前面一节说到了《 为什么说Redis是单线程的以及Redis为什么这么快!》,今天给大家整理一篇关于Redis经常被问到的问题:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等概念的入门及简单解决方案。 一、缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。 缓存正常从Redis中获取,示意图如下: 缓存失效瞬间示意图如下: 缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。 以下简单介绍两种实现方式的伪代码: (1)碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队,伪代码如下: //伪代码public object GetProductListNew() { int cacheTime = 30; String cacheKey = "product_list"; String lockKey = cacheKey; String cacheValue = CacheHelper.get(cacheKey); if (cacheValue != null) { return cacheValue; } else { synchronized(lockKey) { cacheValue = CacheHelper.get(cacheKey); if (cacheValue != null) { return cacheValue; } else { //这里一般是sql查询数据 cacheValue = GetProductListFromDB(); CacheHelper.Add(cacheKey, cacheValue, cacheTime); } } return cacheValue; }} 加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法! 注意:加锁排队的解决方式分布式环境的并发问题,有可能还要解决分布式锁的问题;线程还会被阻塞,用户体验很差!因此,在真正的高并发场景下很少使用! (2)还有一个解决办法解决方案是:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存,实例伪代码如下: //伪代码public object GetProductListNew() { int cacheTime = 30; String cacheKey = "product_list"; //缓存标记 String cacheSign = cacheKey + "_sign"; String sign = CacheHelper.Get(cacheSign); //获取缓存值 String cacheValue = CacheHelper.Get(cacheKey); if (sign != null) { return cacheValue; //未过期,直接返回 } else { CacheHelper.Add(cacheSign, "1", cacheTime); ThreadPool.QueueUserWorkItem((arg) ->{/ / here is generally the sql query data cacheValue = GetProductListFromDB (); / / the date is cached twice as long as it is used to dirty read CacheHelper.Add (cacheKey, cacheValue, cacheTime * 2);}); return cacheValue;}}
Explanation:
1. Cache token: record whether the cached data has expired. If it expires, it will trigger another thread to update the actual key cache in the background.
2. Cache data: its expiration time is twice longer than that of the cache mark, for example: the mark cache time is 30 minutes, and the data cache is set to 60 minutes. In this way, when the cache token key expires, the actual cache can also return the old data to the caller, and the new cache will not be returned until another thread has finished updating in the background.
With regard to the solution to cache crash, here are three solutions: using lock or queue, setting expiration flag to update cache, setting different cache expiration time for key, and a solution called "secondary cache", which can be studied by interested readers.
Second, cache penetration
Cache traversal refers to the user querying data, which is not available in the database and naturally does not exist in the cache. As a result, when the user makes a query, he can't find it in the cache, so he has to go to the database and query it again each time, and then return null (equivalent to two useless queries). In this way, the request bypasses the cache and looks up the database directly, which is also a frequently asked issue of cache hit rate.
There are many ways to effectively solve the problem of cache penetration, the most common is to use a Bloom filter to hash all possible data into a large enough bitmap, and a certain non-existent data will be intercepted by this bitmap, thus avoiding the query pressure on the underlying storage system.
There is also a simpler and more crude way, if the data returned by a query is empty (whether the data does not exist or a system failure), we still cache the empty result, but its expiration time will be very short. The maximum is no more than five minutes. Through this directly set default value is stored in the cache, so that the second time to get the value in the buffer, instead of continuing to access the database, this method is the simplest and roughest!
/ / pseudo code public object GetProductListNew () {int cacheTime = 30; String cacheKey = "product_list"; String cacheValue = CacheHelper.Get (cacheKey); if (cacheValue! = null) {return cacheValue;} cacheValue = CacheHelper.Get (cacheKey); if (cacheValue! = null) {return cacheValue;} else {/ / database query not found, empty cacheValue = GetProductListFromDB () If (cacheValue = = null) {/ / if it is found to be empty, set a default value and cache cacheValue = string.Empty;} CacheHelper.Add (cacheKey, cacheValue, cacheTime); return cacheValue;}}
The empty result is also cached so that the next time the same request can directly return empty, that is, you can avoid cache penetration when the value of the query is empty. At the same time, you can also set a separate cache area to store null values, pre-check the key to be queried, and then release it to the later normal cache processing logic.
Third, cache warm-up
Cache preheating should be a common concept. I believe many partners can easily understand it. Cache preheating means loading relevant cache data directly into the cache system after the system is online. In this way, you can avoid the problem of querying the database and then caching the data when the user requests it. Users directly query pre-warmed cache data!
The solution is as follows:
1. Write a cache to refresh the page directly, and do it manually when you launch.
2. The amount of data is small and can be loaded automatically when the project starts.
3. Refresh the cache regularly
IV. Cache updates
In addition to the cache invalidation policy provided by the cache server (Redis has 6 policies to choose from by default), we can also customize cache elimination according to specific business needs. There are two common strategies:
(1) regularly clean up expired caches
(2) when a user requests, determine whether the cache used in the request has expired. If it expires, go to the underlying system to get new data and update the cache.
Both have their own advantages and disadvantages. The first disadvantage is that it is troublesome to maintain a large number of cached key, and the second disadvantage is to judge the cache invalidation every time the user requests it. The logic is relatively complex! You can weigh which scheme to use according to your own application scenario.
Fifth, cache degradation
When there is a sharp increase in traffic, when there is a problem with the service (such as slow response time or non-response), or when non-core services affect the performance of the core process, it is still necessary to ensure that the service is still available, even if it is damaging. The system can be degraded automatically according to some key data, or the switch can be configured to achieve manual degradation.
The ultimate goal of the downgrade is to ensure that core services are available, even if they are damaging. And some services cannot be downgraded (such as adding shopping carts, clearing).
Before downgrading, the system should be combed to see if the system can lose pawn protection, so as to sort out which must be vowed to protect and which can be degraded. For example, you can refer to the log level to set a plan:
(1) General: for example, some services can be degraded automatically if they time out occasionally because of network jitter or when the service is online.
(2) warning: if the success rate of some services fluctuates over a period of time (for example, between 95% and 100%), they can be downgraded automatically or manually, and send an alarm.
(3) error: for example, if the availability rate is less than 90%, or the database connection pool is burst, or the number of visits suddenly soars to the maximum threshold that the system can bear, it can be downgraded automatically or manually according to the situation.
(4) serious errors: for example, if the data is wrong for special reasons, an emergency manual downgrade is required.
At this point, the study on "how to use Java caching technology" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.