本篇文章來介紹如何藉助redis的zSet集合有序特性來幫助我們進行數據排序。
前言
我們在實際的開發過程中經常會遇到這樣一個問題,需要高頻次德對某個業務數據集進行某種規則的排序,如果是普通的排序,一般的方法就可以實現,我們這裏強調的是海量數據、高頻次的更新排序場景,如對上千萬、上億的數據進行排序操作。這時候需要我們花大量的時間和精力去尋找一種高效的排序算法,但往往需要各種因素的取捨,搞不好就要內存溢出,cpu爆棚等等負面影響,我們需要一種更好的算法,來解決這些問題。
原理
Redis支持多種數據類型,有String、List、Hash、Set、sorted set等等。
其中sorted Set有序集合和集合一樣也是string類型元素的集合,且不允許重複的成員。
不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。
有序集合的成員是唯一的,但分數(score)卻可以重複。
集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是O(1)。 集合中最大的成員數爲 232 - 1 (4294967295, 每個集合可存儲40多億個成員)。
通過對sortedSet特性分析:元素是String類型的集合、成員不重複、排序因子可以重複、hash表數據結構降低了操作複雜度、成員數量龐大。
看完這些優良的特性,是不是感覺如果不用來進行海量數據的排序都浪費了。
實踐
生成1000W條數據,並對這些數據進行排序,拿出前30條
public void sortedUserList(int size) {
Long startTime = System.currentTimeMillis();
logger.info("Begin program:{}", startTime);
while (size > 0) {
redisTemplate.opsForZSet().incrementScore("userSet", size, size);
size--;
}
Long numSize = redisTemplate.opsForZSet().size("userSet");
Set set = redisTemplate.opsForZSet().range("userSet", 0, 30);
logger.info("First 30 :[{}]", set);
logger.info("End program:{}[{}ms]", numSize, System.currentTimeMillis() - startTime);
}
這是簡單的數據結構,所以1000W條耶只用了2.7秒就插入了
對前面的10條數據的score進行修改
public void sortedUserList(int size) {
Long startTime = System.currentTimeMillis();
logger.info("Begin program:{}", startTime);
while (size > 0) {
redisTemplate.opsForZSet().incrementScore("userSet", size, 10);
size--;
}
Long numSize = redisTemplate.opsForZSet().size("userSet");
Set set = redisTemplate.opsForZSet().range("userSet", 0, 30);
logger.info("First 30 :[{}]", set);
logger.info("End program:{}[{}ms]", numSize, System.currentTimeMillis() - startTime);
}
再取出30條
由上可以看出,把元素的score修改之後,其對應的順序會自動排序。
在實際業務場景中可以靈活應用score的選擇來達到對應的排序目的。