4

に説明したように、mysqldbサーバーにいくつかの問題がありました。私はブラックホールにいるので、あなたの意見を知っていて、いくつかのアイデアがあります。サーバーの動作が起こっているので、私にはわかりません。

すべての環境を説明しようと思います。私は1つのDBを持っており、多くのテーブルがあります。DBからすべてのデータをエクスポートするエクスポーターツールをJavaで作成しました。データは5つの異なるテーブルに格納されており、5つのテーブルを結合するデータを取得する必要があります。これらはテーブルです:

DBの構造は、一部のセンサーから情報を受信して​​保存するシステムです。

測定表:センサーから受け取る測定値。

+--------------------+------------+------+-----+---------+----------------+
| Field              | Type       | Null | Key | Default | Extra          |
+--------------------+------------+------+-----+---------+----------------+
| id                 | bigint(20) | NO   | PRI | NULL    | auto_increment |
| version            | bigint(20) | NO   |     | NULL    |                |
| counter            | char(2)    | YES  |     | NULL    |                |
| datemeasurement_id | datetime   | NO   | MUL | NULL    |                |
| datereal_id        | datetime   | NO   | MUL | NULL    |                |
| delayed            | bit(1)     | NO   |     | NULL    |                |
| frequency          | tinyint(4) | YES  |     | NULL    |                |
| measuringentity_id | bigint(20) | NO   | MUL | NULL    |                |
| real               | bit(1)     | NO   |     | NULL    |                |
| tamper             | bit(1)     | NO   |     | NULL    |                |
| value              | float      | NO   |     | NULL    |                |
+--------------------+------------+------+-----+---------+----------------+

Measurement_entityテーブル:1つのセンサーで複数のもの(温度、湿度)を測定できます。そして、これらはエンティティです。

+--------------+------------+------+-----+---------+----------------+
| Field        | Type       | Null | Key | Default | Extra          |
+--------------+------------+------+-----+---------+----------------+
| id           | bigint(20) | NO   | PRI | NULL    | auto_increment |
| version      | bigint(20) | NO   |     | NULL    |                |
| household_id | varchar(4) | NO   | MUL | NULL    |                |
| operative    | bit(1)     | NO   |     | NULL    |                |
| type         | char(20)   | NO   |     | NULL    |                |
| unit         | char(3)    | NO   |     | NULL    |                |
| interval     | float      | YES  |     | NULL    |                |
+--------------+------------+------+-----+---------+----------------+

Sensor_measuring_entity:1つのセンサーに複数のエンティティを関連付けることができます。

+--------------------+------------+------+-----+---------+-------+
| Field              | Type       | Null | Key | Default | Extra |
+--------------------+------------+------+-----+---------+-------+
| sensor_id          | bigint(20) | NO   | PRI | NULL    |       |
| measuringentity_id | bigint(20) | NO   | PRI | NULL    |       |
| version            | bigint(20) | NO   |     | NULL    |       |
+--------------------+------------+------+-----+---------+-------+

センサーテーブル:前のテーブルの測定エンティティに関連するセンサーの情報。

+---------------------+-------------+------+-----+---------+----------------+
| Field               | Type        | Null | Key | Default | Extra          |
+---------------------+-------------+------+-----+---------+----------------+
| id                  | bigint(20)  | NO   | PRI | NULL    | auto_increment |
| version             | bigint(20)  | NO   |     | NULL    |                |
| battery             | bit(1)      | NO   |     | NULL    |                |
| identifier          | char(6)     | NO   |     | NULL    |                |
| installationdate_id | datetime    | NO   | MUL | NULL    |                |
| lastreceiveddate_id | datetime    | YES  | MUL | NULL    |                |
| location_id         | bigint(20)  | NO   | MUL | NULL    |                |
| operative           | bit(1)      | NO   |     | NULL    |                |
| tampererror         | smallint(6) | NO   |     | NULL    |                |
+---------------------+-------------+------+-----+---------+----------------+

ロケーションテーブル:センサーはどこに配置されますか。

+------------+------------+------+-----+---------+----------------+
| Field      | Type       | Null | Key | Default | Extra          |
+------------+------------+------+-----+---------+----------------+
| id         | bigint(20) | NO   | PRI | NULL    | auto_increment |
| version    | bigint(20) | NO   |     | NULL    |                |
| height     | tinyint(4) | YES  |     | NULL    |                |
| operative  | bit(1)     | NO   |     | NULL    |                |
| place      | char(15)   | NO   | MUL | NULL    |                |
| room       | char(15)   | NO   |     | NULL    |                |
| typesensor | char(15)   | NO   |     | NULL    |                |
| formaster  | bit(1)     | YES  |     | NULL    |                |
+------------+------------+------+-----+---------+----------------+

情報をエクスポートするアルゴリズムは、このように、データをクロスし、分離されたcsvファイルに含めることができるすべてのタイプのセンサーの分離された情報をエクスポートしようとする場合に重要です。

