最代碼廣告位
最代碼官方的gravatar頭像
最代碼官方2015-12-12 19:56:20

最代碼網站用戶私信列表采用mysql union查詢優化為Redis查詢的經驗和相關代碼片段分享

由于用戶和私信的數據量逐漸增加,查詢用戶和其他用戶的私信合并排重排序的sql語法給mysql帶來了很大的壓力,springdata jpa的hql查詢語法如下:

select id from (select id,target_id,case when user_id=?1 and type=?2 then 'sender'  else 'receiver' end flag from javaniu_post where user_id=?1 and type=?2  union select id,user_id,case when user_id=?1	then 'sender'  else 'receiver' end flag from javaniu_post where target_id=?1 and type=?2  order by id desc) as ret group by target_id order by id desc

在mysql的slow查詢中經常會出現10s以上的記錄:

最代碼網站用戶私信列表采用mysql union查詢優化為Redis查詢的經驗和相關代碼片段分享

mysql查詢性能的瓶頸在于某個用戶和其他用戶之間交互越多,發送私信越多,mysql union查詢參與運算的集合數據量會越大,性能也會越來越低。

數據變化如下:

1.用戶A發給用戶B生成私信1

用戶A->用戶B->私信1

2.用戶A發給用戶C生成私信2

用戶A->用戶C->私信2

3.用戶A發給用戶E生成私信3

用戶A->用戶E->私信3

4.查詢用戶A的私信列表

私信3,私信2,私信1

5.用戶B發給用戶A生成私信4

用戶B->用戶A->私信4

6.查詢用戶A的私信列表

私信4,私信3,私信2

私信1因為有最新數據私信4的存在,所以不會返回。

所以迫切需要引入新的技術來解決該查詢的性能瓶頸,想到了流行的redis技術,于是修改設計如下:

模擬發私信的數據結構如下

127.0.0.1:6379> zadd uid1 1 2
(integer) 1
127.0.0.1:6379> zadd uid1 2 3
(integer) 1
127.0.0.1:6379> zadd uid1 3 5
(integer) 1
127.0.0.1:6379> zrevrange uid1 0 -1
1) "5"
2) "3"
3) "2"
127.0.0.1:6379> zrevrange uid1 0 -1 withscores
1) "5"
2) "3"
3) "3"
4) "2"
5) "2"
6) "1"
127.0.0.1:6379> zadd uid1 4 2
(integer) 0
127.0.0.1:6379> zrevrange uid1 0 -1 withscores
1) "2"
2) "4"
3) "5"
4) "3"
5) "3"
6) "2"
127.0.0.1:6379>

注意:把私信id做為score來做排序

相關代碼片段如下:

刪除或添加私信時:

String uid = t.getUserId() + "";
String type = ModuleConstants.POST_TYPE_MESSAGE + "";
String tgid = t.getTargetId() + "";
long id = t.getId();
long time = t.getCreateTime().getTime();

String po_uid_tp_tgid = String.format(
        RedisConstants.POST_USERID_TYPE_TARGETID, uid, type, tgid);
// from uid
String po_uid_tp = String.format(RedisConstants.POST_USERID_TYPE,
        uid, type);
// to uid
String po_tgid_tp = String.format(RedisConstants.POST_USERID_TYPE,
        tgid, type);
if (t.getStatus() == ModuleConstants.MODULE_STATUS_DELETED) {// 刪除私信同時要刪除redis
    zsetOps.remove(po_uid_tp_tgid, id + "");
    zsetOps.remove(po_uid_tp, tgid);
    zsetOps.remove(po_tgid_tp, uid);
} else {
    zsetOps.add(po_uid_tp_tgid, id + "", time);
    zsetOps.add(po_uid_tp, tgid, id);
    zsetOps.add(po_tgid_tp, uid, id);
}

查詢用戶的私信時:

String uid = userId + "";
		String tp = ModuleConstants.POST_TYPE_MESSAGE + "";
		String po_uid_tp = String.format(RedisConstants.POST_USERID_TYPE, uid,
				tp);

		int total = zsetOps.zCard(po_uid_tp).intValue();

		int ps = total / count + (total % count > 0 ? 1 : 0);
		if (page > ps) {
			page = ps;
		}
		int start = (page - 1) * count;
		int end = page * count - 1;

		Set<TypedTuple<String>> _ids = zsetOps.reverseRangeWithScores(
				po_uid_tp, start, end);

		List<Long> ids = new ArrayList<Long>();
		Iterator<TypedTuple<String>> iterator = _ids.iterator();
		while (iterator.hasNext()) {
			TypedTuple<String> _id = iterator.next();
			long id = _id.getScore().longValue();
			ids.add(id);
		}
		List<Post> ts = (List<Post>) findAllByIds(ids);
		Pageable pageable = new PageRequest(page - 1, count);
		Page<Post> _page = new PageImpl<Post>(ts, pageable, total);
		initBeans(_page.getContent());

redis查詢最代碼官方,id=1的所有私信列表,和mysql union運算的結果完全一致,但是性能可是天壤之別:

最代碼網站用戶私信列表采用mysql union查詢優化為Redis查詢的經驗和相關代碼片段分享
info的memory截圖和keyspace截圖

最代碼網站用戶私信列表采用mysql union查詢優化為Redis查詢的經驗和相關代碼片段分享

最代碼網站用戶私信列表采用mysql union查詢優化為Redis查詢的經驗和相關代碼片段分享

相關資料和代碼:

mysql sql查詢如何實現發私信用戶和其他用戶的列表?要求消重所有重復的用戶結果

最代碼網站的私信功能

最代碼廣告位

打賞

頂部客服微信二維碼底部
>掃描二維碼關注最代碼為好友掃描二維碼關注最代碼為好友
福彩3d组选020前后关系