简单 PHP / SQL 聊天设置
Simple PHP / SQL chat setup
我设置了一个基本的聊天系统,它使用 SQL 数据库和 PHP 脚本——当用户输入一条消息时,它会发送到数据库,然后被检索并显示.无论如何,每 5 秒显示一次新消息。
综上所述,垃圾邮件很容易导致网站停止响应,此时单击任何链接都会导致错误页面,并且不会输入更多消息。
这是常见的情况吗?我应该如何提高聊天的性能?注意:我真的是新手 PHP and JS/Jquery.
这是经常调用的主要脚本,用于为登录用户更新 html 聊天框的新消息:
比较两个自动递增的值以确定"new messages"、最后显示的消息的值和数据库中最后一条消息的值。
<?php
session_start();
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] == true) {
$alias = $_SESSION['username'];
$host = 'localhost';
$user = 'root';
$pass = '';
$database = 'vethergen_db_accounts';
$table = 'table_messages';
$user_table = 'table_user_info';
$last_id_table = 'table_chat_sync';
$connection = mysqli_connect($host, $user, $pass) or die ("Unable to connect!");
mysqli_select_db($connection,$database) or die ("Unable to select database!");
if ($redis->exists("/lastId/$alias"))
{
$last_id = $redis->get("/lastId/$alias"); //Gets the last id from cache...
}
else
{
$last_id_query = "SELECT last_id FROM $last_id_table WHERE alias = '$alias'";
$last_id_result = mysqli_query($connection,$last_id_query);
$last_id_rows = mysqli_fetch_array($last_id_result);
$last_id = $last_id_rows['last_id'];
// Now that you just read it, create a last_id cache entry for this user
$redis->set("/lastId/$alias", $last_id);
}
$query = "SELECT * FROM $table WHERE text_id > '$last_id'"; //SELECT NEW MESSAGES
$result = mysqli_query($connection,$query);
if ($result && mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_array($result))
{
$color_alias = $row['alias'];
$text_color_query = "SELECT color FROM $user_table WHERE alias = '$color_alias'";
$text_color_result = mysqli_query($connection,$text_color_query);
$text_color_rows = mysqli_fetch_array($text_color_result);
$text_color = $text_color_rows['color'];
if ($row['alias'] === "Vether")
{
echo '<p id = "chat_text" style="color:'.$text_color.'">'.'<b>'.$row['alias'].': '.'</b>'.$row['text']."</p>";
echo '<p id = "time_stamp">'.$row['time'].'</p>';
echo '<p id = "chat_number">'.$row['text_id'].'</p>';
}
else
{
echo '<p id = "chat_text" style="color:'.$text_color.'">'.'<b class = "bold_green">'.$row['alias'].': '.'</b>'.$row['text']."</p>";
echo '<p id = "time_stamp">'.$row['time'].'</p>';
echo '<p id = "chat_number">'.$row['text_id'].'</p>';
}
echo '<hr class = "chat_line"></hr>';
$last_row_id = $row['text_id'];
}
//UPDATE LAST SYNC ID
$update_query = "UPDATE $last_id_table SET last_id = '$last_row_id' WHERE alias = '$alias'";
$redis->delete("/lastId/$alias");
mysqli_query($connection,$update_query);
}
else {echo '';}
?>
您可以在 SELECT * FROM $table WHERE text_id > '$last_id' 的末尾添加限制,这将保留一些垃圾邮件减慢线程的消息。您还可以禁止在 INSERT 语句中重复。
没有具体的正确答案,因为你的问题很笼统,但这里有几件事是显而易见的。您在数据库中建立了一个瓶颈,您拥有的用户越多,您在 table_chat_sync 上进行的更新就越多。
顺便说一句,我不知道为什么要将常量(table 名称)放入 PHP 变量中以供查询。至少这些应该是 php 常量,但这使得语法非常痛苦。您的代码更简单更好,只需在 SQL.
中使用 table 名称
InnoDB
您在使用 InnoDB tables 吗?你应该是,因为你正在更新一行并且使用 InnoDB 你有行级锁定。
您还想确保分配了足够的 innodb 缓冲池缓存以确保数据库在内存中。这将大大缓冲您的 select activity 并为您争取一些空间。
MySQL 解释
您还需要对您的 select 查询进行解释计划,并确保它已正确编入索引,以便使用索引返回查询,并且您不会 table 扫描或临时 table已创建。
与从缓存中获取数据相比,SQL 针对 mysql 的查询速度相当慢,而且实际情况是整套聊天消息变化不大,但您的系统反复将一遍又一遍地查询聊天或其子集。出于这个原因,大多数复杂的系统都使用某种缓存系统或队列。这两种技术之间存在重叠,它们倾向于提供更好的可扩展性以及对 publish/subscribe 等非常适合聊天的概念的支持。
Redis 和其他后端
以 Redis 为例,它可以作为聊天的后端,完全取代消息的实际存储和检索。文档数据库 MongoDB 也是一个替代选项,当数据集可以被控制时,它具有内存中的特性。
将 Redis 与 MySQL 结合使用
Redis 通常与 RDBMS 结合使用,在您的代码中,有几个地方可以提供很大的帮助。例如,您重复执行此查询:
$last_id_query = "SELECT last_id FROM $last_id_table WHERE alias = '$alias'";
使用 redis 你可以这样做:
if ($redis->exists("/lastId/$alias")) {
$last_id = $redis->get("/lastId/$alias");
} else {
$last_id_query = "SELECT last_id FROM $last_id_table WHERE alias = '$alias'";
$last_id_result = mysqli_query($connection,$last_id_query);
$last_id_rows = mysqli_fetch_array($last_id_result);
$last_id = $last_id_rows['last_id'];
// Now that you just read it, create a last_id cache entry for this user
$redis->set("/lastId/$alias", $last_id);
}
唯一的其他细节是,当您更新最后一个 ID 时,您会想要删除 redis 键:
$redis->delete("/lastId/$alias");
希望您能看到这会大大降低 mysql 上的负载,因为如果不添加新消息就不会发生查询。这将缓冲 mysql 相当多,并且可以使用相同的概念来缓存您正在执行的其他查询,这样您就永远不需要 mysql 查询,除非您有一个积极使用 Redis 的新用户。我没有深入探讨这个问题,但您可以将密钥的过期时间设置为一段时间,这样它将清除非活动用户的旧密钥。
负载测试以了解您的瓶颈和容量
你选择依赖 MySQL 是你必须接受的限制,虽然你可以再次调整它,以便在你的用例和负载中,它运行可以接受,但这是没有详细的配置分析和负载测试就无法预测。有很多负载测试和压力测试工具是 FOSS,Apache JMeter 是最古老的工具之一,所以我建议您从它开始。
Websockets
最后但同样重要的是,轮询本质上是一种浪费,如今大多数聊天系统都是使用 websockets 构建的,这更适合拥有持续的客户端-服务器连接的任务。 Websocket 是客户端和服务器代码,并且由于您是 PHP 开发人员,这里有一些项目可以帮助您,Ratchet being one that has been around for a while. There's a PHP client lib Pawl 向您展示了如何建立简单而强大的 websocket 连接。
我设置了一个基本的聊天系统,它使用 SQL 数据库和 PHP 脚本——当用户输入一条消息时,它会发送到数据库,然后被检索并显示.无论如何,每 5 秒显示一次新消息。
综上所述,垃圾邮件很容易导致网站停止响应,此时单击任何链接都会导致错误页面,并且不会输入更多消息。
这是常见的情况吗?我应该如何提高聊天的性能?注意:我真的是新手 PHP and JS/Jquery.
这是经常调用的主要脚本,用于为登录用户更新 html 聊天框的新消息:
比较两个自动递增的值以确定"new messages"、最后显示的消息的值和数据库中最后一条消息的值。
<?php
session_start();
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] == true) {
$alias = $_SESSION['username'];
$host = 'localhost';
$user = 'root';
$pass = '';
$database = 'vethergen_db_accounts';
$table = 'table_messages';
$user_table = 'table_user_info';
$last_id_table = 'table_chat_sync';
$connection = mysqli_connect($host, $user, $pass) or die ("Unable to connect!");
mysqli_select_db($connection,$database) or die ("Unable to select database!");
if ($redis->exists("/lastId/$alias"))
{
$last_id = $redis->get("/lastId/$alias"); //Gets the last id from cache...
}
else
{
$last_id_query = "SELECT last_id FROM $last_id_table WHERE alias = '$alias'";
$last_id_result = mysqli_query($connection,$last_id_query);
$last_id_rows = mysqli_fetch_array($last_id_result);
$last_id = $last_id_rows['last_id'];
// Now that you just read it, create a last_id cache entry for this user
$redis->set("/lastId/$alias", $last_id);
}
$query = "SELECT * FROM $table WHERE text_id > '$last_id'"; //SELECT NEW MESSAGES
$result = mysqli_query($connection,$query);
if ($result && mysqli_num_rows($result) > 0)
{
while($row = mysqli_fetch_array($result))
{
$color_alias = $row['alias'];
$text_color_query = "SELECT color FROM $user_table WHERE alias = '$color_alias'";
$text_color_result = mysqli_query($connection,$text_color_query);
$text_color_rows = mysqli_fetch_array($text_color_result);
$text_color = $text_color_rows['color'];
if ($row['alias'] === "Vether")
{
echo '<p id = "chat_text" style="color:'.$text_color.'">'.'<b>'.$row['alias'].': '.'</b>'.$row['text']."</p>";
echo '<p id = "time_stamp">'.$row['time'].'</p>';
echo '<p id = "chat_number">'.$row['text_id'].'</p>';
}
else
{
echo '<p id = "chat_text" style="color:'.$text_color.'">'.'<b class = "bold_green">'.$row['alias'].': '.'</b>'.$row['text']."</p>";
echo '<p id = "time_stamp">'.$row['time'].'</p>';
echo '<p id = "chat_number">'.$row['text_id'].'</p>';
}
echo '<hr class = "chat_line"></hr>';
$last_row_id = $row['text_id'];
}
//UPDATE LAST SYNC ID
$update_query = "UPDATE $last_id_table SET last_id = '$last_row_id' WHERE alias = '$alias'";
$redis->delete("/lastId/$alias");
mysqli_query($connection,$update_query);
}
else {echo '';}
?>
您可以在 SELECT * FROM $table WHERE text_id > '$last_id' 的末尾添加限制,这将保留一些垃圾邮件减慢线程的消息。您还可以禁止在 INSERT 语句中重复。
没有具体的正确答案,因为你的问题很笼统,但这里有几件事是显而易见的。您在数据库中建立了一个瓶颈,您拥有的用户越多,您在 table_chat_sync 上进行的更新就越多。
顺便说一句,我不知道为什么要将常量(table 名称)放入 PHP 变量中以供查询。至少这些应该是 php 常量,但这使得语法非常痛苦。您的代码更简单更好,只需在 SQL.
中使用 table 名称InnoDB
您在使用 InnoDB tables 吗?你应该是,因为你正在更新一行并且使用 InnoDB 你有行级锁定。
您还想确保分配了足够的 innodb 缓冲池缓存以确保数据库在内存中。这将大大缓冲您的 select activity 并为您争取一些空间。
MySQL 解释
您还需要对您的 select 查询进行解释计划,并确保它已正确编入索引,以便使用索引返回查询,并且您不会 table 扫描或临时 table已创建。
与从缓存中获取数据相比,SQL 针对 mysql 的查询速度相当慢,而且实际情况是整套聊天消息变化不大,但您的系统反复将一遍又一遍地查询聊天或其子集。出于这个原因,大多数复杂的系统都使用某种缓存系统或队列。这两种技术之间存在重叠,它们倾向于提供更好的可扩展性以及对 publish/subscribe 等非常适合聊天的概念的支持。
Redis 和其他后端
以 Redis 为例,它可以作为聊天的后端,完全取代消息的实际存储和检索。文档数据库 MongoDB 也是一个替代选项,当数据集可以被控制时,它具有内存中的特性。
将 Redis 与 MySQL 结合使用 Redis 通常与 RDBMS 结合使用,在您的代码中,有几个地方可以提供很大的帮助。例如,您重复执行此查询:
$last_id_query = "SELECT last_id FROM $last_id_table WHERE alias = '$alias'";
使用 redis 你可以这样做:
if ($redis->exists("/lastId/$alias")) {
$last_id = $redis->get("/lastId/$alias");
} else {
$last_id_query = "SELECT last_id FROM $last_id_table WHERE alias = '$alias'";
$last_id_result = mysqli_query($connection,$last_id_query);
$last_id_rows = mysqli_fetch_array($last_id_result);
$last_id = $last_id_rows['last_id'];
// Now that you just read it, create a last_id cache entry for this user
$redis->set("/lastId/$alias", $last_id);
}
唯一的其他细节是,当您更新最后一个 ID 时,您会想要删除 redis 键:
$redis->delete("/lastId/$alias");
希望您能看到这会大大降低 mysql 上的负载,因为如果不添加新消息就不会发生查询。这将缓冲 mysql 相当多,并且可以使用相同的概念来缓存您正在执行的其他查询,这样您就永远不需要 mysql 查询,除非您有一个积极使用 Redis 的新用户。我没有深入探讨这个问题,但您可以将密钥的过期时间设置为一段时间,这样它将清除非活动用户的旧密钥。
负载测试以了解您的瓶颈和容量
你选择依赖 MySQL 是你必须接受的限制,虽然你可以再次调整它,以便在你的用例和负载中,它运行可以接受,但这是没有详细的配置分析和负载测试就无法预测。有很多负载测试和压力测试工具是 FOSS,Apache JMeter 是最古老的工具之一,所以我建议您从它开始。
Websockets
最后但同样重要的是,轮询本质上是一种浪费,如今大多数聊天系统都是使用 websockets 构建的,这更适合拥有持续的客户端-服务器连接的任务。 Websocket 是客户端和服务器代码,并且由于您是 PHP 开发人员,这里有一些项目可以帮助您,Ratchet being one that has been around for a while. There's a PHP client lib Pawl 向您展示了如何建立简单而强大的 websocket 连接。