for (int i = 0; i < households.length; i++) {

openConnection();

for (int j = 0; j < values.length; j++) {

    for (int k = 0; k < rooms.length; k++) {

        if (places.length > 0) {

        for (int l = 0; l < places.length; l++) {

            for (int m = 0; m < height.length; m++) {

                export(startDate2, endDate,
                    households[i], values[j],
                    rooms[k], places[l],height[m]);
                }
            }
        } else {
            for (int m = 0; m < height.length; m++) {

                export(startDate2, endDate,
                    households[i], values[j],
                    rooms[k], null, height[m]);
            }
        }

    }
}

try {
    connection.close();
} catch (SQLException e1) {
    e1.printStackTrace();
}

        }


public void export(String startTime, String endTime, String household,
        String type, String room, String place, String height)
        throws ExporterException {


    String sql = buildSQLStatement(startTime, endTime, household, type,
            room, place, height);

    Statement query;

    try {
        query = connection.createStatement();
        ResultSet result = query.executeQuery(sql); 
        …
        (The exporting to csv code)
        …





private String buildSQLStatement(String startTime, String endTime,
        String household, String type, String room, String place,
        String height) {


    String sql = "select HIGH_PRIORITY m.datemeasurement_id, me.type, l.place, m.value, l.room, l.height, s.identifier "
            + "FROM measurement as m STRAIGHT_JOIN measuring_entity as me ON m.measuringentity_id = me.id "
            + "STRAIGHT_JOIN sensor_measuring_entity as sme ON me.id = sme.measuringentity_id "
            + "STRAIGHT_JOIN sensor as s ON sme.sensor_id = s.id "
            + "STRAIGHT_JOIN location as l ON l.id = s.location_id"
            + " WHERE m.datemeasurement_id "
            + " >"
            + "'"
            + startTime
            + "'"
            + " AND m.datemeasurement_id"
            + " <"
            + "'"
            + endTime
            + "'"
            + " AND m.measuringentity_id"
            + " IN (SELECT  me.id FROM measuring_entity AS me WHERE me.household_id="
            + "'"
            + household
            + "'"
            + ")";

私の大きな問題はこれです:DBからのこのコードを使用したこのアプリは、非常に遅く動作することがあります。MySQLの動作は非常に遅く、MYSQLの動作が非常に速い場合もあります。なぜこのような行動の違いが起こっているのか理解できません。

たとえば、速度が遅い場合(CPUの0.3〜0%)、DBからすべてのデータをエクスポートするのに約3日かかる場合があります(約200.000クエリ)が、前に述べたように、サーバーが30〜40分で同じ作業(CPUの85%)。

私たちが見た問題は、動作が遅い場合、mysqlが「準備中」状態(クエリごとに約140秒)で多くの時間を費やしてクエリを最適化しようとしていることですが、私が言ったように、これは数回しか発生しません。毎回ではありません。

1016 | root   | localhost:53936                | OptimAAL      | Query   |   10 | preparing | select HIGH_PRIORITY m.datemeasurement_id, me.type, l.place, m.value, l.room, l.height, s.identifier

これらは、実行できるクエリの1つです。

EXPLAIN select HIGH_PRIORITY m.datemeasurement_id, me.type,
l.place,m.value, l.room, l.height, s.identifier
FROM measurement as m
STRAIGHT_JOIN measuring_entity as me ON m.measuringentity_id=me.id
STRAIGHT_JOIN sensor_measuring_entity as sme ON me.id=sme.measuringentity_id
STRAIGHT_JOIN sensor as s ON sme.sensor_id=s.id
STRAIGHT_JOIN location as l ON l.id=s.location_id
WHERE m.datemeasurement_id  >'2012-01-19 06:19:00'
AND m.datemeasurement_id <'2012-01-19 06:20:00'
AND m.measuringentity_id IN (SELECT  me.id FROM measuring_entity AS me
WHERE me.household_id='0022')
AND (height = '0')
AND (type = 'Brightness')
AND (place = 'Corner')
AND (room = 'Living room')
ORDER BY datemeasurement_id

これはexplainの結果です:

+----+--------------------+-------+-----------------+-----------------------------------------------+--------------------+---------+-------------------------------+------+-------------+
| id | select_type        | table | type            | possible_keys                                 | key                | key_len | ref                           | rows | Extra       |
+----+--------------------+-------+-----------------+-----------------------------------------------+--------------------+---------+-------------------------------+------+-------------+
|  1 | PRIMARY            | m     | range           | FK93F2DBBC6292BE2,FK93F2DBBCA61A7F92          | FK93F2DBBC6292BE2  | 8       | NULL                          |    4 | Using where |
|  1 | PRIMARY            | me    | eq_ref          | PRIMARY                                       | PRIMARY            | 8       | OptimAAL.m.measuringentity_id |    1 | Using where |
|  1 | PRIMARY            | sme   | ref             | PRIMARY,FK951FA3ECA61A7F92,FK951FA3ECF9AE4602 | FK951FA3ECA61A7F92 | 8       | OptimAAL.m.measuringentity_id |    1 | Using index |
|  1 | PRIMARY            | s     | eq_ref          | PRIMARY,FKCA0053BA3328FE22                    | PRIMARY            | 8       | OptimAAL.sme.sensor_id        |    1 |             |
|  1 | PRIMARY            | l     | eq_ref          | PRIMARY,place                                 | PRIMARY            | 8       | OptimAAL.s.location_id        |    1 | Using where |
|  2 | DEPENDENT SUBQUERY | me    | unique_subquery | PRIMARY,FK11C7EA07E6EB51F2                    | PRIMARY            | 8       | func                          |    1 | Using where |
+----+--------------------+-------+-----------------+-----------------------------------------------+--------------------+---------+-------------------------------+------+-------------+

明らかに、日付間隔の値を変更すると、DBに100万の測定値があるため、データ量が大幅に増加します。

私はすべてを試しました:

mysQL構成ファイル(/etc/my.cnf)を変更します。

[mysqld]
#bind-address = 141.21.8.197
max_allowed_packet = 128M
sort_buffer_size = 512M
max_connections=500
query_cache_size = 512M
query_cache_limit = 512M
query-cache-type = 2
table_cache = 80
thread_cache_size=8
key_buffer_size = 512M
read_buffer_size=64M
read_rnd_buffer_size=64M
myisam_sort_buffer_size=64M
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_size=700M
innodb_additional_mem_pool_size=20M
datadir=/data/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
#Enable logs
log = /var/log/mysql/mysql-log.log 
log-error = /var/log/mysql/mysql-error.log  
long_query_time = 1
log-slow-queries = /var/log/mysql/mysql-slow.log
[mysqld_safe]
log-error=/var/log/mysql/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
  • 異なるタイプのJOINを使用して、最適化クエリを回避するように強制します。
  • 素敵な-20を使用して、プロセスに最高の優先順位を与えます。
  • コード内のクエリの代わりにストアドプロシージャを使用します。
  • DBへの他の接続をすべて閉じて、他の接続なしで自分だけのDBを作成します。
  • クエリを変更して最適化してみてください。
  • ..。

ご覧のとおり、私はすべてを試しましたが、サーバーが常に高速になるようにするために何ができるかわかりません。

これらはサーバーの情報です:

MySQL version:  5.1.61-log / x86_64 
RAM: 8 GB
OS: CentOS release 6.2 (Final)
CPU: 4 Cores / Xeon E6510  @ 1.73GHz

私は本当にあなたの助けをいただければ幸いです、

編集:

私にとって最大の問題は、なぜサーバーの動作が異なるのかということです。クエリを最適化できることは理解していますが、このコードでは非常に高速に動作する場合があります。

今の私の悪夢は、なぜいつも速くではなく、時々速く働いているのかを知ることです。今、私はIT担当者に、ハードウェアアクセス、ハードディスクなどの問題が発生する可能性があるかどうかを確認しています。

また、SQL構成、またはMYSQL内にあるクエリオプティマイザーの問題である可能性もありますが、ブラックホールの解決策を見つけることができません。

助けてくれてありがとう

4

2 に答える 2

2

あなたができるいくつかの最適化を考えることができます。

まず、バインド変数と準備済みステートメントを使用します。

PreparedStatment stmt = connection.prepareStatement(
"select HIGH_PRIORITY m.datemeasurement_id, me.type, l.place, m.value, l.room, l.height, s.identifier "
+ "FROM measurement as m STRAIGHT_JOIN measuring_entity as me ON m.measuringentity_id = me.id "
+ "STRAIGHT_JOIN sensor_measuring_entity as sme ON me.id = sme.measuringentity_id "
+ "STRAIGHT_JOIN sensor as s ON sme.sensor_id = s.id "
+ "STRAIGHT_JOIN location as l ON l.id = s.location_id"
+ " WHERE m.datemeasurement_id  > ? "
+ " AND m.datemeasurement_id  < ? "
+ " AND m.measuringentity_id IN (SELECT  me.id FROM measuring_entity AS me WHERE me.household_id= ? )";

stmt.setDate(1, startDate);
stmt.setDate(2, endDate);
stmt.setString(3, household);

stmt.executeQuery();

次に、IN を削除します。ここで、measurement_entity に対して別の結合を使用できませんか? IN は、多くの場合、それほどうまく機能しません。

第三に、バッチ処理を使用して挿入を実行できますか? バッチ挿入により、速度が大幅に向上するはずです。

これらは、私がすぐに思いつくことができるいくつかのことです。クエリが Java 側で最適化されていない場合、世界中のすべての SQL チューニングは役に立ちません。

于 2012-05-04T10:50:39.393 に答える
0

私の全体的な答えは、すべてのデータに対して1つのクエリを作成し、後でそれをJavaで分割することです。(ほぼ同じコードで、そこに投稿しました)

最初のボトルネックは、すべての世帯の接続の開始です。2つ目は、世帯、価値観、部屋、場所、高さの配列の大きさによって異なります。接続のオーバーヘッドが非常に大きいため、これだけ多くのクエリを送信することはお勧めできません。すべての情報を1つに連結し(おそらく大きなクエリ)、データをフェッチするために必要な時間を計算する必要があります)。

于 2012-05-04T10:50:23.237 に答える