【问题标题】:Simple PHP / SQL chat setup简单的 PHP/SQL 聊天设置
【发布时间】:2017-12-07 03:59:09
【问题描述】:

我有一个基本的聊天系统设置,它使用一个 SQL 数据库和一个 PHP 脚本——当用户输入一条消息时,它被发送到数据库,然后被检索和显示。无论如何,新消息每 5 秒显示一次。

话虽如此,只是垃圾邮件很容易导致网站停止响应,此时单击任何链接将导致错误页面,并且不会输入更多消息。

这是常见的情况吗?我应该如何提高聊天的性能?注意:我真的是 PHP 和 JS/Jquery 的新手。

这里是经常调用的主脚本,用于为登录用户更新 html 聊天框并使用新消息:

比较两个自动递增的值以确定“新消息”、最后显示的消息的值以及数据库中最后一条消息的值。

<?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 '';}
 ?>

【问题讨论】:

    标签: javascript php chat


    【解决方案1】:

    没有具体的正确答案,因为您的问题非常笼统,但这里有几件事是显而易见的。您在数据库中建立了一个瓶颈,您拥有的用户越多,您对 table_chat_sync 进行的更新就越多。

    顺便说一句,我不知道您为什么要将常量(表名)放入 PHP 变量中以进行查询。至少这些应该是 php 常量,但这使得语法非常痛苦。只需使用 SQL 中的表名,您的代码就更简单更好了。

    InnoDB

    你在使用 InnoDB 表吗?你应该是,因为你正在更新一行并且使用 InnoDB 你有行级锁定。

    您还需要确保分配了足够的 innodb 缓冲池缓存以确保 db 在内存中。这将大大缓冲您的选择活动,并为您腾出一些空间。

    MySQL 解释

    您还需要解释您的选择查询的计划,并确保它已正确编制索引,以便使用索引返回查询,并且您不会扫描表或创建临时表。

    与从缓存中获取数据相比,针对 mysql 的 SQL 查询相当慢,而现实情况是,完整的聊天消息集并没有太大变化,但您的系统会反复查询聊天消息或其中的子集它一遍又一遍。出于这个原因,大多数复杂的系统都在使用某种缓存系统或排队。这两种技术之间存在重叠,它们往往提供更好的可扩展性以及对发布/订阅等非常适合聊天的概念的支持。

    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 构建的,这更适合保持客户端-服务器连接的任务。 Websocket 是客户端和服务器代码,因为您是 PHP 开发人员,所以这里有一些项目可以帮助您,Ratchet 已经存在了一段时间。有一个 PHP 客户端库 Pawl 向您展示了如何建立一个简单、健壮的 websocket 连接。

    【讨论】:

    • 感谢详细的回复。由于我才刚刚开始,有了这些东西,我现在将坚持使用 SQL。话虽如此,能否请您解释一下 InnoDB 表、缓冲池缓存和表扫描是什么?
    • MySQL 有多个引擎可以改变表的存储方式。您应该使用 innodb 引擎。默认情况下,创建的表将使用 myisam 引擎。您可以使用 alter table 命令更改此设置。 innodb 缓冲池是为数据缓存留出内存的配置设置。您可能需要增加默认设置。理想情况下,您希望您的表适合数据缓存,并使用像 innotop 这样的工具,您可以检查您的缓存命中百分比是多少。表扫描是指查询不使用索引,而是从上到下读取整个表。
    • 您应该单独搜索这些内容并阅读您认为有帮助的任何手册页或博客文章。您可以通过使用 EXPLAIN 来确定查询发生了什么,您将其添加到 SELECT 语句的前面,并使用输出来确定您的查询发生了什么。请参阅here 了解一下。
    • 我在答案中添加了一些关于使用 Redis 和 MySQL 方案的信息。
    • 添加了一些压力测试信息,更新了 Redis 部分。祝你好运!
    【解决方案2】:

    您可以在 SELECT * FROM $table WHERE text_id > '$last_id' 的末尾添加一个限制,这样可以防止一些垃圾邮件减慢线程的速度。您还可以在 INSERT 语句中禁止重复。

    【讨论】:

    • 感谢您的回复——我会这样做的!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 2011-04-07
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多