Browse > Home

| 订阅RSS

Web中通用列表缓存的策略和实现。

十二月 23rd, 2009 | No Comments | Posted in cache, 软件架构

在当前的web网站中,缓存扮演越来越重要的角色,是应付网站高并发的重要技术手段,在缓存中,对于单一记录的缓存是最简单的,只要卡住所有的保存、删除、更新操作,然后向缓存控制中心提交对应的事件即可,而对于list的缓存通常是很难解决的,这里说说最近在弄的缓存,自我感觉良好。

第零种:系统中最新博客文章,策略是采用ttl,每隔5‘过期一次。

第一种:最简单的list缓存,策略是只要有数据更新要删除此类的所有list缓存。(目前hibernate的二级缓存的实现策略)

第二种: 条件缓存,譬如:某人的博客;博客系统中的用户和文章的关系,当某一用户更新了自己的文章,不应该删除其他人的博客缓存,策略用用户的id参入到缓存的key中

第N种: 非固定值多条件缓存,譬如:某人9:00~10:00 的博客文章列表;策略自定义缓存策略

谈谈我的思路,一样卡住所有的增、删、改操作,当发生增删该的时候,向缓存服务器发送事件。

public class CacheEvent<T> {

private Class<T> targetClass;

private T targetObject;

private Operation operation;

public CacheEvent(Class<T> targetClass, T targetObject, Operation operation) {

this.targetClass = targetClass;

this.targetObject = targetObject;

this.operation = operation;

}

对于第一种:那么我的缓存定义:

@Cacheable(clazz=TestMember.class)
public List<TestMember> getList();

通过方法和实体类产生一个缓存key, 只要发现此这样的实体发生增、删操作,即刻删除此缓存(关于如何删除,见下文)

对于第二种:我的缓存定义为:
@Cacheable(clazz=TestArticle.class,
namespaces={@CacheNsParameter(name=”member”,keyIndex=1)})
public List<TestArticle> getList(@CacheKeyParameter TestMember member,
@CacheKeParameter start,@CacheKeyParameter offset);
通过方法和namespace的定义产生一个缓存key,接受事件,当发生实体的增、删操作,并且TestArticle的member属性和@CacheKeyparameter 标记的属性相等时候,即刻删除此缓存。
如果缓存的key中参数为固定对象主键(譬如:ns中用户id,且文章表中的member_id 不会被update,要么insert ,要么delete,一旦设置就不会更改)或者参数和实体本身无关(譬如:分页参数start、offset)的时候,那么上述两种方法,基本上能满足所有缓存的需求。
对于第三种缓存用的相对少点,需要自定义,我的定义为:
@Cacheable(clazz=TestArticle.class,strategy=”XX”)
public List<TestArticle> getList(@CacheKeyParameter TestMember member,@CacheKeyParameter int month);
由自定义的xx服务类(我使用的是Tapestry5-IOC)来进行处理。
上面说到中很关键的一个东西就是怎么批量的高效删除缓存,譬如:在第二种中多次运行产生的cacheKey为
TestArticle_member_1_0_10   (后三位分别为member_id,start,offset)
TestArticle_member_1_1_30
TestArticle_member_1_40_30
TestArticle_member_2_0_10
TestArticle_member_2_1_10
TestArticle_member_2_30_10
。。。。。。。。。。
很多这样的key,如果要是每次把key记录一下,那这个代价也是挺高的,也要弄一个缓存的东西给记录下来,现在大家注意一下这个缓存的key,用户1增加文章的时候,实际就是删除所有key以TestArticle_member_1 开头的缓存即可,这样就省事多了,问题转换为怎么批量删除,在缓存中没有批量删除这个概念:( ,因为要遍历所有的key进行操作,效率自然高不了,挠头ing,我搜呀搜,无意中发现了官方的FAQ:http://code.google.com/p/memcached/wiki/FAQ#Namespaces 如果我们把TestArticle_member_1 加个版本号的话,不久好了吗,那么第一次时候产生的key为:

TestArticle_member_1_v0_0_10   (后四位分别为member_id,version,start,offset)
TestArticle_member_1_v0_1_30
TestArticle_member_1_v0_40_30
TestArticle_member_2_v0_0_10
TestArticle_member_2_v0_1_10
TestArticle_member_2_v0_30_10
当memeber 1增加文章的时候,我版本号加一,那么下次请求缓存时候key就变成了:

TestArticle_member_1_v1_0_10   (后四位分别为member_id,version,start,offset)
TestArticle_member_1_v1_1_30
TestArticle_member_1_v1_40_30
TestArticle_member_2_v0_0_10
TestArticle_member_2_v0_1_10
TestArticle_member_2_v0_30_10
cache没找到对应的缓存记录,那么执行方法,然后缓存起来,这样就变相实现了缓存的批量删除,有人会说,那岂不参生垃圾数据了,不用担心缓存系统一般都有LRU算法自动删除没用的数据。 :)
问题就豁然开朗了。其他情况也就迎刃而解了。
可以看看偶的测试用例:
约束、限制、建议:
1) 所有实体的缓存都是以实体本身向下延展。
2) 放入到缓存中的列表都是ID集合
3) 尽量以外建、主键、索引作为缓存的key
4) 使用hibernate的时候,不使用ManyToMany关系,用 one-to-many 和中间实体替代,譬如用户的角色列表。
有待完善:
1) 没次读取缓存的时候,需要分析方法中的Annotation,需要使用LocalCache的方式进行缓存起来。
2) 在更高量的访问时候,应该从memcache中读取的数据一部分放入本地缓存中。
3) 加入压力测试。
不过总算一个还算通用的列表缓存实现,通过前三种方式,系统的list缓存自定义方便了许多。借此拍砖,欢迎抛玉。

