Friday, January 20, 2017

Spring Cache



https://xicojunior.wordpress.com/2013/08/09/using-redis-hash-with-jedis/
https://my.oschina.net/u/914290/blog/832546
You may set exprire time in CacheManager by using RedisCacheManager.setExpires or RedisCacheManager.setDefaultExpiration
All you (should) need is a "custom" RedisCacheManager extending the SDR CacheManager implementation,

        protected <T> T handleErrors(Exception e) throws Exception {
            if (e instanceof <some RedisConnection Exception type>) {
                // log the connection problem
                return null;
            }
            else if (<something different>) { // act appropriately }
            ...
            else {
                throw e;
            }
        }
http://stackoverflow.com/questions/37281139/spring-cache-logging-on-cacheable-hit
logger name="org.springframework.cache" level="trace"
Cache entry for key 'Page request [number: 0, size 20, sort: null]' found in cache 'persons'

No cache entry for key
Cache entry for key found in cache
http://stackoverflow.com/questions/29801844/how-to-make-cacheable-return-null-when-not-found-in-cache-but-do-not-cache-the

@Cacheable(value="books", key="#isbn", unless = "#result == null")
public Book findBookFromCache(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
{
    return null;
}

  • Looks up in the cache based on key
  • if found return the result from cache
  • if not found evaluates method that return null
  • return value is matched with unless condition (which always return true)
  • null value do not get cached
  • null value is returned to caller
In contrast to the @Cacheable annotation, CachePut does not cause the advised method to be skipped. Rather, it always causes the method to be invoked and its result to be stored in the associated cache. Note that Java8's Optional return types are automatically handled and its content is stored in the cache if present.

keyGenerator Mutually exclusive with the key attribute.
https://dzone.com/articles/spring-31-caching-and
@Cacheable(value = "employee", condition = "#age < 25")
https://www.foreach.be/blog/spring-cache-annotations-some-tips-tricks
 @Cacheable(value = "userCache", unless = "#result != null")
 @Caching(
      put = {
            @CachePut(value = "userCache", key = "'username:' + #result.username", condition = "#result != null"),
            @CachePut(value = "userCache", key = "#result.id", condition = "#result != null")
      }
 )

@EnableCaching(mode = AdviceMode.ASPECTJ)
https://dzone.com/articles/cacheable-overhead-spring-0
In science we don't believe and trust, we measure and benchmark. 
first of all, check out the core implementation in CacheAspectSupport - it's actually quite complex. Secondly, is it really that slow? Do the math - you typically use Spring in business applications where database, network and external APIs are the bottleneck. What latencies do you typically see? Milliseconds? Tens or hundreds of milliseconds? Now add an overhead of 2 μs (worst case scenario). For caching database queries or REST calls this is completely negligible. It doesn't matter which technique you choose.

But if you are caching very low-level methods close to the metal, like CPU-intensive, in-memory computations, Spring abstraction layer might be an overkill. The bottom line: measure!
http://stackoverflow.com/questions/22835023/spring-redistemplate-use-same-key-with-multiple-redistemplate-to-store-differe
Actually Redis is a key/value store and if you use the same key for the same store you just override the old value with new one. And it doesn't matter how much RedisTemplates (or even connectionFactorys) you have, if the real Redis server is the same.
Now how to help you with your task:
You should have different kyes for different domain objects: e.g. studentsaddresses. Since you are going to store domain objects with their own keys it looks like Map value is for you. I mean under key students a map of Students should be stored ,and the same for Addresses.
which may have same ids (starting with 1 for each table). I read some where it can be achieved by using keys like, 'student:{id}', 'address:{id}', etc. I am still searching for the solution
http://stackoverflow.com/questions/33383366/cacheble-annotation-on-no-parameter-method/33384311
The easiest workaround is to provide the name of the method as the key:
@Cacheable(value="usercache", key = "#root.methodName")
public string sayHello(){
return "test"
}
This would set sayHello as the key.
If you really need a static key, you should define a static variable in the class, and use #root.target:
public static final String MY_KEY = "mykey";

@Cacheable(value="usercache", key = "#root.target.MY_KEY")
public string sayHello(){
return "test"
}
You can find here the list of SPEL expressions that you can use in your key.

NameLocationDescriptionExample
methodNameroot objectThe name of the method being invoked#root.methodName
methodroot objectThe method being invoked#root.method.name
targetroot objectThe target object being invoked#root.target
targetClassroot objectThe class of the target being invoked#root.targetClass
argsroot objectThe arguments (as array) used for invoking the target#root.args[0]
cachesroot objectCollection of caches against which the current method is executed#root.caches[0].name
argument nameevaluation contextName of any of the method arguments. If for some reason the names are not available (e.g. no debug information), the argument names are also available under the #a<#arg> where #arg stands for the argument index (starting from 0).#iban or #a0 (one can also use #p0 or #p<#arg> notation as an alias).
resultevaluation contextThe result of the method call (the value to be cached). Only available in unless expressions, cache put expressions (to compute the key), or cache evict expressions (when beforeInvocation is false). For supported wrappers such as Optional#result refers to the actual object, not the wrapper.#result

@Cacheable(value="bookCache", key="isbn + '_' + checkWarehouse + '_' + includeUsed")

java.lang.IllegalArgumentException: Null key returned for cache operation (maybe you are using named params on classes without debug info?)

- Not use right param name in @CacheXXX annotation
- Not param names are available - javac compiler
http://stackoverflow.com/questions/2237803/can-i-obtain-method-parameter-name-using-java-reflection
  • getting parameter names is possible if debug information is included during compilation. See this answer for more details
  • otherwise getting parameter names is not possible
  • getting parameter type is possible, using method.getParameterTypes()
For the sake of writing autocomplete functionality for an editor (as you stated in one of the comments) there are a few options:
  • use arg0arg1arg2 etc.
  • use intParamstringParamobjectTypeParam, etc.
  • use a combination of the above - the former for non-primitive types, and the latter for primitive types.
  • don't show argument names at all - just the types.
http://stackoverflow.com/questions/6759880/getting-the-name-of-a-method-parameter/6759953#6759953
Parameter names are available if you have told the compiler to include them (compile with debug information). Spring has ParameterNameDiscoverer which can help you obtain the names. The default implementation uses asm ClassReader to do so.
With javac you should include the -g argument to include debug information. With eclipse I think it is there by default.
Some frameworks use this. For example spring-mvc has @RequestParam which defaults to the param name, if resolvable. It also supports explicit naming - @RequestParam("foo") in case no debug information is provided.
http://stackoverflow.com/questions/11462662/getting-an-ehcache-instance-with-spring-intelligently
@Value("#{cacheManager.getCache('myCacheName')}")
private Cache myCache;
See also examples how to use Spring EL inside the @Value()http://www.mkyong.com/spring3/spring-el-method-invocation-example/ if you are interested.
<bean id="cache" class="net.sf.ehcache.Cache" factory-bean="cacheManager" factory-method="getCache">
    <constructor-arg value="CacheNameHere" />          
</bean>
And my java class
@Autowired
private net.sf.ehcache.Cache cache;

http://ankushs92.github.io/libraries/2016/09/06/spring-redis-bypass-cache-layer.html
https://gitlab.com/ankushs92/sample-boot-redis-errorHandler/blob/master/src/main/groovy/com/example/config/CacheConfig.java
All you need to do is to extend CachingConfigurerSupport and override errorHandler(). Now, whenever you are performing a GET operation ,like our findById(Integer id) , and the same error occurs,then Spring would bypass the Cache layer and execute the method.

@Cacheable maps to handleCacheGetError(RuntimeException ex, Cache cache, Object key) .
@CacheEvict maps to handleCacheEvictError(RuntimeException ex, Cache cache, Object key) .
@CachePut maps to handleCachePutError(RuntimeException ex, Cache cache, Object arg1, Object arg2)
You can use a similar strategy with other Cache backends that Spring provides.

http://stackoverflow.com/questions/27707857/spring-cache-with-redis-how-to-gracefully-handle-or-even-skip-caching-in-case
As from Spring Framework 4.1, there is a CacheErrorHandler that you can implement to handle such exceptions. Refer to the javadoc for more details.
http://stackoverflow.com/questions/26021991/spring-redis-error-handle
The above code presumes that Redis was completely down, and unpersisted, and therefore would be empty when it becomes available. In the event that Redis was only temporarily unreachable, or if Redis went down but the caches were persisted, updates to the database would still have gone through and the cache entries could be stale. Perhaps it should "remember" cache keys received while Redis was unavailable, and evict them when Redis becomes available

http://stackoverflow.com/questions/32994544/spring-cacheable-not-caching
There is a nasty trick you can use to override this called inject yourself:
public class YourClass {
    @Autowired
    private YourClass instance;

    @Cacheable
    public String method1() {
          // now you go through the cache again
          return instance.method2();
    }

    @Cacheable
    public String method2() {
          return "2";
    }
}
http://stackoverflow.com/questions/16899604/spring-cache-cacheable-not-working-while-calling-from-another-method-of-the-s
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {

    private final AService _aService;

    @Autowired
    public AService(AService aService) {
        _aService = aService;
    }

    @Cacheable("employeeData")
    public List<EmployeeData> getEmployeeData(Date date){
        ..println("Cache is not being used");
        ...
    }

    public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
        List<EmployeeData> employeeData = _aService.getEmployeeData(date);
        ...
    }
}

@Cacheable("settingsCache")
public String findValue(String name) {
    Setting setting = settingRepository.findOne(name);
    if(setting == null){
        return null;
    }
    return setting.getValue();
}

@Override
public Boolean findBoolean(String name) {
    String value = getSpringProxy().findValue(name);
    if (value == null) {
        return null;
    }
    return Boolean.valueOf(value);
}

/**
 * Use proxy to hit cache 
 */
private SettingService getSpringProxy() {
    return applicationContext.getBean(SettingService.class);
}

Another simple and good solution: move @Cacheable annotation to lower (DAO) level. Problem remain at inside DAO class, but solved for Service.

Redis
Atomic counters
Operations that require that the key and the value are given every time an operation is performed. These operations are handy when we have to execute a single operation by using a key and a value.
Operations that are bound to a specific key that is given only once. We should use this approach when we have to perform multiple operations by using the same key.
@Bean
public RedisAtomicLong redisAtomicLong() {
  return new RedisAtomicLong("contact", redisConnectionFactory());
}

https://github.com/akihyro/try-spring-boot-with-redis/blob/master/src/main/java/akihyro/tryspringbootwithredis/RedisConfiguration.java
http://stackoverflow.com/questions/21672245/spring-redistemplate-serialise-multiple-model-classes-into-json-need-to-use-mu
This will add @Class property to the JSON to understand the type, which helps Jackson to deserialize, so no need to explicitly map the model on the configuration class.
"{\"@class\":\"com.prnv.model.WhitePaper\",\"title\":\"Hey\",\"author\":{\"@class\":\"com.prnv.model.Author\",\"name\":\"Hello\"},\"description\":\"Description\"}"
https://blog.pranavek.com/integrating-redis-with-spring-application/
  public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
    redisCacheManager.setTransactionAware(true);
    redisCacheManager.setLoadRemoteCachesOnStartup(true);
    redisCacheManager.setUsePrefix(true);
    return redisCacheManager;
  }

