【发布时间】:2017-07-30 23:32:24
【问题描述】:
我在加载/存储 MySQL 数据时遇到问题,这是我的代码
public void loadPlayer(Player p) {
if (isPlayerInDataBase(p)) {
Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), new Runnable() {
@Override
public void run() {
Connection connection = sql.getConnection();
try {
PreparedStatement select = connection
.prepareStatement("SELECT * FROM `MurderData` WHERE playername='" + p.getName() + "'");
ResultSet result = select.executeQuery();
if (getPlayerData(p) != null) {
while (result.next()) {
getPlayerData(p).setdeaths(result.getInt("deaths"));
getPlayerData(p).setkills(result.getInt("kills"));
getPlayerData(p).setwins(result.getInt("wins"));
getPlayerData(p).setlose(result.getInt("loses"));
getPlayerData(p).setscore(result.getInt("score"));
getPlayerData(p).setcoins(result.getInt("coins"));
}
CloseResultSet(result);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
});
} else {
Connection connection = sql.getConnection();
Bukkit.getScheduler().runTaskAsynchronously(Main.getInstance(), new Runnable() {
@Override
public void run() {
try {
PreparedStatement insert = connection.prepareStatement(
"INSERT INTO `MurderData` (playername, wins, deaths, loses, kills, coins, score) VALUES (?, ?, ?, ?, ?, ?, ?)");
insert.setString(1, p.getName());
insert.setInt(2, 0);
insert.setInt(3, 0);
insert.setInt(4, 0);
insert.setInt(5, 0);
insert.setInt(6, 0);
insert.setInt(7, 0);
insert.executeUpdate();
ClosePreparedStatement(insert);
} catch (SQLException e) {
e.printStackTrace();
}
}
public boolean isPlayerInDataBase(Player p) {
Connection connection = sql.getConnection();
try {
PreparedStatement select = connection
.prepareStatement("SELECT * FROM `MurderData` WHERE playername='" + p.getName() + "'");
ResultSet result = select.executeQuery();
if (result.next()) {
CloseResultSet(result);
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
所以基本上问题是,我应该在更新 MySQL 数据后关闭准备好的语句吗?我应该让它有一个延迟,比如在 5 秒后关闭它吗?我可以优化此代码以使其更好吗?
数据库中的播放器可以吗?
这是我的两种关闭方法:
public void CloseResultSet(ResultSet s) {
new BukkitRunnable() {
@Override
public void run() {
if (s != null) {
try {
s.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.runTaskLater(this, 20 * getConfig().getInt("close-sql-statements-after"));
}
public void ClosePreparedStatement(PreparedStatement s) {
new BukkitRunnable() {
@Override
public void run() {
if (s != null) {
try {
s.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.runTaskLater(this, 20 * getConfig().getInt("close-sql-statements-after"));
}
更新:这是第二个问题,第一个问题是小滞后尖峰第二个问题是在第 252 行存储数据
public void setSQLData(Player p, int kills, int deaths, int loses, int wins, int coins, int score) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
Connection connection = plugin.sql.getConnection();
try {
PreparedStatement insert = connection.prepareStatement(
"INSERT INTO `MurderData` (playername, wins, deaths, loses, kills, coins, score) VALUES (?, ?, ?, ?, ?, ?, ?)");
insert.setString(1, p.getName());
insert.setInt(2, wins);
insert.setInt(3, deaths);
insert.setInt(4, loses);
insert.setInt(5, kills);
insert.setInt(6, coins);
insert.setInt(7, score);
insert.executeUpdate();// error line 252
plugin.ClosePreparedStatement(insert);
} catch (SQLException e) {
e.printStackTrace();
}
}
});
}
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at java.lang.reflect.Constructor.newInstance(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.Util.handleNewInstance(Util.java:407)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1116)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3082)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2968)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3516)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1986)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2140)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2626)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2111)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2407)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2325)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2310)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at me.joseph.murder.api.MurderAPI$1.run(MurderAPI.java:252)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftTask.run(CraftTask.java:71)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at org.bukkit.craftbukkit.v1_8_R3.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:53)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at java.lang.Thread.run(Unknown Source)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2529)
[22:31:18] [Craft Scheduler Thread - 71/WARN]: at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2979)
【问题讨论】:
-
不要以这种方式运行它,与 Bukkit 的主线程同步,而是在异步线程中运行它,使其成为后台。它不应该锁定您的服务器,因此不会造成延迟
-
另外,在
isPlayerInDataBase(Player p)中运行查询,然后如果他在数据库中再次运行完全相同的查询似乎有点多余。只需使用您在检查期间获得的数据即可。 -
这可能完全不符合实际,但另一种方法(除了 Kerooker 和 Solarflare 提到的两种方法)可以帮助解决延迟问题(在系统资源允许的情况下)缓存最近离开的玩家,所以如果他们很快就会回来(比如重新登录),您不必再次获取他们的数据。现在,如果您有系统资源允许,这只会使事情变得更快,如果没有,那么这可能会使事情变慢。现在缓存是一种常见的做法;但是,我不知道这对于 mysql 或 bukkit/minecraft 有多真实。
-
基本上,我在这里关注这个教程,spigotmc.org/wiki/asynchronously-working-with-a-database,关于第二条评论,你的意思是我需要检查一次?就像如果玩家在数据库中保存在配置中?,我目前与当前代码没有太多滞后,但主要问题是关闭准备好的语句和结果集导致错误,如关闭结果集后无法保存/加载数据和准备好的统计数据,这就是为什么我使用该方法在 5 秒后关闭它们但仍然会导致错误(不是为我),而是为使用该插件的人。
-
更新增加了一个问题,我的主要问题是存储数据,第二个问题是小滞后峰值。