初步解决了Eclipse在Ubuntu上崩溃的问题

四月 30th, 2009 | No Comments | Posted in Linux

Eclipse在64bit的Linux机器上很不稳定,据说是Sun jdk的问题,不过今天采用了另外一种方法,用了一个下午的Eclipse没有死。

一句话就是用32bit的eclipse和32bit的jdk

下载eclipse的linux32bit版本,

运行: sudo apt-get install ia32-sun-java6-bin

偶的Eclipse启动参数(eclipse.ini)为:

-showsplash
org.eclipse.platform
-framework
plugins/org.eclipse.osgi_3.4.3.R34x_v20081215-1030.jar
-vm
/usr/lib/jvm/ia32-java-6-sun/bin/java
-vmargs
-Dosgi.requiredJavaVersion=1.5
-XX:MaxPermSize=256m
-Xms128m
-Xmx1024m
-XX:CompileCommand=exclude,org/eclipse/core/internal/dtree/DataTreeNode,forwardDeltaWith

目前为止,使用良好了,据说使用ibm的64bit 的jdk跑64bit的eclipse没问题,但是今天一直没有下载下来ibm的jdk,改天试试。

Tags: , ,

晒晒自己的svn备份做法

四月 4th, 2009 | No Comments | Posted in Linux

今天在google reader的分享中,看到东东分享的备份脚本,发现那个备份脚本最大不好的地方就是每次是一个全库备份,这要是上万次的提交,那个速度相当的受不了,现在把我一直使用的svn备份拿出来分享一下。

More »

Tags: , , ,

围绕在SVN(subversion)周围的GIT运用

四月 3rd, 2009 | No Comments | Posted in Linux, 开发环境

前一段因为家里面的宽带不是包月的,按照时间收费的,在开发的时候,老要提交东西,不提交的放在本地总感觉要和别人冲突了(谁都怕冲突呀,:)),然后就想到了git,现在还是挺火的,然后就试试,没想到等我尝试完毕之后再也放不下了,真是爱不释手,谈谈我的使用感受吧,git看起来很复杂,使用起来的确很方便。

More »

Tags: , , , ,

ubuntu 上安装atheros无线驱动

四月 3rd, 2009 | No Comments | Posted in Linux

现在使用的是Thinkpad的T400,里面内置的无线网卡是Atheros的(win下面显示好像是:thinkpad a/b),但是默认安装ubuntu 8.04并不支持这个程序,具体安装如下:

More »

Tags: , , ,

在Windows上搭建基于VIM 的Rails开发环境

四月 3rd, 2009 | No Comments | Posted in 开发环境

最近要修改OA系统的Rails网站的程序,要修改的美观,我日一直在linux下,好久没用windows了,得建立个开发环境,随记录下来,供以后使用:

  1. 安装ruby
    从ruby的官方网站下载1.8.6 One-Click Installer 下载地址为:http://rubyforge.org/frs/download.php/29263/ruby186-26.exe
    运行安装向导,安装过程中需要注意的是,要选中enable-rubygems,如下图:
  2. More »
    Tags: , , ,

[命令集3.8]通过Samba和Windows进行数据共享

四月 2nd, 2009 | No Comments | Posted in Linux