http://m.blog.csdn.net/article/details?id=52251148
如果不进行设置的话,默认使用JdkSerializationRedisSerializer进行数据序列化。
(把任何数据保存到redis中时,都需要进行序列化)

所有的key和value还有hashkey和hashvalue的原始字符前,都加了一串字符。查了一下,这是JdkSerializationRedisSerializer进行序列化时,加上去的。
原以为只会在value或hashvalue上加,没想到在key和hashkey上也加了,这样的话,用原来的key就取不到我们保存的数据了。
StringRedisSerializer:对String数据进行序列化。序列化后,保存到Redis中的数据,不会有像上面的“\xAC\xED\x00\x05t\x00\x09”多余字符。就是"frequency".
Jackson2JsonRedisSerializer:用Jackson2,将对象序列化成Json。这个Serializer功能很强大,但在现实中,是否需要这样使用,要多考虑。一旦这样使用后,要修改对象的一个属性值时,就需要把整个对象都读取出来,再保存回去。
JdkSerializationRedisSerializer:使用Java序列化。结果就像最上面的样子。
GenericToStringSerializer:使用Spring转换服务进行序列化。在网上没有找到什么例子,使用方法和StringRedisSerializer相比,StringRedisSerializer只能直接对String类型的数据进行操作,如果要被序列化的数据不是String类型的,需要转换成String类型,例如:String.valueOf()。但使用GenericToStringSerializer的话,不需要进行转换,直接由String帮我们进行转换。但这样的话,也就定死了序列化前和序列化后的数据类型,例如:template.setValueSerializer(new GenericToStringSerializer<Long>(Long.class));
我们只能用对Long型进行序列化和反序列化。(但基础类型也不多,定义8个可能也没什么)
OxmSerializer:使用SpringO/X映射的编排器和解排器实现序列化,用于XML序列化。


