一些使用上遇到过的问题。
- mc key限制。 长度限制250;不能有空格,中文。
- mc超时时间设置。过大,会表现为set进去成功, 但get不出来。如果mc有打印verbose级日志的话会显示超时:
<34 set 111 0 8640000 3
> NOT FOUND 111
<35 get 111
> FOUND KEY 111 -nuked by expire
原因是:
Expiration times can be set from 0, meaning "never expire", to 30 days. Any time higher than 30 days is interpreted as a unix timestamp date. If you want to expire an object on january 1st of next year, this is how you do that.
see:https://code.google.com/p/memcached/wiki/NewProgramming#Expiration
原来,mc范围类型的时间,最大是设置为30天了(设置的时候要改为秒,也就是60*60*24*30秒)。超过这个值,就当做是unix 时间戳类型的时间。
一般时间戳类型的时间用得不多,mc最大时限设置为1个月,或者0就好了。
- 单点雪崩问题。假设线上只有1台MC。忽然之间这台MC挂了。然后所有的请求都跑到数据库中去了。导致整个系统极慢甚至挂掉。就算此时立即恢复mc,mc重启了但是数据都没了。照样跑到数据库,导致挂掉。所以,线上环境要使用多台mc跑一致性hash。并且监控好mc的情况(使用率如何、是否在跑LRU了)。
- 使用率问题。根据slab allocator的原理。存放一个item的空间是已经分配好的。必定有浪费的情况(比如说set一个字符串到mc中, 这个字符串需要100b的空间,会把这个字符串放到128b大小的slab中)。这相差的28b就是被浪费掉的了。见do_item_alloc方法调用slabs_clsid:
unsigned int slabs_clsid(const size_t size) { int res = POWER_SMALLEST; if (size == 0) return 0; while (size > slabclass[res].size) if (res++ == power_largest) /* won't fit in the biggest slab */ return 0; return res; }
一般来说,都要比起评估的值大40%左右(本来评估要1G内存,可能就要申请到1.4G内存了)。一个系统要申请多大的mc,需要根据实际情况评估mc的量。
- 版本号问题。一个详情页的页面用行缓存,把请求从数据库拦截掉。key是post_1。原来存放的结构:
{ "title": "测试", "id": 1 }
后来版本迭代。增加了一些属性:
{
"title": "测试",
"id": 1,
"content": "test aaaaaaa"
}
此时,如果key还是post_1的话,就有可能序列化失败从而抛异常。这种问题在研发、测试环境不容易出现,但是线上出现就麻烦了。要网维一个个的处理这种mc key,甚至直接flush_all整个mc。所以出现这种版本迭代要改变缓存结构的情况。一般增加版本号。比如说把key修改为:post_v2_1。