现在windows大行天下,难免要和windows的机器进行共享数据,通过linux中的samba协议能够获得windows的共享数据。
假设我们要访问计算机 smbserver 上的名叫 myshare 的 SMB 共享,在 window PC 上键入的地址是
\\smbserver\myshare\。我挂载到 /mnt/smbshare 上。注意 cifs 必须是 IP 或 DNS 名,不是 Windows 名
字。

More »

Tags: , , , ,

[命令集3.7] 立刻创建swap分区

三月 24th, 2009 | No Comments | Posted in Linux

假设你需要很多的 swap 空间 (即刻),如一个 2GB 文件 /swap2gb (只限 Linux)。

# dd if=/dev/zero of=/swap2gb bs=1024k count=2000
# mkswap /swap2gb                    # 创建交换区
# swapon /swap2gb                    # 激活这个 swap。现在可以使用了
# swapoff /swap2gb                   # 当使用完毕,释放这个 swap
# rm /swap2gb
Tags: , ,

[Linux秘籍31] PS3 - 在shell脚本中的提示符

三月 24th, 2009 | No Comments | Posted in Linux

在shell脚本中,你能够定义一个自定义的提示符,举个例子如下:
当没有定义PS3的时候

jcai@jcai-notebook:~/tmp$ cat ps3.sh 
  select i in mon tue wed exit
  do
     case $i in
        mon) echo "Monday";;
        tue) echo "Tuesday";;
        wed) echo "Wednesday";;
        exit) exit;;
     esac
  done
jcai@jcai-notebook:~/tmp$ ./ps3.sh 
1) mon
2) tue
3) wed
4) exit
#? 1
Monday
#? 2
Tuesday
#? 4

当定义了PS3的时候如下:

jcai@jcai-notebook:~/tmp$ cat ps3.sh
  PS3="请选择 1-4:"
  select i in mon tue wed exit
  do
     case $i in
        mon) echo "Monday";;
        tue) echo "Tuesday";;
        wed) echo "Wednesday";;
        exit) exit;;
     esac
  done
jcai@jcai-notebook:~/tmp$ ./ps3.sh 
1) mon
2) tue
3) wed
4) exit
请选择 1-42
Tuesday
请选择 1-44
Tags: ,

[命令集3.6]挂载/重挂载一个文件系统

三月 23rd, 2009 | No Comments | Posted in Hacks, Linux

举个 cdrom 的例子。如果已经列于 /etc/fstab 中:

 # mount /cdrom

或在 /dev/ 中查找设备,亦或使用 dmesg 命令
FreeBSD

 # mount -v -t cd9660 /dev/cd0c /mnt # cdrom
 # mount_cd9660 /dev/wcd0c /cdrom      # 另外一个方法
 # mount -v -t msdos /dev/fd0c /mnt # 软驱

/etc/fstab 中的一条:

 # Device                 Mountpoint      FStype  Options         Dump    Pass#
 /dev/acd0                /cdrom          cd9660  ro,noauto       0       0

要允许用户做这些,可以这么做:

 # sysctl vfs.usermount=1   # 或者在 /etc/sysctl.conf 中插入一条 "vfs.usermount=1"

Linux

 # mount -t auto /dev/cdrom /mnt/cdrom # 典型的 cdrom 挂载命令
 # mount /dev/hdc -t iso9660 -r /cdrom # IDE
 # mount /dev/sdc0 -t iso9660 -r /cdrom # SCSI

/etc/fstab 中的条目:
/dev/cdrom /media/cdrom subfs noauto,fs=cdfss,ro,procuid,nosuid,nodev,exec 0 0
用 Linux 挂载一个 FreeBSD 分区
用 fdisk 查找分区号,这通常是 root 分区,但也可能是其他 BSD slice。如果 FreeBSD 有许多 slice,他们
不列于同一个 fdisk 分区表中,但可见于 /dev/sda* 或 /dev/hda* 中。

 # fdisk /dev/sda                      # 查找 FreeBSD 分区
 /dev/sda3 *          5357         7905    20474842+ a5 FreeBSD
 # mount -t ufs -o ufstype=ufs2,ro /dev/sda3 /mnt
 /dev/sda10 = /tmp; /dev/sda11 /usr # 其他 slice

重挂载
不用卸载一个设备来重挂载。 对 fsck 来说是必须的。举个例子:

 # mount -o remount,ro /               # Linux
 # mount -o ro /                       # FreeBSD

从 cdrom 拷贝原始数据进一个 iso 映像文件:

# dd if=/dev/cd0c of=file.iso
Tags: , ,