http://yetanotherdevblog.com/introduction_to_spring_data_redis

http://www.jiagoushuo.com/article/1000380.html
//创建List条目,key是cart
BoundListOperations<String, Object>cart=redisTemplate.boundListOps("cart");
//删除最后的一条数据
cart.rightPop();
//在最后,添加一条数据
cart.rightPush("我笑了");

@Cacheable(value="workDetailV150",key="'workDetailV150'.concat(#param.workId.toString()).concat(#param.userId.toString())")

http://m.2cto.com/kf/201606/518712.html
对于使用 @Cacheable 注解的方法,每个缓存的 key 生成策略默认使用的是参数名+参数值,比如以下方法:
@Cacheable(value="commonCache")// 使用了一个缓存名叫 accountCache
public Account getAccountByName(String userName) {
// 方法内部实现不考虑缓存逻辑,直接实现业务
System.out.println("real query account."+userName);
return getFromDB(userName);
}
username取值为zhenghuasheng时,key为userName-zhenghuasheng,一般情况下没啥问题,二般情况如方法 key 取值相等然后参数名也一样的时候就出问题了,如:
@Cacheable(value="commonCache")
public LoginData getLoginData(String userName){

}
这个方法的缓存也将保存于 key 为 users~keys 的缓存下。对于 userName 取值为 “zheghuasheng” 的缓存,key 也为 “userName-zhenghuasheng”,将另外一个方法的缓存覆盖掉。
解决办法是使用自定义缓存策略,对于同一业务(同一业务逻辑处理的方法,哪怕是集群/分布式系统),生成的 key 始终一致,对于不同业务则不一致
http://www.jianshu.com/p/2513f625fcae
@Cacheable(value="users", key="#userid.toString() + #username.toString()")  
public User findByUsername(String username, String userid)  

