游戏缓存粗谈

原创
2015/08/05 19:21
阅读数 719

1.本地缓存和分布式缓存

       游戏服务器为了保证访问数据的性能需要将玩家的数据进行缓存,根据缓存的位置不一样,可以分为:本地jvm缓存和分布式缓存。之前做的2个游戏算是2种方式都接触过。

      本地jvm缓存:实现方式有多种,比较好的方式就是用一些第三方缓存,比如ehcache,提供了多种缓存策略,或者自己实现一个本地缓存,比如通过ConcurrentHashMap来实现。

      分布式缓存:之前有接触的redis,redis3.0之后也支持了服务器端实现集群。
      (更多http://my.oschina.net/OutOfMemory/blog/412408)

2.缓存与数据库

      有些游戏可能把缓存紧紧是当做一个缓存,get的时候先从数据中取然后放入缓存中,真正需要update,delete的时候都是直接操作数据库的,这种方式我觉得是比较折中的方式,既保证了一定的性能,有保证的安全性。

      另外一种情况就是所有东西都在缓存里面做,然后定期同步到数据库,这种情况就有一定的风险,导致缓存数据没有同步到数据库中。当然如果用的是redis之类的缓存,它本身就提供了2种持久化的功能,分别是RDB和AOF,其实RDB模式下也有可能出现数据的丢失。

      游戏公司感觉更多的是关注性能,有时候对数据安全这块不是特别注重,是能够容忍数据丢失(游戏回档),反正我是不是特别赞成这样,但事实就是好多公司都这样搞。

3.数据同步

        本地缓存的同步看到过2种方式的实现,一种是将所有的增删改操作按顺序放入队列,然后使用定时器去读取队列,将数据同步到数据库;另外一种是通过CRC32检查对象是否改变,然后进行同步。

下面看一个关于CRC32的一个实例

public class CRC32Util {

	/**
	 * 获取对象的crc值
	 * 
	 * @param object
	 *            实现Serializable接口的对象
	 * @return
	 */
	public static long getCRC(Object object) {
		long crc = 0;
		CRC32 crc32 = new CRC32();
		crc32.update(object2Byte(object));
		crc = crc32.getValue();
		return crc;
	}

	/**
	 * 讲可序列化对象转成字节数组
	 * 
	 * @param object
	 *            实现Serializable接口的对象
	 * @return
	 */
	public static byte[] object2Byte(Object object) {
		byte data[] = null;
		ObjectOutputStream out = null;
		ByteArrayOutputStream baos = null;
		try {
			baos = new ByteArrayOutputStream();
			out = new ObjectOutputStream(baos);
			out.writeObject(object);
			data = baos.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (baos != null) {
				try {
					baos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return data;
	}
}
public class BagCache implements Serializable {
	private static final long serialVersionUID = 1L;
	private long id;
	private int pid;
	private int num;

	public BagCache(long id, int pid, int num) {
		this.id = id;
		this.pid = pid;
		this.num = num;
	}
        //get.set方法
}
public class RoleCache implements Serializable {

	private static final long serialVersionUID = 1L;
	private int roleId;
	private String roleName;
	private Map<Long, BagCache> bagMap = new HashMap<Long, BagCache>();

	public RoleCache(int roleId, String roleName) {
		this.roleId = roleId;
		this.roleName = roleName;
	}

	public BagCache getBag(long id) {
		return bagMap.get(id);
	}

	public void putBag(BagCache bag) {
		bagMap.put(bag.getId(), bag);
	}
        //get.set方法
}
public class CRC32Test {

	public static void main(String[] args) {
		RoleCache role = new RoleCache(1, "a1");
		BagCache bag = new BagCache(1, 1, 1);
		role.putBag(bag);

		long crc = CRC32Util.getCRC(role);
		System.out.println("第一次:" + crc);

		role.setRoleName("a2");
		crc = CRC32Util.getCRC(role);
		System.out.println("第二次:" + crc);

		bag.setNum(2);
		crc = CRC32Util.getCRC(role);
		System.out.println("第三次:" + crc);

	}
}

通过定时检查可序列化的内存对象的CRC32的值,来判断是否需要同步当前对象。

       关于分布式缓存,因为本身自己是支持持久化的,很多情况下不需要自己去做持久化了,比如mogodb;当然有个问题就是像redis和mogodb这种缓存数据库,其实并不是特别方便我们去做数据统计的,所以我们也经常使用redis+mysql的方式,将数据也同步到mysql中,一方面是为了安全,一方面是方便做数据统计。redis同步到mysql思路也差不多,将key的增删改操作放入redis的set列表中,然后由定时器去读取set列表,进行同步mysql。

展开阅读全文
加载中
点击加入讨论🔥(1) 发布并加入讨论🔥
打赏
1 评论
8 收藏
0
分享
返回顶部
顶部