表散列

公司数据库使用的是MySQL5.0.37-log,不支持分区,在数据量较大又没有使用分布式的情况下,我们通常的做法是在应用端实现表散列,用一个简单的hash算法将数据分散到多张表中。

 

以一个记录用户行为,判断用户在指定时间内行为是否有效的功能为例来说明,实现方式如下:

1.散列表的张数以及散列表的命名方式需要能够在外部的配置文件中实现可配置,在这里用一个简单的配置文件action.properties来实现

TABLE_HASH_SEED=100
TABLE_PREFIX=action_record_

2.建表时确定好索引

	private void createTable() throws IOException {
		Properties properties = new Properties();
		InputStream is = ActionDao.class.getClassLoader().getResourceAsStream("action.properties");
		properties.load(is);
		
		TABLE_HASH_SEED = Integer.parseInt(properties.getProperty("TABLE_HASH_SEED"));
		TABLE_PREFIX = properties.getProperty("TABLE_PREFIX");
		
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < TABLE_HASH_SEED; i++) {
			String tableName = TABLE_PREFIX + i;
			sb.append("CREATE TABLE IF NOT EXISTS `" + tableName + "` (");
			sb.append("`id` int(11) NOT NULL AUTO_INCREMENT,");
			sb.append("`source` bigint(20) NOT NULL,");
			sb.append("`target` bigint(20) NOT NULL,");
			sb.append("`action_type` varchar(255) default NULL,");
			sb.append("`time_` datetime DEFAULT NULL,");
			sb.append("PRIMARY KEY (`id`),");
			sb.append("KEY `action_index` (`source`,`target`)");
			sb.append(") ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;");
			
			jdbcTemplate.getJdbcOperations().execute(sb.toString());
			sb.delete(0, sb.length());
		}
	}

3.hash算法用一个简单的取模来实现,将数据分散在指定的N张表内,获取表明的方法

	/**
	 * 获取表名
	 * @param source	行为发起者
	 * @param target	行为接受者
	 * @return
	 */
	public String getTableName(long source, long target){
		if(source != 0){
			return TABLE_PREFIX + source % TABLE_HASH_SEED;
		}else{
			return TABLE_PREFIX + target % TABLE_HASH_SEED;
		}
	}

4.插入数据时,先确定需要插入的表名,然后再执行插入操作

	/**
	 * 插入记录
	 * @param source	行为发起者
	 * @param target	行为接受者
	 * @param actionType	行为类型
	 */
	public void addRecord(long source, long target, ActionType actionType) {
		String tableName = getTableName(source, target);
		Date now = new Date();
		String sql = "INSERT INTO " + tableName + " (source,target,action_type,time_) VALUES (?,?,?,?)";
		jdbcTemplate.update(sql, source, target, actionType.toString(), now);
	}

5判断行为是否合法的方法

	/**
	 * 判断用户本次操作是否合法
	 * @param source	行为发起方
	 * @param target	行为接受方
	 * @param actionType	行为类型
	 * @param legalCount	指定时间间隔内合法的操作次数
	 * @param interval	时间间隔,时间段为:<br>start:本次操作的时间 - 时间间隔	end:当前时间
	 * @param isRecord	若合法,是否增加相应的记录
	 * @return
	 */
	public boolean isIllegal (long source, long target, ActionType actionType, long legalCount, long interval, boolean isRecord){
		String tableName = actionDao.getTableName(source, target);
		try {
			lock.lock();
			long count = 0;
			String sql = "SELECT COUNT(*) FROM " + tableName + " WHERE 1=1";
			if(source != 0){
				sql += " AND source=" + source;
			}
			if(target != 0){
				sql += " AND target=" + target;
			}
			
			sql += " AND action_type='" + actionType.toString();
			
			if(interval == DAY){
				sql +=  "' AND DATE(time_)=CURDATE()";
			}else{
				sql += "' AND time_>DATE_SUB(NOW(),INTERVAL " + interval + " SECOND)";
			}

			count = actionDao.getJdbcTemplate().queryForLong(sql);
			if(count >= legalCount){
				return false;
			}else{
				if(isRecord){
					actionDao.addRecord(source, target, actionType);
				}
				return true;
			}
		} catch (DataAccessException e) {
			logger.info("check user action error", e);
			return false;
		} finally{
			lock.unlock();
		}
	}

当然,对于这样的功能,使用memcache这样的缓存来实现,速度自然不在一个数量级。在分表的情况下,稍微复杂一点的条件查询都会成为恶梦,夸多表的查询对于系统系能的影响也是无法接受的。有时间研究一下MySQL 5.1的分表,看看性能如何。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值