本文参考自:(值得参考,线上可用)Mysql(jdbc连接池) + Redis(连接池)实现排行榜实战,感谢
代码逻辑:把mysql中的玩家分数表的数据导到redis中,使用redis中的有序集合zset来实现数据递减排行并返回结果(排行榜)
我们要明确用redis做排行榜的意义,如果在mysql中有一张游戏的玩家分数表,那么我们用简单的sql语句就能实现数据排行的功能,为什么还要用redis做数据排行?首先mysql等关系型数据库做大数据量的数据查询排序是有性能瓶颈的,而redis是基于内存的键值数据库,其查询、排序的运算速度要比mysql等关系型数据库要快得多;并且redis中内置了一个有序集合(zset)的数据结构,zset里面的元素是唯一的,按分数字段从小到大排序,是有序的,非常适合用于实现排行榜功能。
准备
启动mysql,在mysql中准备一张玩家表,表中存储着玩家的名字和相应的分数
启动redis
启动时选择关闭保护模式,也可以启动之前到redis.conf注释掉bind 127.0.0.1并把protected-mode置为no)
cd /usr/local/redis-5.0.4/src
./redis-server --protected-mode no
项目结构
pom.xml
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>3.8.1version>
<scope>testscope>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.14version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.15version>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.0.1version>
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 rankSet = null;
//从redis的有序集合中按分数递减顺序返回排序好的成员集合
rankSet = RedisConn.getRank();
System.out.println(rankSet);
}
}
项目运行结果
最后返回来的是按gold字段降序排序好的成员集合