JAVA MYSQL聊天性能问题,有100个用户
我正在尝试使用Javaservlet和mysql(innoDB引擎)以及jetty服务器开发一个客户机-服务器聊天应用程序。我使用jmeter测试了100个模拟用户同时点击服务器的连接代码,但我得到了40秒作为平均时间:(对于所有用户来说,以线程占用的最小时间(2秒)和最大时间(80秒)连接)。我的连接数据库表具有以下结构:两列连接(用户,陌生人)我的servlet代码如下所示。我使用innoDB引擎进行行级锁定。我还使用显式写锁选择……进行内部事务更新。如果事务因死锁而回滚,我将循环该事务,直到它至少执行一次。一旦两个用户连接,他们将使用彼此随机生成的数据更新陌生人的列唯一号码 我使用c3p0连接池,打开至少100个线程,使用jetty,打开至少100个线程。 请帮助我确定找到它们所需的瓶颈或工具JAVA MYSQL聊天性能问题,有100个用户,java,mysql,performance,jdbc,transactions,Java,Mysql,Performance,Jdbc,Transactions,我正在尝试使用Javaservlet和mysql(innoDB引擎)以及jetty服务器开发一个客户机-服务器聊天应用程序。我使用jmeter测试了100个模拟用户同时点击服务器的连接代码,但我得到了40秒作为平均时间:(对于所有用户来说,以线程占用的最小时间(2秒)和最大时间(80秒)连接)。我的连接数据库表具有以下结构:两列连接(用户,陌生人)我的servlet代码如下所示。我使用innoDB引擎进行行级锁定。我还使用显式写锁选择……进行内部事务更新。如果事务因死锁而回滚,我将循环该事务,直
import java.io.*;
import java.util.*;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.naming.*;
import javax.sql.*;
public class connect extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws java.io.IOException {
String unumber=null;
String snumber=null;
String status=null;
InitialContext contxt1=null;
DataSource ds1=null;
Connection conxn1=null;
PreparedStatement stmt1=null;
ResultSet rs1=null;
PreparedStatement stmt2=null;
InitialContext contxt3=null;
DataSource ds3=null;
Connection conxn3=null;
PreparedStatement stmt3=null;
ResultSet rs3=null;
PreparedStatement stmt4=null;
ResultSet rs4=null;
PreparedStatement stmt5=null;
boolean checktransaction = true;
unumber=req.getParameter("number"); // GET THE USER's UNIQUE NUMBER
try {
contxt1 = new InitialContext();
ds1 =(DataSource)contxt1.lookup("java:comp/env/jdbc/user");
conxn1 = ds1.getConnection();
stmt1 = conxn1.prepareStatement("SELECT * FROM profiles WHERE number=?"); // GETTING USER DATA FROM PROFILE
stmt1.setString(1,unumber);
rs1 = stmt1.executeQuery();
if(rs1.next()) {
res.getWriter().println("user found in PROFILE table.........");
uage=rs1.getString("age");
usex=rs1.getString("sex");
ulocation=rs1.getString("location");
uaslmode=rs1.getString("aslmode");
stmt1.close();
stmt1=null;
conxn1.close();
conxn1 = null;
contxt3 = new InitialContext();
ds3 =(DataSource)contxt3.lookup("java:comp/env/jdbc/chat");
conxn3 = ds3.getConnection();
conxn3.setAutoCommit(false);
while(checktransaction) {
// TRANSACTION STARTS HERE
try {
stmt2 = conxn3.prepareStatement("INSERT INTO "+ulocation+" (user,stranger) VALUES (?,'')"); // INSERTING RECORD INTO LOCAL CHAT TABLE
stmt2.setString(1,unumber);
stmt2.executeUpdate();
stmt2.close();
stmt2 = null;
res.getWriter().println("inserting row into LOCAL CHAT TABLE.........");
System.out.println("transaction starting........."+unumber);
stmt3 = conxn3.prepareStatement("SELECT user FROM "+ulocation+" WHERE (stranger='' && user!=?) LIMIT 1 FOR UPDATE");
stmt3.setString(1,unumber); // SEARCHING FOR STRANGER
rs3=stmt3.executeQuery();
if (rs3.next()) { // stranger found
stmt4 = conxn3.prepareStatement("SELECT stranger FROM "+ulocation+" WHERE user=?");
stmt4.setString(1,unumber); //CHECKING FOR USER STATUS BEFORE CONNECTING TO STRANGER
rs4=stmt4.executeQuery();
if(rs4.next()) {
status=rs4.getString("stranger");
}
stmt4.close();
stmt4=null;
if(status.equals("")) { // user status is also null
snumber = rs3.getString("user");
stmt5 = conxn3.prepareStatement("UPDATE "+ulocation+" SET stranger=? WHERE user=?"); // CONNECTING USER AND STRANGER
stmt5.setString(1,snumber);
stmt5.setString(2,unumber);
stmt5.executeUpdate();
stmt5.setString(2,snumber);
stmt5.setString(1,unumber);
stmt5.executeUpdate();
stmt5.close();
stmt5=null;
}
} // end of stranger found
stmt3.close();
stmt3 = null;
conxn3.commit(); // TRANSACTION ENDING
checktransaction = false;
} // END OF TRY INSIDE WHILE
catch(java.sql.SQLTransactionRollbackException e) {
System.out.println("transaction restarted......."+unumber);
counttransaction = counttransaction+1;
}
} //END OF WHILE LOOP
conxn3.close();
conxn3 = null;
} // END OF USER FOUND IN PROFILE TABLE
} // end of try
catch(java.sql.SQLException sqlexe) {
try {conxn3.rollback();}
catch(java.sql.SQLException exe) {conxn3=null;}
sqlexe.printStackTrace();
res.getWriter().println("UNABE TO GET CONNECTION FROM POOL!");
}
catch(javax.naming.NamingException namexe) {
namexe.printStackTrace();
res.getWriter().println("DATA SOURCE LOOK UP FAILED!");
}
}
}
你有多少用户?你能先把他们全部装入内存并进行内存查找吗? 如果将DB层与表示层分开,则可以在不更改servlet的情况下更改它(因为它不应该关心数据来自何处) 如果使用Java内存,则每个用户所用的时间不应超过20毫秒
下面是一个测试,它在内存中创建一百万个配置文件,查找它们并创建聊天条目,然后删除。每个操作的平均时间为640纳秒(纳秒,或十亿分之一秒)
如果您需要更快的速度,可以考虑使用Trove4j,在这种情况下速度可能是Trove4j的两倍。考虑到速度可能足够快,我会尽量简化操作。您有多少用户?您能先将他们全部加载到内存中并进行内存查找吗? 如果将DB层与表示层分开,则可以在不更改servlet的情况下更改它(因为它不应该关心数据来自何处) 如果使用Java内存,则每个用户所用的时间不应超过20毫秒
下面是一个测试,它在内存中创建一百万个配置文件,查找它们并创建聊天条目,然后删除。每个操作的平均时间为640纳秒(纳秒,或十亿分之一秒)
如果您需要更快的速度,可以考虑使用Trove4j,在这种情况下速度可能是Trove4j的两倍。考虑到速度可能足够快,我会尽量让事情简单。您考虑过缓存读取和批处理写入吗?您考虑过缓存读取和批处理写入吗?我不确定如何实现这一点我希望任何人仅仅通过查看源代码就可以确定瓶颈在哪里 要找到瓶颈,您应该运行应用程序和负载测试,并附加一个探查器,如或。这将准确地告诉您在代码的每个区域花费了多少时间 任何人都可以通过查看您的代码来评论的唯一一件事是基本架构:
- 为什么要在每个doGet()上查找数据源
- 为什么要将事务用于看似无关的数据库插入和查询
- 首先,使用RDBMS支持聊天系统真的是最好的主意吗
- 为什么要在每个doGet()上查找数据源
- 为什么要将事务用于看似无关的数据库插入和查询
- 首先,使用RDBMS支持聊天系统真的是最好的主意吗
学习如何正确设置这些表,您应该会看到死锁计数和响应时间下降如果您的响应时间如此之高,您需要正确索引db表。根据您提供的时间,我假设这没有完成。您需要加快读写速度 查找执行计划以及如何读取它们。执行计划将向您显示索引是否/何时用于查询;是否对表执行搜索或扫描等。通过使用这些,您可以调整查询/索引/表,使其更加优化 正如其他人所说,RDMS不会是大规模应用程序中的最佳选择,但由于您刚刚起步,在了解更多信息之前,它应该是可以的
学习正确设置这些表,您应该会看到死锁计数和响应时间下降,您需要分离应用程序层。servlet类不应该进行db查找。任何类都不应该同时导入javax.sql和javax.servlet。曾经。我猜延迟是来自所有100个试图一次命中db的客户端我可能会考虑在DB上排队所有的命中,所以,不是让客户端直接与DB交互,而是将它们的输入发送到处理队列的进程(我想起了ReCK)。
import java.util.LinkedHashMap;
import java.util.Map;
public class Main {
public static void main(String... args) {
UserDB userDB = new UserDB();
// add 1000,000 users
for (int i = 0; i < 1000000; i++)
userDB.addUser(
new Profile(i,
"user+i",
(short) (18 + i % 90),
i % 2 == 0 ? Profile.Sex.Male : Profile.Sex.Female,
"here", "mode"));
// lookup a users and add a chat session.
long start = System.nanoTime();
int operations = 0;
for(int i=0;i<userDB.profileCount();i+=2) {
Profile p0 = userDB.getProfileByNumber(i);
operations++;
Profile p1 = userDB.getProfileByNumber(i+1);
operations++;
userDB.chatsTo(i, i+1);
operations++;
}
for(int i=0;i<userDB.profileCount();i+=2) {
userDB.endChat(i);
operations++;
}
long time = System.nanoTime() -start;
System.out.printf("Average lookup and update time per operation was %d ns%n", time/operations);
}
}
class UserDB {
private final Map<Long, Profile> profileMap = new LinkedHashMap<Long, Profile>();
private final Map<Long, Long> chatsWith = new LinkedHashMap<Long, Long>();
public void addUser(Profile profile) {
profileMap.put(profile.number, profile);
}
public Profile getProfileByNumber(long number) {
return profileMap.get(number);
}
public void chatsTo(long number1, long number2) {
chatsWith.put(number1, number2);
chatsWith.put(number2, number1);
}
public void endChat(long number) {
Long other = chatsWith.get(number);
if (other == null) return;
Long number2 = chatsWith.get(other);
if (number2 != null && number2 == number)
chatsWith.remove(other);
}
public int profileCount() {
return profileMap.size();
}
}
class Profile {
final long number;
final String name;
final short age;
final Sex sex;
final String location;
final String aslmode;
Profile(long number, String name, short age, Sex sex, String location, String aslmode) {
this.number = number;
this.name = name;
this.age = age;
this.sex = sex;
this.location = location;
this.aslmode = aslmode;
}
enum Sex {Male, Female}
}
Average lookup and update time per operation was 636 ns