??xml version="1.0" encoding="utf-8" standalone="yes"?>
上周参加QconQ有个兄弟分享秒杀pȝ的优化,其观Ҏ(gu)些赞同,大部分观点却q不同意Q结合自ql验Q谈谈自q一些看法?/span>
一、ؓ(f)什么难
U杀pȝ隑ց的原因:(x)库存只有一份,所有h?x)在集中的时间读和写q些数据?/span>
例如米手机每周二的U杀Q可能手机只?万部Q但瞬时q入的流量可能是几百几千万?/span>
又例?2306抢票Q亦与秒杀cMQ瞬时流量更甚?/span>
二、常见架?/strong>
量C亿别,常见站点架构如上Q?br style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;" />
1Q?span style="margin: 0px; padding: 0px; max-width: 100%; color: #ff0000; box-sizing: border-box !important; word-wrap: break-word !important;">览器端Q最上层Q会(x)执行C些JS代码
2Q?span style="margin: 0px; padding: 0px; max-width: 100%; color: #ff0000; box-sizing: border-box !important; word-wrap: break-word !important;">站点?/span>Q这一层会(x)讉K后端数据Q拼html面q回l浏览器
3Q?span style="margin: 0px; padding: 0px; max-width: 100%; color: #ff0000; box-sizing: border-box !important; word-wrap: break-word !important;">服务?/span>Q向上游屏蔽底层数据l节
4Q?span style="margin: 0px; padding: 0px; max-width: 100%; color: #ff0000; box-sizing: border-box !important; word-wrap: break-word !important;">数据?/span>Q最l的库存是存在这里的Qmysql是一个典?/span>
三、优化方?/strong>
1Q?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">请求尽量拦截在pȝ上游Q传l秒杀pȝ之所以挂Q请求都压倒了后端数据层,数据d锁冲H严重,q发高响应慢Q几乎所有请求都时Q流量虽大,下单成功的有效流量甚【一火车其实只?000张票Q?00w个h来买Q基本没有h能买成功Q请求有效率??/span>
2Q?span style="margin: 0px; padding: 0px; max-width: 100%; color: #ff0000; box-sizing: border-box !important; word-wrap: break-word !important;">充分利用~存Q这是一个典型的d些少的应用场景【一火车其实只?000张票Q?00w个h来买Q最?000个h下单成功Q其他h都是查询库存Q写比例只有0.1%Q读比例?9.9%】,非常适合使用~存
四、优化细?/strong>
4.1Q浏览器层请求拦?/span>
点击?#8220;查询”按钮之后Q系l那个卡呀Q进度条涨的慢呀Q作为用P?x)不自觉的再ȝ?#8220;查询”Ql点Ql点Q点点点。。。有用么Q^白无故的增加了系l负载(一个用L(fng)5ơ,80%的请求是q么多出来的Q,怎么_(d)
aQ?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">产品层面Q用L(fng)?#8220;查询”或?#8220;购票”后,按钮|灰Q禁止用户重复提交请?/strong>
bQ?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">JS层面Q限制用户在xU之内只能提交一ơ请?/strong>
如此限流Q?0%量已拦
4.2Q站点层h拦截与页面缓?/span>
览器层的请求拦截,只能拦住白用户Q不q这?9%的用户哟Q,高端的程序员Ҏ(gu)不吃q一套,写个for循环Q直接调用你后端的httphQ怎么_(d)
aQ?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">同一个uidQ限制访问频度,做页面缓?/strong>QxU内到达站点层的hQ均q回同一面
bQ?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">同一个item的查询,例如手机车次Q做面~存QxU内到达站点层的hQ均q回同一面
如此限流Q又?9%的流量会(x)被拦截在站点?/span>
4.3Q服务层h拦截与数据缓?/span>
站点层的h拦截Q只能拦住普通程序员Q高U黑客,假设他控制了10w台肉鸡(q且假设买票不需要实名认证)Q这下uid的限制不行了吧?怎么_(d)
aQ大哥,我是服务层,我清楚的知道米只有1万部手机Q我清楚的知道一列火车只?000张R,我?0w个请求去数据库有什么意义呢Q?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">对于写请求,做请求队列,每次只透过有限的写hL据层Q如果均成功再放下一批,如果库存不够则队列里的写h全部q回“已售?#8221;
bQ?strong style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">对于读请?/strong>Q还用说么?cache来抗Q不是memcachedq是redisQ单机抗个每U?0w应该都是没什么问题的
如此限流Q只有非常少的写hQ和非常的ȝ存mis的请求会(x)透到数据层去Q又?9.9%的请求被拦住?/span>
4.4Q数据层闲庭信步
C数据q一层,几乎没有什么请求了Q单Z能扛得住Q还是那句话Q库存是有限的,米的能有限,透过q多h来数据库没有意义?/span>
五、ȝ
没什么ȝ了,上文应该描述的非常清楚了Q对于秒杀pȝQ再ơ重复下W者的两个架构优化思\Q?/span>
1Q尽量将h拦截在系l上?/span>
2Q读多写的常用多用缓?/span>
关于q个机制|上有很多解释的Q我个h的ȝ如下?/p>
Memcached的内存分配以page为单位,默认情况下一个page?MQ可以通过-I参数在启动时指定。如果需要申请内存时Qmemcached?x)划分出一个新的pageq分配给需要的slab?img border="0" alt="" src="http://www.aygfsteel.com/images/blogjava_net/stevenjohn/images/11.jpg" width="511" longdesc="" height="272" />
域。page一旦被分配在重启前不会(x)被回收或者重新分配(page ressign已经?.2.8版移除了Q?span class="Apple-converted-space">
Memcachedq不是将所有大的数据都放在一L(fng)Q而是预先数据空间划分ؓ(f)一pdslabsQ每个slab只负责一定范围内的数据存储。如下图Q每个slab只存储大于其上一个slab的sizeq小于或者等于自己最大size的数据。例如:(x)slab 3只存储大介?37 ?224 bytes的数据。如果一个数据大ؓ(f)230byte被分配到slab 4中。从下图可以看出Q每个slab负责的空间其实是不等的,memcached默认情况下下一个slab的最大gؓ(f)前一个的1.25倍,q个可以通过修改-f参数来修改增长比例?span class="Apple-converted-space">
Chunk是一pd固定的内存空_(d)q个大小是理它的slab的最大存攑֤。例如:(x)slab 1的所有chunk都是104byteQ而slab 4的所有chunk都是280byte。chunk是memcached实际存放~存数据的地方,因ؓ(f)chunk的大固定ؓ(f)slab能够存放的最大|所以所有分配给当前slab的数据都可以被chunk存下。如果时间的数据大小于chunk的大,IZ的空间将?x)被闲置Q这个是Z防止内存片而设计的。例如下图,chunk size?24byteQ而存储的数据只有200byteQ剩下的24byte被闲置?span class="Apple-converted-space">
Memcached在启动时通过-m指定最大用内存,但是q个不会(x)一启动占用,是随着需要逐步分配l各slab的?br /> 如果一个新的缓存数据要被存放,memcached首先选择一个合适的slabQ然后查看该slab是否q有I闲的chunkQ如果有则直接存放进去;如果没有则要q行甌。slab甌内存时以page为单位,所以在攑օW一个数据,无论大小为多,都会(x)?M大小的page被分配给该slab。申请到page后,slab?x)将q个page的内存按chunk的大进行切分,q样变成了一个chunk的数l,在从q个chunk数组中选择一个用于存储数据。如下图Qslab 1和slab 2都分配了一个pageQƈ按各自的大小切分成chunk数组?span class="Apple-converted-space">
l合上面的介l,memcached的内存分配策略就是:(x)按slab需求分配pageQ各slab按需使用chunk存储?br />q里有几个特点要注意Q?/p>
知道了这些以后,可以理解ؓ(f)什么d存没有被全部占用的情况下Qmemcached却出C丢失~存数据的问题了?br />*******************
当存储的值item大于1M的时候:(x)
1M 用完?x)申请一个新?nbsp;1M
Memcached 是一个高性能的分布式内存对象~存pȝQ用于动态Web应用以减L据库负蝲。它通过在内存中~存数据和对象来减少d数据库的ơ数Q从而提供动态、数据库驱动|站的速度。MemcachedZ一个存储键/值对的hashmap。其守护q程Qdaemon Q是用C写的Q但是客L(fng)可以用Q何语a来编写,q过memcached协议与守护进E通信。但是它q不提供冗余Q例如,复制其hashmap条目Q;当某个服务器S停止q行或崩溃了Q所有存攑֜S上的?值对都将丢失?/p>
Memcached官方Q?a style="margin: 0px; padding: 0px; color: #6fbc4c; text-decoration: initial;">http://danga.com/memcached/
关于Memcached的介l请参考:(x)Memcached深度分析
下蝲Windows的Server?/strong>
下蝲地址Q?a style="margin: 0px; padding: 0px; color: #6fbc4c; text-decoration: initial;">http://code.jellycan.com/memcached/
windows服务端下载地址:如何在一个中型的Java应用中用Memcached~存数据不是个简单的问题。当某个~存数据需要在多个pȝ间共享和失效Ӟ必须要有l一的规划才能保证不出错。经q各U实践,目前pȝ在用Memcached~存数据全部采用Simple-Spring-Memcached框架来完成,q统一规划各系lSpring和Cache key的配|?br />下面对在使用q程中需要注意的点做一个详l说明:(x)
目前我们pȝ中有两个不同的Memcached服务器:(x)
׃应用所有的~存数据都放在app~存上,为避免各应用的缓存数据出现冲H,必须规划好它们的命名I间。所qSimple-Spring-Memcached支持namespace的概念,因此对各应用的namespace前缀规定如下Q?/p>
应用 | NAMESPACE前缀 | |
---|---|---|
goodscenter | goodscenter | |
trade | trade | |
uic | uic |
q个namespace在生成keyӞ放在最前面Q稍后会(x)有例子详q?br />同一个应用中存在许多需要缓存的对象Q因此约定namespace前缀之后再加上缓存对象的cd?br />例子如下Q?/p>
应用 | ~存对象 | 完整的NAMESPACE | 最l生成的KEY |
---|---|---|---|
trade | TcRate (id?2) | trade:TcRate | trade:TcRate:12 |
goodscenter | GoodsDo(id?2) | goodscenter:GoodsDo | goodscenter:GoodsDo:12 |
Simple-Spring-Memcached提供的针对单个对象的注解接口提供了两Ukey生成方式Q?a title="使用SSM注解做缓存操作(一Q? target="_blank" style="cursor: pointer; color: #eb374b;">详情见此?/a>
对于W一U只要求必须保证key不与其它的冲H,且namesapceW合规则?br />W二U时Q约定缓存的数据对象必须实现有带CacheKeyMethod的cacheKeyҎ(gu)Q参考实现如下:(x)
@CacheKeyMethod public String cacheKey() { return this.getId(); }
![]() | 目前@CacheKeyMethod只支持返回String的方法,需要改造成可接受LongQInteger型的。当前必L单独的方法来作ؓ(f)~存Key的生成器 |
![]() | 真实存放到Memcached的key的生成规则是Qnamespace:key?br />如goodscenter的id?2的domain对象GoodsDo,按上q方式生成的key为:(x)goodscenter:GoodsDo:42 |
关于Simple-Spring-Memcached具体XML配置如下Q?/p>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <import resource="classpath:simplesm-context.xml"/> <aop:aspectj -autoproxy/> <context:annotation -config/> <bean name="appCache" class="com.google.code.ssm.CacheFactory"> <property name="cacheClientFactory"> <bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/> </property> <property name="addressProvider"> <bean class="com.google.code.ssm.config.DefaultAddressProvider"> <!--memcached服务器ip:port 可以是多个,格式? 127.0.0.1:11211,192.168.100.11:11211--> <property name="address" value="{memcached.server}"/> </bean> </property> <property name="configuration"> <!-- memcachedq接器的配置Q具体的配置参考这个类 --> <bean class="com.google.code.ssm.providers.XMemcachedConfiguration"> <!--是否使用一致性哈?-> <property name="consistentHashing" value="true"/> <!--q接?-> <property name="connectionPoolSize" value="10"/> <property name="optimizeGet" value="true"/> </bean> </property> <property name="cacheName"> <!-- 该Memcached配置的Cache名称 一个应用中存在多个MemcachedӞ各个配置的cacheName必须不同。如果该值未设,pȝ默认为default --> <value>appCache</value> </property> </bean> </beans>
直接使用注解来处理缓存及(qing)失效非常单,下面是相应的例子Q?br />d~存Q?/strong>
@Override @ReadThroughSingleCache(namespace = "goodscenter:EventGoodsDo", expiration = 60) @CacheName("appCache") public EventGoodsDo queryEventGoodsDo(@ParameterValueKeyProvider(order = 0) long goodsId, @ParameterValueKeyProvider(order = 1) long eventId) { return getRemoteServiceBean().queryEventGoodsDo(goodsId, eventId); }
更新~存Q?/strong>
@BridgeMethodMappings(value = {@BridgeMethodMapping(erasedParamTypes ={Object.class},targetParamTypes = {com.hqb360.promotion.dao.entity.EventGoods.class},methodName = "update")}) public class EventGoodsDaoImpl<EventGoods> extends BaseDaoImpl<EventGoods> implements EventGoodsDao<EventGoods> { @Override public DaoStatementName getDaoStatementName() { return new DefaultDaoStatementName() { public String getDomainName() { return "EventGoods"; } }; } @Override @InvalidateSingleCache(namespace = "goodscenter:EventGoodsDo") @CacheName("appCache") public void update(@ParameterValueKeyProvider EventGoods obj) throws DataAccessException { super.update(obj); } }
@CacheKeyMethod public String getCacheKey() { return goodsId + CACHE_ID_SEPARATOR + eventId; } public static final String CACHE_ID_SEPARATOR = "/";
某些场景我们希望更便捷地自己手动来管理缓存数据,此时需要用Simple-Spring-Memcached配置中定义的bean。以上面的配|文件ؓ(f)例,使用Ҏ(gu)如下
bean的注入:(x)
@Autowired private Cache appCache;
bean的?
appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);
Posted in Java
@CacheKeyMethod:~存key生成注解
---------------------------------d-------------------------------------------
@ReadThroughAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000): d指定key~存
@ReadThroughSingleCache(namespace = SINGLE_NS, expiration = 0):d单个~存
@ReadThroughMultiCache(option = @ReadThroughMultiCacheOption(generateKeysFromResult = true)):d多个~存
@ReadThroughMultiCacheOption(generateKeysFromResult = true) d多个~存操作generateKeysFromResult 通过l果生成key
---------------------------------更新-------------------------------------------
@UpdateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000): 指定key更新~存
@UpdateSingleCache(namespace = SINGLE_NS, expiration = 2): 更新单个~存(namespace 命名I间, expiration 失效旉单位U?
@UpdateMultiCache(namespace = "Bravo", expiration = 300): 更新多个~存
---------------------------------失效-------------------------------------------
@InvalidateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo") : 指定key失效~存
@InvalidateSingleCache(namespace = SINGLE_NS):失效单个~存
@InvalidateMultiCache(namespace = "Delta") : 失效多个~存
---------------------------------参数-------------------------------------------
@ParameterDataUpdateContent: 标记Ҏ(gu)的参C为更新内宏V这个注解应l合Update*Cache注解使用
@ParameterValueKeyProvider: 标记方法的参数做ؓ(f)计算~存key.如果Ҏ(gu)被注解的对象标记CacheKeyMethod的方法将?x)用来生成缓存key否则调用toString()生成
@ParameterValueKeyProvider(order=0) 属性表C如果多个参数做为key旉提供参数序
与@ParameterValueKeyProvidercM的注解有:
{
@ReturnValueKeyProvider: q回值对象中计算key
}
---------------------------------泛型处理-------------------------------------------
@BridgeMethodMappings({ @BridgeMethodMapping(methodName = "updateUser",
erasedParamTypes = { Object.class }, targetParamTypes = { AppUser.class }) }): 泛型桥接注解
methodName : 指定Ҏ(gu)
erasedParamTypes : 擦除对象cd
targetParamTypes : 目标转换cd
---------------------------------计数?------------------------------------------
@InvalidateAssignCache :在给的计器上加1. 如果不存在则初始化ؓ(f)1
@DecrementCounterInCache : 在给的计数器上减1
@ReadCounterFromCache :d计数?/span>
@UpdateCounterFromCache : 更新计数?/span>
Simple-Spring-Memcached代码阅读之BridgeMethod
http://www.colorfuldays.org/program/java/bridgemethod%E7%9A%84%E4%BD%9C%E7%94%A8/
http://www.colorfuldays.org/tag/ssm/ q个pd不错
某些场景我们希望更便捷地自己手动来管理缓存数据,此时需要用Simple-Spring-Memcached配置中定义的bean。以上面的配|文件ؓ(f)例,使用Ҏ(gu)如下
bean的注入:(x)
@Autowired private Cache appCache;
bean的?
appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);
学习(fn)了memcacheQ这是个好东西,分n一下自q实例,也方便以后查找?/p>
一、前期准?/p>
1) 下蝲memcached服务端memcached-1.2.6-win32-bin.zipQ地址Qhttp:
//code.jellycan.com/memcached/
2) 下蝲java版客L(fng) java_memcached-release_2.6.1.zip
3) 解压~memcached-1.2.6-win32-bin.zip到指定目录,例如QD:\memcached-1.2.6-win32 Q?/code>在终端(即cmd命o(h)行界面)
D:\memcached-1.2.6-win32\memcached.exe -d install
D:\memcached\memcached.exe -d start
q样memcache׃(x)作ؓ(f)windowspȝ服务在每ơ开机时启动memcache服务?/code>
package com.abin.lee.spring.memcache;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.danga.MemCached.MemCachedClient;
public class MemcacheUtilTest {
static MemCachedClient memcachedClient;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
ApplicationContext context= new ClassPathXmlApplicationContext("com/abin/lee/spring/memcache/spring-memcache.xml");
memcachedClient= (MemCachedClient)context.getBean("memcachedClient");
}
@Test
public void test() {
memcachedClient.set("name", "abin");
System.out.println(memcachedClient.get("name"));
}
}
- 到http://code.jellycan.com/memcached/下蝲E_版?/p>
- 下蝲后解压到某个盘下面,比如在c:\memcachedQ在l端Q也即cmd命o(h)界面Q下输入 ‘c:\memcached\memcached.exe -d install’ 安装?/p>
- 再输入:(x) ‘c:\memcached\memcached.exe -d start’ 启动?/p>
- 修改memcache的内存大,可以在注册表里找到HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/memcached ServerQ修改ImagePath的gؓ(f)
“C:/memcached/memcached.exe” -d runservice -m 512
NOTE: Windows版本一般用作开发调试只用,不徏议在产品环境中用?br />
二:(x)JAVAq接使用Memcached
1, 下蝲memcached客户端开发包Q地址https://github.com/gwhalin/Memcached-Java-Client
2Q下面是一个连接ƈ使用的简单例?/p>
package com.ea.online.memcache;
import java.util.Date;
import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;
public class MyClass {
// create a static client as most installs only need
// a single instance
protected static MemCachedClient mcc = new MemCachedClient();
protected static SockIOPool pool = null;
// set up connection pool once at class load
static {
// Server list
String[] servers = { "localhost:11211" };
// Specify memcached capacity
Integer[] weights = { 3, 3, 2 };
/*
* String[] serverlist = { "cache0.server.com:12345",
* "cache1.server.com:12345" }; Integer[] weights = { new
* Integer(5), new Integer(2) }; int initialConnections = 10; int
* minSpareConnections = 5; int maxSpareConnections = 50; long
* maxIdleTime = 1000 * 60 * 30; // 30 minutes long maxBusyTime = 1000 *
* 60 * 5; // 5 minutes long maintThreadSleep = 1000 * 5; // 5 seconds
* int socketTimeOut = 1000 * 3; // 3 seconds to block on reads int
* socketConnectTO = 1000 * 3; // 3 seconds to block on initial
* connections. If 0, then will use blocking connect (default) boolean
* failover = false; // turn off auto-failover in event of server down
* boolean nagleAlg = false; // turn off Nagle's algorithm on all
* sockets in pool boolean aliveCheck = false; // disable health check
* of socket on checkout
*
* SockIOPool pool = SockIOPool.getInstance();
* pool.setServers(serverlist);
* pool.setWeights(weights);
* pool.setInitConn(initialConnections);
* pool.setMinConn(minSpareConnections);
* pool.setMaxConn(maxSpareConnections); pool.setMaxIdle(maxIdleTime);
* pool.setMaxBusyTime(maxBusyTime);
* pool.setMaintSleep(maintThreadSleep);
* pool.setSocketTO(socketTimeOut); pool.setNagle(nagleAlg);
* pool.setHashingAlg(SockIOPool.NEW_COMPAT_HASH);
* pool.setAliveCheck(true); pool.initialize();
*/
// grab an instance of our connection pool
pool = SockIOPool.getInstance();
// set the servers and the weights
pool.setServers(servers);
pool.setWeights(weights);
// Specify main thread maintain frequency
pool.setMaintSleep(30);
// set some TCP settings
// disable nagle
pool.setNagle(false);
// set the read timeout to 3 secs
pool.setSocketTO(3000);
// and don't set a connect timeout
pool.setSocketConnectTO(0);
// initialize the connection pool
pool.initialize();
}
// from here on down, you can call any of the client calls
public static void main(String[] args) {
// Test expired
mcc.set("foo", "This is a test String", new Date(
new Date().getTime() + 3000));
String bar = mcc.get("foo").toString();
System.out.println("test-->" + bar);
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(mcc.get("foo"));
}
// pool.shutDown();
}
}
详细使用可以借鉴q篇文章Qhttp://sillycat.iteye.com/blog/563615
三:(x)l合AOP~程
1Q对于memcached与spring aop的集成网上又是一堆,q里不提了?br /> 2Q对于memcached与guice aop的集成可参考http://code.google.com/p/google-guice/wiki/AOP
3Q对于需要通过手动实现动态代理的方式来实现AOP的可以参?http://www.aygfsteel.com/DoubleJ/archive/2008/03/04/183796.html
四:(x)一个简单的环绕通知切面Q不可运行,仅作参?/strong>
public Around implements MethodInterceptor {
....
public Object invoke(MethodInvocation mi, Object[] args) {
Object obj = null;
//从Memcached中获?br /> obj = mcc.get(this.class.getName() + mi.getMethodName() + args.hashcode());
if(obj != null) {
return obj;
}
obj = method.invoke(args);
//存入Memcached
mcc.set(this.class.getName() + mi.getMethodName() + args.hashcode, obj, expiredDate);
return obj;
}
....
}
南京-HF(226358522) 11:22:44
?br />ζ q了时的行 ?779014475) 11:22:50
加mc 本来是Z减少mysql操作的。?br />ζ q了时的行 ?779014475) 11:22:54
q样子都没啥意义了。?br />南京-HF(226358522) 11:23:07
应该是mysql里面有触发器Q当数据有改变,׃(x)同步到memcached
南京-HF(226358522) 11:23:30
q你以哪个数据Z?br />南京-HF(226358522) 11:24:04
q样memcached的目的就是加快你的查询速度
沧v镉K(136419390) 11:24:18
q方案根据自己需求设?br />沧v镉K(136419390) 11:24:23
l合很多?br />ζ q了时的行 ?779014475) 11:25:03
最q也到。。mysql与mc同步的。。不知道有啥好方?br />南京-HF(226358522) 11:25:08
毕竟Q查询用的是最多的。修Ҏ(gu)者插入的话,数据库的数据必须得边Q所以才有这么个Ҏ(gu)。当然这个也不一定适合你们的场景,你自己取?br />南京-HF(226358522) 11:26:21
如果你的数据Q增删改查都很频J那q种׃适合?br />ζ q了时的行 ?779014475) 11:26:54
恩。。这L(fng)话读写都直接对mc操作。。然后定时同步。?br />ζ q了时的行 ?779014475) 11:27:09
但是蛋疼的就是同步方案。?/font>
ζ q了时的行 ?779014475) 11:27:25
不好做增量同步。?br />沧v镉K(136419390) 11:27:31
有时做些冗余是不错的
ζ q了时的行 ?779014475) 11:27:36
服务端用c写的?br />