目錄
- 排行榜的背景
- 排行榜數據庫設計
- 排行榜代碼簡易實現
排行榜的背景
排行榜功能是一個很普遍的需求。使用 Redis 中有序集合(zset)的特性來實現排行榜是又好又快的選擇。一般排行榜都是有實效性的,比如“用戶積分榜”,遊戲中活躍度排行榜,遊戲裝備排行榜等。
排行榜數據庫設計
面臨的問題:
數據庫設計複雜,併發數較高,數據要求實時性高
簡單談一下設計數據庫的注意點:
1、表設計過程中應該注意的點即數據類型
1)字節更小的通常更好
控制字節長度
2)使用合適的數據類型
如tinyint只佔8個位,char(1024)與varchar(1024)的對比,char用於類似定長數據存儲比varchar節省空間,如:uuid(32),可以用char(32).
3)儘量避免NULL建議使用NOT NULL DEFAULT ‘’
NULL的列會讓索引統計和值比較都更復雜。可爲NULL的列會佔據更多的磁盤空間,在Mysql中也需要更多複雜的處理程序
2、索引設計過程中應該注意的點
1)選擇唯一性索引
唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄,保證物理上面唯一
2)爲經常需要排序、分組和聯合操作的字段建立索引
經常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作會浪費很多時間
3)常作爲查詢條件的字段建立索引
如果某個字段經常用來做查詢條件,那麼該字段的查詢速度會影響整個表的查詢速度
4)數據少的地方不必建立索引**
本文設計簡易三張表:
數據庫表
score_flow(積分流水錶)
user_score(用戶積分表總表)
sys_user(用戶信息表)設計,用於實現兩個功能:
1)查top100
2)查用戶的排名
排行榜代碼簡易實現
本文環境搭建參考與Springboot與Redis Cache深度整合
工程結構:
1.sql腳本
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `score_flow`
-- ----------------------------
#積分流水錶
DROP TABLE IF EXISTS `score_flow`;
CREATE TABLE `score_flow` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`score` bigint(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`user_name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for `sys_user`
-- ----------------------------
#用戶表
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', '高晨曦');
INSERT INTO `sys_user` VALUES ('2', '瑞燊兒');
INSERT INTO `sys_user` VALUES ('3', '張飛');
INSERT INTO `sys_user` VALUES ('4', '馬超');
INSERT INTO `sys_user` VALUES ('5', '關羽');
-- ----------------------------
-- Table structure for `user_score`
-- ----------------------------
#用戶得分表
DROP TABLE IF EXISTS `user_score`;
CREATE TABLE `user_score` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`user_score` bigint(11) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
2.mapper採用的mybatis.generator.plugin反向生成的,這裏不列舉如何生成的,mapper包下存放以下代碼類
ScoreFlowMapper相關代碼
package com.gcxzflgl.redis.mapper;
public interface ScoreFlowMapper {
@SelectProvider(type=ScoreFlowSqlProvider.class, method="countByExample")
int countByExample(ScoreFlowExample example);
@DeleteProvider(type=ScoreFlowSqlProvider.class, method="deleteByExample")
int deleteByExample(ScoreFlowExample example);
@Delete({
"delete from score_flow",
"where id = #{id,jdbcType=INTEGER}"
})
int deleteByPrimaryKey(Integer id);
@Insert({
"insert into score_flow (score, user_id, ",
"user_name)",
"values (#{score,jdbcType=BIGINT}, #{userId,jdbcType=INTEGER}, ",
"#{userName,jdbcType=VARCHAR})"
})
@Options(useGeneratedKeys=true,keyProperty="id")
int insert(ScoreFlow record);
@InsertProvider(type=ScoreFlowSqlProvider.class, method="insertSelective")
@Options(useGeneratedKeys=true,keyProperty="id")
int insertSelective(ScoreFlow record);
@SelectProvider(type=ScoreFlowSqlProvider.class, method="selectByExample")
@Results({
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="score", property="score", jdbcType=JdbcType.BIGINT),
@Result(column="user_id", property="userId", jdbcType=JdbcType.INTEGER),
@Result(column="user_name", property="userName", jdbcType=JdbcType.VARCHAR)
})
List<ScoreFlow> selectByExample(ScoreFlowExample example);
@Select({
"select",
"id, score, user_id, user_name",
"from score_flow",
"where id = #{id,jdbcType=INTEGER}"
})
@Results({
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="score", property="score", jdbcType=JdbcType.BIGINT),
@Result(column="user_id", property="userId", jdbcType=JdbcType.INTEGER),
@Result(column="user_name", property="userName", jdbcType=JdbcType.VARCHAR)
})
ScoreFlow selectByPrimaryKey(Integer id);
@UpdateProvider(type=ScoreFlowSqlProvider.class, method="updateByExampleSelective")
int updateByExampleSelective(@Param("record") ScoreFlow record, @Param("example") ScoreFlowExample example);
@UpdateProvider(type=ScoreFlowSqlProvider.class, method="updateByExample")
int updateByExample(@Param("record") ScoreFlow record, @Param("example") ScoreFlowExample example);
@UpdateProvider(type=ScoreFlowSqlProvider.class, method="updateByPrimaryKeySelective")
int updateByPrimaryKeySelective(ScoreFlow record);
@Update({
"update score_flow",
"set score = #{score,jdbcType=BIGINT},",
"user_id = #{userId,jdbcType=INTEGER},",
"user_name = #{userName,jdbcType=VARCHAR}",
"where id = #{id,jdbcType=INTEGER}"
})
int updateByPrimaryKey(ScoreFlow record);
}
ScoreFlowSqlProvider 相關代碼
package com.gcxzflgl.redis.mapper;
public class ScoreFlowSqlProvider {
public String countByExample(ScoreFlowExample example) {
BEGIN();
SELECT("count(*)");
FROM("score_flow");
applyWhere(example, false);
return SQL();
}
public String deleteByExample(ScoreFlowExample example) {
BEGIN();
DELETE_FROM("score_flow");
applyWhere(example, false);
return SQL();
}
public String insertSelective(ScoreFlow record) {
BEGIN();
INSERT_INTO("score_flow");
if (record.getScore() != null) {
VALUES("score", "#{score,jdbcType=BIGINT}");
}
if (record.getUserId() != null) {
VALUES("user_id", "#{userId,jdbcType=INTEGER}");
}
if (record.getUserName() != null) {
VALUES("user_name", "#{userName,jdbcType=VARCHAR}");
}
return SQL();
}
public String selectByExample(ScoreFlowExample example) {
BEGIN();
if (example != null && example.isDistinct()) {
SELECT_DISTINCT("id");
} else {
SELECT("id");
}
SELECT("score");
SELECT("user_id");
SELECT("user_name");
FROM("score_flow");
applyWhere(example, false);
if (example != null && example.getOrderByClause() != null) {
ORDER_BY(example.getOrderByClause());
}
return SQL();
}
public String updateByExampleSelective(Map<String, Object> parameter) {
ScoreFlow record = (ScoreFlow) parameter.get("record");
ScoreFlowExample example = (ScoreFlowExample) parameter.get("example");
BEGIN();
UPDATE("score_flow");
if (record.getId() != null) {
SET("id = #{record.id,jdbcType=INTEGER}");
}
if (record.getScore() != null) {
SET("score = #{record.score,jdbcType=BIGINT}");
}
if (record.getUserId() != null) {
SET("user_id = #{record.userId,jdbcType=INTEGER}");
}
if (record.getUserName() != null) {
SET("user_name = #{record.userName,jdbcType=VARCHAR}");
}
applyWhere(example, true);
return SQL();
}
public String updateByExample(Map<String, Object> parameter) {
BEGIN();
UPDATE("score_flow");
SET("id = #{record.id,jdbcType=INTEGER}");
SET("score = #{record.score,jdbcType=BIGINT}");
SET("user_id = #{record.userId,jdbcType=INTEGER}");
SET("user_name = #{record.userName,jdbcType=VARCHAR}");
ScoreFlowExample example = (ScoreFlowExample) parameter.get("example");
applyWhere(example, true);
return SQL();
}
public String updateByPrimaryKeySelective(ScoreFlow record) {
BEGIN();
UPDATE("score_flow");
if (record.getScore() != null) {
SET("score = #{score,jdbcType=BIGINT}");
}
if (record.getUserId() != null) {
SET("user_id = #{userId,jdbcType=INTEGER}");
}
if (record.getUserName() != null) {
SET("user_name = #{userName,jdbcType=VARCHAR}");
}
WHERE("id = #{id,jdbcType=INTEGER}");
return SQL();
}
protected void applyWhere(ScoreFlowExample example, boolean includeExamplePhrase) {
if (example == null) {
return;
}
String parmPhrase1;
String parmPhrase1_th;
String parmPhrase2;
String parmPhrase2_th;
String parmPhrase3;
String parmPhrase3_th;
if (includeExamplePhrase) {
parmPhrase1 = "%s #{example.oredCriteria[%d].allCriteria[%d].value}";
parmPhrase1_th = "%s #{example.oredCriteria[%d].allCriteria[%d].value,typeHandler=%s}";
parmPhrase2 = "%s #{example.oredCriteria[%d].allCriteria[%d].value} and #{example.oredCriteria[%d].criteria[%d].secondValue}";
parmPhrase2_th = "%s #{example.oredCriteria[%d].allCriteria[%d].value,typeHandler=%s} and #{example.oredCriteria[%d].criteria[%d].secondValue,typeHandler=%s}";
parmPhrase3 = "#{example.oredCriteria[%d].allCriteria[%d].value[%d]}";
parmPhrase3_th = "#{example.oredCriteria[%d].allCriteria[%d].value[%d],typeHandler=%s}";
} else {
parmPhrase1 = "%s #{oredCriteria[%d].allCriteria[%d].value}";
parmPhrase1_th = "%s #{oredCriteria[%d].allCriteria[%d].value,typeHandler=%s}";
parmPhrase2 = "%s #{oredCriteria[%d].allCriteria[%d].value} and #{oredCriteria[%d].criteria[%d].secondValue}";
parmPhrase2_th = "%s #{oredCriteria[%d].allCriteria[%d].value,typeHandler=%s} and #{oredCriteria[%d].criteria[%d].secondValue,typeHandler=%s}";
parmPhrase3 = "#{oredCriteria[%d].allCriteria[%d].value[%d]}";
parmPhrase3_th = "#{oredCriteria[%d].allCriteria[%d].value[%d],typeHandler=%s}";
}
StringBuilder sb = new StringBuilder();
List<Criteria> oredCriteria = example.getOredCriteria();
boolean firstCriteria = true;
for (int i = 0; i < oredCriteria.size(); i++) {
Criteria criteria = oredCriteria.get(i);
if (criteria.isValid()) {
if (firstCriteria) {
firstCriteria = false;
} else {
sb.append(" or ");
}
sb.append('(');
List<Criterion> criterions = criteria.getAllCriteria();
boolean firstCriterion = true;
for (int j = 0; j < criterions.size(); j++) {
Criterion criterion = criterions.get(j);
if (firstCriterion) {
firstCriterion = false;
} else {
sb.append(" and ");
}
if (criterion.isNoValue()) {
sb.append(criterion.getCondition());
} else if (criterion.isSingleValue()) {
if (criterion.getTypeHandler() == null) {
sb.append(String.format(parmPhrase1, criterion.getCondition(), i, j));
} else {
sb.append(String.format(parmPhrase1_th, criterion.getCondition(), i, j,criterion.getTypeHandler()));
}
} else if (criterion.isBetweenValue()) {
if (criterion.getTypeHandler() == null) {
sb.append(String.format(parmPhrase2, criterion.getCondition(), i, j, i, j));
} else {
sb.append(String.format(parmPhrase2_th, criterion.getCondition(), i, j, criterion.getTypeHandler(), i, j, criterion.getTypeHandler()));
}
} else if (criterion.isListValue()) {
sb.append(criterion.getCondition());
sb.append(" (");
List<?> listItems = (List<?>) criterion.getValue();
boolean comma = false;
for (int k = 0; k < listItems.size(); k++) {
if (comma) {
sb.append(", ");
} else {
comma = true;
}
if (criterion.getTypeHandler() == null) {
sb.append(String.format(parmPhrase3, i, j, k));
} else {
sb.append(String.format(parmPhrase3_th, i, j, k, criterion.getTypeHandler()));
}
}
sb.append(')');
}
}
sb.append(')');
}
}
if (sb.length() > 0) {
WHERE(sb.toString());
}
}
}
UserMapper相關代碼
package com.gcxzflgl.redis.mapper;
@Mapper
@Component
public interface UserMapper {
@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
void insert(User u);
@Update("update sys_user set user_name = #{userName} where id=#{id} ")
void update(User u);
@Delete("delete from sys_user where id=#{id} ")
void delete(@Param("id") String id);
@Select("select id,user_name from sys_user where id=#{id} ")
User find(@Param("id") String id);
//注:方法名和要UserMapper.xml中的id一致
List<User> query(@Param("userName") String userName);
@Delete("delete from sys_user")
void deleteAll();
}
UserScoreMapper 相關代碼
package com.gcxzflgl.redis.mapper;
public interface UserScoreMapper {
@SelectProvider(type=UserScoreSqlProvider.class, method="countByExample")
int countByExample(UserScoreExample example);
@DeleteProvider(type=UserScoreSqlProvider.class, method="deleteByExample")
int deleteByExample(UserScoreExample example);
@Delete({
"delete from user_score",
"where id = #{id,jdbcType=INTEGER}"
})
int deleteByPrimaryKey(Integer id);
@Insert({
"insert into user_score (user_id, user_score, ",
"name)",
"values (#{userId,jdbcType=INTEGER}, #{userScore,jdbcType=BIGINT}, ",
"#{name,jdbcType=VARCHAR})"
})
@Options(useGeneratedKeys=true,keyProperty="id")
int insert(UserScore record);
@InsertProvider(type=UserScoreSqlProvider.class, method="insertSelective")
@Options(useGeneratedKeys=true,keyProperty="id")
int insertSelective(UserScore record);
@SelectProvider(type=UserScoreSqlProvider.class, method="selectByExample")
@Results({
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="user_id", property="userId", jdbcType=JdbcType.INTEGER),
@Result(column="user_score", property="userScore", jdbcType=JdbcType.BIGINT),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR)
})
List<UserScore> selectByExample(UserScoreExample example);
@Select({
"select",
"id, user_id, user_score, name",
"from user_score",
"where id = #{id,jdbcType=INTEGER}"
})
@Results({
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="user_id", property="userId", jdbcType=JdbcType.INTEGER),
@Result(column="user_score", property="userScore", jdbcType=JdbcType.BIGINT),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR)
})
UserScore selectByPrimaryKey(Integer id);
@UpdateProvider(type=UserScoreSqlProvider.class, method="updateByExampleSelective")
int updateByExampleSelective(@Param("record") UserScore record, @Param("example") UserScoreExample example);
@UpdateProvider(type=UserScoreSqlProvider.class, method="updateByExample")
int updateByExample(@Param("record") UserScore record, @Param("example") UserScoreExample example);
@UpdateProvider(type=UserScoreSqlProvider.class, method="updateByPrimaryKeySelective")
int updateByPrimaryKeySelective(UserScore record);
@Update({
"update user_score",
"set user_id = #{userId,jdbcType=INTEGER},",
"user_score = #{userScore,jdbcType=BIGINT},",
"name = #{name,jdbcType=VARCHAR}",
"where id = #{id,jdbcType=INTEGER}"
})
int updateByPrimaryKey(UserScore record);
}
UserScoreSqlProvider 相關代碼
package com.gcxzflgl.redis.mapper;
public class UserScoreSqlProvider {
public String countByExample(UserScoreExample example) {
BEGIN();
SELECT("count(*)");
FROM("user_score");
applyWhere(example, false);
return SQL();
}
public String deleteByExample(UserScoreExample example) {
BEGIN();
DELETE_FROM("user_score");
applyWhere(example, false);
return SQL();
}
public String insertSelective(UserScore record) {
BEGIN();
INSERT_INTO("user_score");
if (record.getUserId() != null) {
VALUES("user_id", "#{userId,jdbcType=INTEGER}");
}
if (record.getUserScore() != null) {
VALUES("user_score", "#{userScore,jdbcType=BIGINT}");
}
if (record.getName() != null) {
VALUES("name", "#{name,jdbcType=VARCHAR}");
}
return SQL();
}
public String selectByExample(UserScoreExample example) {
BEGIN();
if (example != null && example.isDistinct()) {
SELECT_DISTINCT("id");
} else {
SELECT("id");
}
SELECT("user_id");
SELECT("user_score");
SELECT("name");
FROM("user_score");
applyWhere(example, false);
if (example != null && example.getOrderByClause() != null) {
ORDER_BY(example.getOrderByClause());
}
return SQL();
}
public String updateByExampleSelective(Map<String, Object> parameter) {
UserScore record = (UserScore) parameter.get("record");
UserScoreExample example = (UserScoreExample) parameter.get("example");
BEGIN();
UPDATE("user_score");
if (record.getId() != null) {
SET("id = #{record.id,jdbcType=INTEGER}");
}
if (record.getUserId() != null) {
SET("user_id = #{record.userId,jdbcType=INTEGER}");
}
if (record.getUserScore() != null) {
SET("user_score = #{record.userScore,jdbcType=BIGINT}");
}
if (record.getName() != null) {
SET("name = #{record.name,jdbcType=VARCHAR}");
}
applyWhere(example, true);
return SQL();
}
public String updateByExample(Map<String, Object> parameter) {
BEGIN();
UPDATE("user_score");
SET("id = #{record.id,jdbcType=INTEGER}");
SET("user_id = #{record.userId,jdbcType=INTEGER}");
SET("user_score = #{record.userScore,jdbcType=BIGINT}");
SET("name = #{record.name,jdbcType=VARCHAR}");
UserScoreExample example = (UserScoreExample) parameter.get("example");
applyWhere(example, true);
return SQL();
}
public String updateByPrimaryKeySelective(UserScore record) {
BEGIN();
UPDATE("user_score");
if (record.getUserId() != null) {
SET("user_id = #{userId,jdbcType=INTEGER}");
}
if (record.getUserScore() != null) {
SET("user_score = #{userScore,jdbcType=BIGINT}");
}
if (record.getName() != null) {
SET("name = #{name,jdbcType=VARCHAR}");
}
WHERE("id = #{id,jdbcType=INTEGER}");
return SQL();
}
protected void applyWhere(UserScoreExample example, boolean includeExamplePhrase) {
if (example == null) {
return;
}
String parmPhrase1;
String parmPhrase1_th;
String parmPhrase2;
String parmPhrase2_th;
String parmPhrase3;
String parmPhrase3_th;
if (includeExamplePhrase) {
parmPhrase1 = "%s #{example.oredCriteria[%d].allCriteria[%d].value}";
parmPhrase1_th = "%s #{example.oredCriteria[%d].allCriteria[%d].value,typeHandler=%s}";
parmPhrase2 = "%s #{example.oredCriteria[%d].allCriteria[%d].value} and #{example.oredCriteria[%d].criteria[%d].secondValue}";
parmPhrase2_th = "%s #{example.oredCriteria[%d].allCriteria[%d].value,typeHandler=%s} and #{example.oredCriteria[%d].criteria[%d].secondValue,typeHandler=%s}";
parmPhrase3 = "#{example.oredCriteria[%d].allCriteria[%d].value[%d]}";
parmPhrase3_th = "#{example.oredCriteria[%d].allCriteria[%d].value[%d],typeHandler=%s}";
} else {
parmPhrase1 = "%s #{oredCriteria[%d].allCriteria[%d].value}";
parmPhrase1_th = "%s #{oredCriteria[%d].allCriteria[%d].value,typeHandler=%s}";
parmPhrase2 = "%s #{oredCriteria[%d].allCriteria[%d].value} and #{oredCriteria[%d].criteria[%d].secondValue}";
parmPhrase2_th = "%s #{oredCriteria[%d].allCriteria[%d].value,typeHandler=%s} and #{oredCriteria[%d].criteria[%d].secondValue,typeHandler=%s}";
parmPhrase3 = "#{oredCriteria[%d].allCriteria[%d].value[%d]}";
parmPhrase3_th = "#{oredCriteria[%d].allCriteria[%d].value[%d],typeHandler=%s}";
}
StringBuilder sb = new StringBuilder();
List<Criteria> oredCriteria = example.getOredCriteria();
boolean firstCriteria = true;
for (int i = 0; i < oredCriteria.size(); i++) {
Criteria criteria = oredCriteria.get(i);
if (criteria.isValid()) {
if (firstCriteria) {
firstCriteria = false;
} else {
sb.append(" or ");
}
sb.append('(');
List<Criterion> criterions = criteria.getAllCriteria();
boolean firstCriterion = true;
for (int j = 0; j < criterions.size(); j++) {
Criterion criterion = criterions.get(j);
if (firstCriterion) {
firstCriterion = false;
} else {
sb.append(" and ");
}
if (criterion.isNoValue()) {
sb.append(criterion.getCondition());
} else if (criterion.isSingleValue()) {
if (criterion.getTypeHandler() == null) {
sb.append(String.format(parmPhrase1, criterion.getCondition(), i, j));
} else {
sb.append(String.format(parmPhrase1_th, criterion.getCondition(), i, j,criterion.getTypeHandler()));
}
} else if (criterion.isBetweenValue()) {
if (criterion.getTypeHandler() == null) {
sb.append(String.format(parmPhrase2, criterion.getCondition(), i, j, i, j));
} else {
sb.append(String.format(parmPhrase2_th, criterion.getCondition(), i, j, criterion.getTypeHandler(), i, j, criterion.getTypeHandler()));
}
} else if (criterion.isListValue()) {
sb.append(criterion.getCondition());
sb.append(" (");
List<?> listItems = (List<?>) criterion.getValue();
boolean comma = false;
for (int k = 0; k < listItems.size(); k++) {
if (comma) {
sb.append(", ");
} else {
comma = true;
}
if (criterion.getTypeHandler() == null) {
sb.append(String.format(parmPhrase3, i, j, k));
} else {
sb.append(String.format(parmPhrase3_th, i, j, k, criterion.getTypeHandler()));
}
}
sb.append(')');
}
}
sb.append(')');
}
}
if (sb.length() > 0) {
WHERE(sb.toString());
}
}
}
3.再biz包下創建RedisService工具方法和RangingService業務方法
RedisService工具類
package com.gcxzflgl.redis.biz;
@Service
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;
private static double size = Math.pow(2, 32);
/**
* 寫入緩存
*
* @param key
* @param offset 位 8Bit=1Byte
* @return
*/
public boolean setBit(String key, long offset, boolean isShow) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.setBit(key, offset, isShow);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 寫入緩存
*
* @param key
* @param offset
* @return
*/
public boolean getBit(String key, long offset) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.getBit(key, offset);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 寫入緩存
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 寫入緩存設置時效時間
*
* @param key
* @param value
* @return
*/
public boolean set(final String key, Object value, Long expireTime) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 批量刪除對應的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 刪除對應的value
*
* @param key
*/
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判斷緩存中是否有對應的value
*
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 讀取緩存
*
* @param key
* @return
*/
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
/**
* 哈希 添加
*
* @param key
* @param hashKey
* @param value
*/
public void hmSet(String key, Object hashKey, Object value) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put(key, hashKey, value);
}
/**
* 哈希獲取數據
*
* @param key
* @param hashKey
* @return
*/
public Object hmGet(String key, Object hashKey) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
return hash.get(key, hashKey);
}
/**
* 列表添加
*
* @param k
* @param v
*/
public void lPush(String k, Object v) {
ListOperations<String, Object> list = redisTemplate.opsForList();
list.rightPush(k, v);
}
/**
* 列表獲取
*
* @param k
* @param l
* @param l1
* @return
*/
public List<Object> lRange(String k, long l, long l1) {
ListOperations<String, Object> list = redisTemplate.opsForList();
return list.range(k, l, l1);
}
/**
* 集合添加
*
* @param key
* @param value
*/
public void add(String key, Object value) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
set.add(key, value);
}
/**
* 集合獲取
*
* @param key
* @return
*/
public Set<Object> setMembers(String key) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
return set.members(key);
}
/**
* 有序集合添加
*
* @param key
* @param value
* @param scoure
*/
public void zAdd(String key, Object value, double scoure) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
zset.add(key, value, scoure);
}
/**
* 有序集合獲取
*
* @param key
* @param scoure
* @param scoure1
* @return
*/
public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
redisTemplate.opsForValue();
return zset.rangeByScore(key, scoure, scoure1);
}
//第一次加載的時候將數據加載到redis中
public void saveDataToRedis(String name) {
double index = Math.abs(name.hashCode() % size);
long indexLong = new Double(index).longValue();
boolean availableUsers = setBit("availableUsers", indexLong, true);
}
//第一次加載的時候將數據加載到redis中
public boolean getDataToRedis(String name) {
double index = Math.abs(name.hashCode() % size);
long indexLong = new Double(index).longValue();
return getBit("availableUsers", indexLong);
}
/**
* 有序集合獲取排名
*
* @param key 集合名稱
* @param value 值
*/
public Long zRank(String key, Object value) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.rank(key,value);
}
/**
* 有序集合獲取排名
*
* @param key
*/
public Set<ZSetOperations.TypedTuple<Object>> zRankWithScore(String key, long start,long end) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
Set<ZSetOperations.TypedTuple<Object>> ret = zset.rangeWithScores(key,start,end);
return ret;
}
/**
* 有序集合添加
*
* @param key
* @param value
*/
public Double zSetScore(String key, Object value) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.score(key,value);
}
/**
* 有序集合添加分數
*
* @param key
* @param value
* @param scoure
*/
public void incrementScore(String key, Object value, double scoure) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
zset.incrementScore(key, value, scoure);
}
/**
* 有序集合獲取排名
*
* @param key
*/
public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithScore(String key, long start,long end) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeByScoreWithScores(key,start,end);
return ret;
}
/**
* 有序集合獲取排名
*
* @param key
*/
public Set<ZSetOperations.TypedTuple<Object>> reverseZRankWithRank(String key, long start, long end) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
Set<ZSetOperations.TypedTuple<Object>> ret = zset.reverseRangeWithScores(key, start, end);
return ret;
}
}
RangingService業務方法
package com.gcxzflgl.redis.biz;
@Service
public class RangingService implements InitializingBean {
private static final String RANKGNAME = "user_score";
private static final String SALESCORE = "sale_score_rank:";
@Autowired
private RedisService redisService;
@Autowired
private UserMapper userMapper;
@Autowired
private ScoreFlowMapper scoreFlowMapper;
@Autowired
private UserScoreMapper userScoreMapper;
/**
* 添加用戶積分
*
* @param uid
* @param score
*/
public void increSaleSocre(String uid, Integer score) {
User user = userMapper.find(uid);
if (user == null) {
return;
}
int uidInt = Integer.parseInt(uid);
long socreLong = Long.parseLong(score + "");
String name = user.getUserName();
String key = uid + ":" + name;
scoreFlowMapper.insertSelective(new ScoreFlow(socreLong, uidInt, name));
userScoreMapper.insertSelective(new UserScore(uidInt, socreLong, name));
redisService.incrementScore(SALESCORE, key, score);
}
public Map<String, Object> userRank(String uid, String name) {
Map<String, Object> retMap = new LinkedHashMap<>();
String key = uid + ":" + name;
Integer rank = redisService.zRank(SALESCORE, key).intValue();
Long score = redisService.zSetScore(SALESCORE, key).longValue();
retMap.put("userId", uid);
retMap.put("score", score);
retMap.put("rank", rank);
return retMap;
}
public List<Map<String, Object>> reverseZRankWithRank(long start, long end) {
Set<ZSetOperations.TypedTuple<Object>> setObj = redisService.reverseZRankWithRank(SALESCORE, start, end);
List<Map<String, Object>> mapList = setObj.stream().map(objectTypedTuple -> {
Map<String, Object> map = new LinkedHashMap<>();
map.put("userId", objectTypedTuple.getValue().toString().split(":")[0]);
map.put("userName", objectTypedTuple.getValue().toString().split(":")[1]);
map.put("score", objectTypedTuple.getScore());
return map;
}).collect(Collectors.toList());
return mapList;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("======enter init bean=======");
this.rankSaleAdd();
}
}
4.rest層放入對外訪問的接口地址訪問
package com.gcxzflgl.redis.rest;
@RestController
public class RankingController {
@Autowired
private RangingService rankingService;
/**
* 添加用戶積分
* @param uid
* @param score
* @return
*/
@RequestMapping("/sale/increScore")
public String increSaleScore(String uid, Integer score) {
rankingService.increSaleSocre(uid, score);
return "success";
}
/**
* 用戶排名查看
* @param uid
* @param name
* @return
*/
@RequestMapping("/sale/userScore")
public Map<String,Object> userScore(String uid,String name) {
return rankingService.userRank(uid,name);
}
/**
* topx
* @param start
* @param end
* @return
*/
@RequestMapping("/sale/top")
public List<Map<String,Object>> reverseZRankWithRank(long start,long end) {
return rankingService.reverseZRankWithRank(start,end);
}
}
5.resources/mapper放入UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gcxzflgl.redis.mapper.UserMapper">
<select id="query" resultType="com.gcxzflgl.redis.domain.User">
select id ,user_name
from sys_user
where 1=1
<if test="userName != null">
and user_name like CONCAT('%',#{userName},'%')
</if>
</select>
</mapper>
6.啓動項目測試
sql腳本中用戶表(sys_user)模擬的是存量用戶方便測試演示
用戶添加積分地址:
http://xxx.xxx.xx.xx:8080/sale/increScore?uid=1&score=2
http://xxx.xxx.xx.xx:8080/sale/increScore?uid=2&score=5
http://xxx.xxx.xx.xx:8080/sale/increScore?uid=3&score=10
http://xxx.xxx.xx.xx:8080/sale/increScore?uid=4&score=15
http://xxx.xxx.xx.xx:8080/sale/increScore?uid=5&score=30
用戶排行查看地址:
http://xxx.xxx.xx.xx:8080/sale/userScore?uid=3&name=張飛
{"userId":"3","score":17,"rank":2}
用戶積分前top10:
http://xxx.xxx.xx.xx:8080/sale/top?start=0&end=9
[{"userId":"5","userName":"關羽","score":40.0},{"userId":"4","userName":"馬超","score":25.0},{"userId":"3","userName":"張飛","score":17.0},{"userId":"2","userName":"瑞燊兒","score":10.0},{"userId":"1","userName":"高晨曦","score":5.0}]
細節之處處理的不夠好,只是爲了說明排行榜思想是什麼,至此本案例演示完畢!