很久没有更新博客,希望以后有时间还是持续去写,现在才发现这是很棒的过程。很早之前就整理过有关缓存的使用和介绍,现在将它贴出来,也作为个人笔记:
许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、网站显示延迟等重大影响。这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。
为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。
Spring配置:
<beanname="memcachedClient" class="net.rubyeye.xmemcached.utils.XMemcachedClientFactoryBean"destroy-method="shutdown"> <property name="servers" value="${VIPCPS_MC_S1_HOST}:${VIPCPS_MC_S1_PORT}${VIPCPS_MC_S2_HOST}:${VIPCPS_MC_S2_PORT}${VIPCPS_MC_S3_HOST}:${VIPCPS_MC_S3_PORT}${VIPCPS_MC_S4_HOST}:${VIPCPS_MC_S4_PORT}${VIPCPS_MC_S5_HOST}:${VIPCPS_MC_S5_PORT}"></property> <!-- nio connection poolsize.不建议使用连接池,待性能测试 --> <property name="connectionPoolSize" value="1"></property> <!-- Distributed strategy --> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> </bean>
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sortedset --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis 是一个高性能的key-value数据库。redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Python,Ruby,Erlang,PHP客户端,使用很方便。[1]
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。从盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
redis的存储分为内存存储、磁盘存储和log文件三部分,配置文件中有三个参数对其进行配置。save seconds updates,save配置,指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。appendonly yes/no ,appendonly配置,指出是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面的save条件来同步的,所以有的数据会在一段时间内只存在于内存中。appendfsync no/always/everysec ,appendfsync配置,no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次。
Redis更多的是作为Memched的一种替换而存在。
用key值和client代表值的hash值之间的某种运算来确定 存储的server。
为了解决负载均衡,当server节点太少就要构造虚拟的server节点。
解决了简单hash的以下弱点:
1)增删市点时,更新效率低。当系统中存储节点数量发生增加或减少时,映射公式将发生变化为hash(object)%(N±1),这将使得所有obiect的映射位置发生变化,整个系统数据对象的映射位置都需要重新进行计算,系统无法对外界访问进行正常响应,将导致系统处于崩溃状态。
2)平衡性差,未考虑节点性能差异。由于硬件性能的提升,新添加的节点具有更好的承载能力,如何对算法进行改进,使节点性能可以得到较好利用。
3)单调性不足。衡量数据分布技术的一项重要指标是单调性,单调性是指如果已经有一些内容通过哈希计算分派到了相应的缓冲中,当又有新的缓冲加入到系统中时,哈希的结果应能够保证原有已分配的内容可以被映射到新的缓冲中去,而不会被映射到旧的缓冲集合中的其他缓冲区。
<!-- 默认 --> <bean id="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig"> <property name="maxActive"value="${redis.pool.maxActive}" /> <property name="maxIdle"value="${redis.pool.maxIdle}" /> <property name="maxWait"value="${redis.pool.maxWait}" /> <property name="testOnBorrow"value="${redis.pool.testOnBorrow}" /> </bean> <bean id="stringRedisTemplate0" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory0"scope="prototype"></bean> <bean id="stringRedisTemplate1" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory1"scope="prototype"></bean> <!-- 自定义 --> <bean id="jedisConnectionFactory0" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName"value="${VIPCPS_REDIS_HOST_0}" /> <property name="port"value="${VIPCPS_REDIS_PORT_0}" /> <property name="poolConfig"ref="jedisPoolConfig" /> </bean> <bean id="jedisConnectionFactory1" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <property name="hostName"value="${VIPCPS_REDIS_HOST_1}" /> <property name="port"value="${VIPCPS_REDIS_PORT_1}" /> <property name="poolConfig"ref="jedisPoolConfig" /> </bean> <bean id="redisTemplateRouter" class="com.vipshop.unionweb.framework.redis.RedisTemplateRouter"> <constructor-arg> <list> <ref bean="stringRedisTemplate0"/> <ref bean="stringRedisTemplate1"/> </list> </constructor-arg> </bean>
public class RedisTemplateRouter { privatestatic finalLogger logger = LoggerFactory.getLogger(RedisTemplateRouter.class); privatestatic CRC32 crc32 = newCRC32(); /** * 服务数量 */ privatestatic intSERVER_COUNT = 1; privatestatic List<StringRedisTemplate>redisTemplates = newArrayList<StringRedisTemplate>(); publicRedisTemplateRouter(List<StringRedisTemplate> redisTemplates){ RedisTemplateRouter.redisTemplates= redisTemplates; SERVER_COUNT= redisTemplates.size(); } publicstatic List<StringRedisTemplate>getRedisTemplates() { returnredisTemplates; } publicstatic StringRedisTemplategetRouteRedis(String key) { crc32.reset(); crc32.update(key.getBytes()); longcrc32Value = crc32.getValue(); intindex = Math.abs((int) (crc32Value% SERVER_COUNT)); logger.debug("route to redis [key:{},index:{},crc32:{}]",new Object[] {key, index, crc32Value}); returnredisTemplates.get(index); } }
用法:
RedisTemplate template= RedisTemplateRouter.getRouteRedis(key); Object result = template.opsForValue().get(key);
是一个纯Java的进程内缓存框架;缓存在内存和磁盘存储可以伸缩到数G; Ehcache是一种广泛使用的开源Java分布式缓存;支持LRU、LFU和FIFO多种淘汰算法;可以对页面、对象、数据进行缓存,同时支持集群/分布式缓存;
对象缓存就是将查询的数据,添加到缓存中,下次再次查询的时候直接从缓存中获取,而不去数据库中查询。 <!-- 关联配置文件-->
1. <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> 2. <property name="configLocation" value="classpath:com/config/ehcache.xml"/> 3. </bean> 4. 5. <!-- 支持缓存注解 --> 6. <cache:annotation-driven /> 7. 8. <!-- 默认是cacheManager 配置缓存管理器--> 9. <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> 10. <property name="cacheManager" ref="cacheManagerFactory"/> 11. </bean>
<ehcacheupdateCheck="false"monitoring="autodetect"dynamicConfig="true"> <diskStorepath="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="0" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> <!-- 渠道综合报表缓存 --> <cachename="channelSummaryCache" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="120"timeToLiveSeconds="120" overflowToDisk="false"maxElementsOnDisk="10000000" diskPersistent="false"diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="FIFO" /> <!-- 配置自定义缓存 maxElementsInMemory:缓存中允许创建的最大对象数 eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。 timeToIdleSeconds:缓存数据的钝化时间,也就是在一个元素消亡之前, 两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效, 如果该值是 0 就意味着元素可以停顿无穷长的时间。 timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值, 这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。 overflowToDisk:内存不足时,是否启用磁盘缓存。 memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。 -->
缓存注解有以下三个:
@Cacheable(value=”accountCache”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。
@CacheEvict 注释来标记要清空缓存的方法,当这个方法被调用后,即会清空缓存。
@CachePut 注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。
#带参数
@CachePut(value=" channelSummaryCache", key="#startTime.getTime().toString().concat('_').concat(''+#endTime).concat('_')" + ".concat(''+#commissionType).concat('_').concat(#userId).concat('_'+#channelTag)") PublicList<SummaryReportWithChannelVO>summaryGroupByChannel( Integer userId,Date startTime, Date endTime, Short commissionType, String channelTag) {……}