前言


代码逻辑:把mysql中的玩家分数表的数据导到redis中,使用redis中的有序集合zset来实现数据递减排行并返回结果(排行榜)

我们要明确用redis做排行榜的意义,如果在mysql中有一张游戏的玩家分数表,那么我们用简单的sql语句就能实现数据排行的功能,为什么还要用redis做数据排行?首先mysql等关系型数据库做大数据量的数据查询排序是有性能瓶颈的,而redis是基于内存的键值数据库,其查询、排序的运算速度要比mysql等关系型数据库要快得多;并且redis中内置了一个有序集合(zset)的数据结构,zset里面的元素是唯一的,按分数字段从小到大排序,是有序的,非常适合用于实现排行榜功能。

准备

启动mysql,在mysql中准备一张玩家表,表中存储着玩家的名字和相应的分数

App 排行榜数据只用Redis 保险吗 redis 做排行榜_连接池

启动redis

启动时选择关闭保护模式,也可以启动之前到redis.conf注释掉bind 127.0.0.1并把protected-mode置为no)

cd /usr/local/redis-5.0.4/src
./redis-server --protected-mode no

代码实现

项目结构

App 排行榜数据只用Redis 保险吗 redis 做排行榜_mysql_02

pom.xml

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.14</version>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.15</version>
</dependency>

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.0.1</version>
</dependency>
  </dependencies>

mysql连接池配置文件

在使用6.0以上的jdbc连接驱动连接mysql时,需要在url里指定时区(我的mysql和jdbc都是8.0以上的)

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/new_schema?serverTimezone=UTC
username=root
password=root
initialSize=3
maxActive=20
maxWait=3000

initialSize:连接池启动时要初始化多少个连接。
maxWait:连接池出借连接的最长期限,单位是毫秒,比如设为10000ms,客户端从连接池获取(借出)一个连接后,10000毫秒没有归还(return),则连接池会抛出异常。

maxActive:配置连接池同时能维持的最大连接数,如果客户端理论上需要100个连接,则这个值设为100

jedis连接池配置文件

host=192.168.100.10
port=6379
maxTotal=50
maxIdle=10

host和post为redis服务所在的ip地址和端口号

maxIdle的意思是连接池中空闲连接的最大数量

maxTotal是连接池中总连接的最大数量

JDBCUtils.java

实现mysql连接池

package redis_demo.demo2;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
 
public class JDBCUtils {
    private static DataSource ds;
 
    static {
        try {
        	//加载连接池的配置文件
            Properties pro = new Properties();
            pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //生成连接池
            ds = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
 
    public static DataSource getDataSource() {
        return ds;
    }
 
    public static void close(ResultSet rs, PreparedStatement pstmt, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
 
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
 
        if (conn != null) {
            try {
                // 关闭连接,用完就关闭
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

RedisUtils.java

实现jedis连接池

package redis_demo.demo2;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class RedisUtils {
   private static JedisPool jedisPool;

   static {
	   //加载redis连接池的配置文件
       InputStream is = RedisUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
       Properties pro = new Properties();
       try {
           pro.load(is);
       } catch (IOException e) {
           e.printStackTrace();
       }
       
       JedisPoolConfig config = new JedisPoolConfig();
       config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
       config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
	   //生成连接池对象
       jedisPool = new JedisPool(config, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")));
   }

   public static Jedis getJedis() {
       return jedisPool.getResource();
   }
}

User.java

用于封装数据元素形成user对象

package redis_demo.demo2;

//创建user类,封装数据元素形成对象
public class User {
	public int id;
	public String name;
	public int gold;
	
	public String toString() {
        return "User{" +
                "id=" + id +
                ", uname='" + name + '\'' +
                ", gold=" + gold +
                '}';
	}
}

MysqlConn.java

从mysql连接池中获取连接,连接mysql,根据传入的id获得相应的数据元素封装成user对象

package redis_demo.demo2;

import java.awt.print.Printable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import redis_demo.demo2.JDBCUtils;

public class MysqlConn {
	public static User getUserByUid(int paramUid) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        try {
        	//从连接池获取连接
            conn = JDBCUtils.getConnection();
            //执行sql语句
            String sql = "select * from persons where id = ?";
            pstmt = conn.prepareStatement(sql);
            //设定sql语句的参数
            pstmt.setInt(1, paramUid);
            //返回结果集
            rs = pstmt.executeQuery();
            
            User user = null;
            if (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int gold = rs.getInt("gold");
 
                user = new User();
                user.id = id;
                user.name = name;
                user.gold = gold;
            }
            System.out.println(user);
            return user;
        } catch (Exception e) {
            return null;
        } finally {
            JDBCUtils.close(rs, pstmt, conn);
        }
	}
}

RedisConn.java

通过jedis连接redis,添加成员到有序集合,并从有序集合中返回按分数递减排序的成员集合

package redis_demo.demo2;

import java.util.Set;
import redis.clients.jedis.Jedis;

public class RedisConn {
	//添加成员到有序集合
    public static void addToRank(String name, int gold) {
        Jedis jedis = RedisUtils.getJedis();
        jedis.zadd("rank", gold, name);
        jedis.close();
    }
 
    //从有序集合中获取按分数递减排序的成员集合
    public static Set<String> getRank() {
        Jedis jedis = RedisUtils.getJedis();
        Set<String> rankSet = jedis.zrevrange("rank", 0, -1);
        jedis.close();
        return rankSet;
    }
}

App.java

项目运行主类

循环persons表中的每一行数据,添加到redis中的有序集合中,再从redis的有序集合中按分数递减顺序返回排序好的成员集合

package redis_demo.demo2;

import java.util.Set;

public class App {
   public static void main(String[] args) {
	   //循环persons表中的每一行数据,添加到redis中的有序集合中
       for (int i = 1; i <= 3; i++) {
           User user = MysqlConn.getUserByUid(i);
           RedisConn.addToRank(user.name, user.gold);
       }

       Set<String> rankSet = null;
       
       //从redis的有序集合中按分数递减顺序返回排序好的成员集合
       rankSet = RedisConn.getRank();

       System.out.println(rankSet);
   }
}

项目运行结果

最后返回来的是按gold字段降序排序好的成员集合

App 排行榜数据只用Redis 保险吗 redis 做排行榜_数据库_03