@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(value="users", key="#p0")
public User find(Integer id) {
    returnnull;
}

@Cacheable(value="users", key="#p0.id")
public User find(User user) {
    returnnull;
}

http://www.codeceo.com/article/redis-spring-cache.html
(1)Redis数据库完全在内存中,使用磁盘仅用于持久性。
(2)相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。
(3)Redis可以将数据复制到任意数量的从服务器。
Redis 优势?
(1)异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。
(2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。
(3)操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。
(4)多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。
 public Object get(final String key) {
  Object result = null;
  ValueOperations<Serializable, Object> operations = redisTemplate
    .opsForValue();
  result = operations.get(key);
  return result;
 }

 /**
  * 写入缓存
  * 
  * @param key
  * @param value
  * @return
  */
 public boolean set(final String key, Object value) {
  boolean result = false;
  try {
   ValueOperations<Serializable, Object> operations = redisTemplate
     .opsForValue();
   operations.set(key, value);
   result = true;
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
http://blog.csdn.net/zhaozhenzuo/article/details/46535573
  项目中使用spring Redis cache做为cache客户端。
  spring redis cache中RedisCache是整个spring cache的领域模型,对应一个cache块的操作类。
 RedisCache中定义了put,get,clean,evict操作。
 其中clean方法用于清除当前cache块中所有的元素,这里会加锁,而锁的实现是往redis服务器上存放一个key为:cache块名称加上~lock的元素。最后清除锁则是在clean方法执行完成后在finally中清除。
 put与get方法运行时会查看是否存在lock锁,存在则会sleep 300毫秒。这个过程会一直继续,直到redis服务器上不存在锁时才会进行相应的get与put操作。
 这里存在一个问题,如果clean方法运行时间很长,这时当前运行clean操作的机子挂了,就导致lock元素一直存在于redis服务器上。
 之后就算这个机子重新启动后,也无法正常使用cache。原因是:get与put方法在运行时,锁lock始终存在于redis服务器上。
 项目运行中,遇到了这种情况。
  在开发过程中,如果使用到开源框架中的某个方法,一定要深入了解其实现方式,运用场景。最坏的情况是什么。

http://www.cnblogs.com/softidea/p/5801499.html

  1.     public RedisTemplate<String, String> redisTemplate(  
  2.             RedisConnectionFactory factory) {  
  3.         StringRedisTemplate template = new StringRedisTemplate(factory);  
  4.         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  
  5.         ObjectMapper om = new ObjectMapper();  
  6.         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);  
  7.         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);  
  8.         jackson2JsonRedisSerializer.setObjectMapper(om);  
  9.         template.setValueSerializer(jackson2JsonRedisSerializer);  
  10.         template.afterPropertiesSet();  
  11.         return template;  
  12.     }  
  1. 要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis,比如本文中的 com.defonds.bdp.city.bean.City 类,如果不实现 Serializable 的话将会遇到类似这种错误:nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.defonds.bdp.city.bean.City]]。
  2. 缓存的生命周期我们可以配置,然后托管 Spring CacheManager,不要试图通过 redis-cli 命令行去管理缓存。比如 provinceCities 方法的缓存,某个省份的查询结果会被以 key-value 的形式存放在 Redis,key 就是我们刚才自定义生成的 key,value 是序列化后的对象,这个 key 会被放在 key 名为 provinceCities~keys key-value 存储中,参考下图"provinceCities 方法在 Redis 中的缓存情况"。可以通过 redis-cli 使用 del 命令将 provinceCities~keys 删除,但每个省份的缓存却不会被清除。
  3. CacheManager 必须设置缓存过期时间,否则缓存对象将永不过期,这样做的原因如上,避免一些野数据“永久保存”。此外,设置缓存过期时间也有助于资源利用最大化,因为缓存里保留的永远是热点数据。
  4. 缓存适用于读多写少的场合,查询时缓存命中率很低、写操作很频繁等场景不适宜用缓存。
方案一:数据库乐观锁
乐观锁通常实现基于数据版本(version)的记录机制实现的,比如有一张红包表(t_bonus),有一个字段(left_count)记录礼物的剩余个数,用户每领取一个奖品,对应的left_count减1,在并发的情况下如何要保证left_count不为负数,乐观锁的实现方式为在红包表上添加一个版本号字段(version),默认为0。
方案二:基于Redis的分布式锁
第三种方案:基于Zookeeper的分布式锁
利用节点名称的唯一性来实现独占锁
ZooKeeper机制规定同一个目录下只能有一个唯一的文件名,zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建/lock/${lock_name}_lock节点,最终成功创建的那个客户端也即拥有了这把锁,创建失败的可以选择监听继续等待,还是放弃抛出异常实现独占锁。
利用临时顺序节点控制时序实现
/lock已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。\
算法思路:对于加锁操作,可以让所有客户端都去/lock目录下创建临时顺序节点,如果创建的客户端发现自身创建节点序列号是/lock/目录下最小的节点,则获得锁。否则,监视比自己创建节点的序列号小的节点(比自己创建的节点小的最大节点),进入等待。
对于解锁操作,只需要将自身创建的节点删除即可。

Labels

Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

Popular Posts