Singletonパターン

Singletonパターンについて書いてみます。

Singletonパターンはインスタンスが1つしか存在しない唯一のオブジェクトを生成するために使います。

オブジェクトが1つしか必要ないというケースとは ・スレッドプール ・キャッシュ ・ダイアログボックス ・プリファレンス ・レジストリの設定を処理するオブジェクト ・ロギング用のオブジェクト など。 これらのオブジェクトを二つ以上インスタンス化してしまうとプログラムの誤操作、リソースの使いすぎ、つじつまの合わない結果などの問題が出てきます。

特徴としてクラス外部からインスタンスを作成できないようにするためにコンストラクタをprivateにします。

・Singleton.php

<?php
class Singleton
{
    private static Singleton $singleton;

    private function __construct()
    {
        echo "create singleton";
    }

    public static function getInstance(): \\Singleton
    {
        if (!isset(self::$singleton)) {
            self::$singleton = new Singleton();
        }

        return self::$singleton;
    }
}

・index.php

<?php
require_once 'Singleton.php';

$singleton1 = Singleton::getInstance();
$singleton2 = Singleton::getInstance();

if ($singleton1 === $singleton2) {
    echo "同じインスタンス";
}

// new Singleton();
// Fatal error: Uncaught Error: Call to private

インスタンスをnewしようとするとコンストラクタがプライベートなのでFatal errorとなります。 インスタンスが一つしか存在しないことを保証できます


(参考)
Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
2005/12/2
Eric Freeman (著), Elisabeth Freeman (著), Kathy Sierra (著), Bert Bates (著), 佐藤 直生 (監訳), 木下 哲也 (翻訳), 有限会社 福龍興業 (翻訳)
5章 Singletonパターン:唯一のオブジェクト

SQLパフォーマンス詳解を読んだ

明けましておめでとうございます。今年もよろしくお願いいたします。

11月の事ですが、SQLパフォーマンス詳解を読みました。
感想などを書いていきたいと思います。

読んでみようと思ったきっかけ
かなり多くの回数呼ばれるプログラムのデバッグや後輩のコードレビューをする中でパフォーマンスを意識したSQLの知識が皆無に等しくこのままでは良くないと思ったため。また先輩から勧められたため。

感想
インデックスをどう活用するかを書いてくれててわかりやすかった。どうすればパフォーマンスが良くなるのかということでググると、「適切なインデックス」を張るということをよく目にするがいつも「どうすれば適切になるんだ」と思っていた。張り過ぎもよくないと聞くので「過ぎ」ってどの程度のことなんだろうと思ったりしていた。インデックスは作成されているけど、sqlの書き方のせいで使えていないとかもある。そういうものがわかってきたので読んで良かったと思います。MySQLのExtra列については今後しっかり勉強したい。

以下まとめ(MySQLのみ) 第4章、5章は省略

第1章 インデックスの内部構造

インデックスの最も重要な目的は、インデックスを張ったデータに対して順序をつけてアクセスできるようにすることです。

writeの処理(insert, update, delete)が走る際に、大量のデータを動かすわけにはいかない。そういう場合の解決策として双方向連結リストでインデックスリーフノードと対応するテーブルのデータを対応させている。

検索ツリー(Bツリー)
データベースがリーフノードを高速に見つけるのに役に立つ。MySQLの場合は、BTREEと表記されている。Bはバランスの略でその名の通り木構造。1~1000というidが入ったノードと1001~2000、2001~3000といったノードがあって20xxというidを検索するとき、1から順に検索しなくて良いので速くなる。1001以上かどうか、2001以上かどうかなどを調べて該当のノードの中を確認する。 ツリーが深さは対数的に増え、リーフノードの増加に対してツリーの深さの増大は非常に遅い。

インデックスによる検索は、以下の3ステップで行われる。

  1. ツリーを走査
  2. リーフノードチェーンをたどる
  3. テーブルからデータを読み出す

UNIQUEなSCANは1しか行わないため高速。

第2章 WHERE句

前提としてSQLを発行する際には実行計画(EXPLAIN)は必ず行う。 MySQLにおける実行計画で最低限注意して見るべき点は下の記事を参考にしています。

https://nippondanji.blogspot.com/2009/03/mysqlexplain.html

複合インデックス定義する際に考えるべき最も重要なのは、そのインデックスを使えるSQL文ができるだけ多くなるように、列の順番を決めることです。

a, b, c, dという4つの列の複合インデックスがあった場合は、a,bを条件に検索するときも使えるので先に定義しておく。cを条件にすることが多い場合は、c, a, b, dなどの順で定義するといい。こうすることで新たなインデックスの作成を減らせる。インデックスの数が多いと更新処理のパフォーマンスに影響する。

WHERE句はインデックスの列の順に合わせて使う。 OR検索やWHERE INなどは場合によっては実行計画が変わるので出来るだけUNIONやJOINをうまく使って出来るだけ使わないようにしたほうがいいかもしれない。データ数が少ないテーブルの場合はそんなに気にしなくていいとは思うが。

パラメータ化クエリはちゃんと使う。SQLインジェクションを防ぐために。残念ながらMySQLはされていないが、SQLによってはパラメータ化クエリを使うことでキャッシュされたSQLが使える。

・範囲検索(大なり、小なり、BETWEEN) なるべく先に狭い範囲になるように検索してから残りを検索する。

・LIKE ワイルドカード(%)を先頭に出来るだけ使わない。 使ったことなかったけどmatchやagainstを使えるなら使う。

カラムはしっかり指定して*で取得するのはやめる。

インデックスは2つ使うより、1つだけ使う方が高速

第3章 パフォーマンスとスケーラビリティ

データ量が多くなるにつれパフォーマンスは悪くなる。

注意深い実行計画の調査結果は、うわべだけのベンチマークよりも信用のおけるものです。完全な負荷テストは意味のあることですが、そのための手間はかかります。

本番のデータとテスト時のデータ、開発時のデータは全く違うものだと思うし、似たようなテストをするにしても大変な手間がかかる。大量データの場合は特に、適切なインデックスが張られているかを注意する。ただそれだけデータがあるということはそれだけ更新処理があると思うので張りすぎにも注意する。

リレーショナルSQLデータベースかリレーショナルでないシステムに関わらず、正しいインデックスを作ることは、クエリの応答時間を短くする唯一の方法です。

第6章 ソートとグルーピング

order by句がインデックスによる順序付けと一致している場合、 データベースは明示的なソート処理を省略できます。

order by句でASK, DESCが混在している場合はインデックスが効かない。ASC,ASCやDESC,DESCだったら効く。混在させる場合はインデックスを定義する必要がある。

第7章 部分的結果

・LIMIT

データベースは、部分結果のみを取得することを事前に知っている場合のみ、部分結果のみ、部分結果向けにクエリを最適化できます。

実際に事前に知れているかどうかは実行計画を確認する。ただMySQLの場合は、Extra列に「using filesort」がなければ部分結果の取得が行われている。

・ページングについて

ページングの際は、並べ替えの順序は確定的である必要があります。

ページングの際はoffsetを使うのが一般的だがそのテーブルにリアルタイムで行が挿入される場合は、挿入されたデータが最初に来る場合があるので、ずれてしまう。

第8章 挿入、削除、更新

インデックスが作られるとデータベースはインデックスを自動的にメンテナンスする。 簡単にいうとinsertするとインデックスのノードも更新するため処理が重くなる。 インデックスを張りすぎると良くないのはこのため。insertが圧倒的に多いテーブルにはインデックスは出来るだけ少なくする必要がある。

インデックスは注意深くかつ慎重に使い、かつ、可能な限り冗長なインデックスは使わないようにしましょう。これは、delete文やupdate文を使う際にも同じ事が言えます。

deleteやupdateにも実行計画はあります。

Xdebug3 var_dumpもきれいに表示したいしデバッグもしたいし、というときの設定

Xdebugがバージョン3になってました。

https://xdebug.org/

環境は、Windows+PHPStorm+PHP7.4(Xampp)です。

PHP7.2→PHP7.4 にしたくて、Xamppを新しくし、Xdebugを設定しなおして気づきました。

まず、PHPStormでデバッグできず、ちょっとはまりました。

注意点は

①PHPStormのバージョンが2020.3以降じゃないとXdebug3に対応していない
②ポート番号がデフォルト9003になっている

です。

さて、表題の話なんですが、まず、デバッグしたいために最初は次のようにphp.iniを設定していました。

zend_extension = D:\xampp2\php\ext\php_xdebug-3.0.1-7.4-vc15-x86_64.dll
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9001
xdebug.var_display_max_children=-1
xdebug.var_display_max_data=-1
xdebug.var_display_max_depth=-1
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9001
xdebug.mode = debug

最後のxdebug.modeがポイントです。

xdebug.mode = debug

にしないとPHPStormでデバッグできません。(ほかのIDEで試してないのでほかのIDEでどうかわかりません。)

しかし、このままにしておくと、PHPerは必ず使うであろう、var_dump関数の実行結果が美しく表示されません。

きれいに表示されないvar_dumpの結果
きれいに表示されないvar_dumpの結果

こんなん見てられないし、辛いですよね~(>_<)


xdebug.mode = develop

にすると、美しく整形されて表示されます。developがデフォルトだそうです。

美しく出力されたvar_dumpの結果。これならだれでもわかるね!⊂(^-^)⊃

しかし、このままではデバッグできません。

一体どうして…。

と、ぼんやりXdebugの作者のDerickさんのTwitterを見ていたら

Xdebug3のModeについて説明動画があるじゃないですか!!!

Xdebugを作ってくれているだけでもありがたいのに、動画まで…。その優しさに涙が溢れますね…。(>_<)

というわけで、上記を見ると、modeが二つ平行で設定できることがわかります。

xdebug.mode = develop,debug

上記のように設定すると、var_dumpも美しく出力され、デバッグもできました!⊂(^-^)⊃

ちな、Xdebug関係のphp.iniの設定を下記に書いておきます。(私はデバッグのポートを9001で運用しています。)

[xdebug]
zend_extension = D:\xampp2\php\ext\php_xdebug-3.0.1-7.4-vc15-x86_64.dll
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port=9001
xdebug.var_display_max_children=-1
xdebug.var_display_max_data=-1
xdebug.var_display_max_depth=-1
xdebug.client_host = 127.0.0.1
xdebug.client_port = 9001
xdebug.mode = develop,debug

php.iniを変更した後は、Webサーバーの再起動を忘れないように!

ありがとう、Derickさん!ロンドンいろいろ大変そうですが、がんばってください。

Smartyでクラス定数を使いたい

最近enumが熱く、enumでクラスなどを作っていると表題みたいなことがあります。

下は 物凄く雑ですがenum

namespace Mental;

class Menhealer {
    const YABAI = 1; 
    const MURI = 2;
    const MOUIYA = 3;
    const KIDUITE = 4;
    const WAKATTEYO = 5;
}

これをSmarty側で呼び出す方法は以下です

{"Mental\Menhealer::YABAI"|constant}

if文だとこう書くらしいです。

{if $menhealer === constant("Mental\Menhealer::KIDUITE"}
    努力はしている
{/if}

クラス定数使うとわかりやすいですが、Smartyだと今のところuseが使えないのもあって長くなるのが難点ですね。

WordPress高速化に使えるプラグイン

会社のWordpressサイトをPageSpeed Insightsでしらべてみたところ、点数がとても低いので対策しました。

いろいろ調査して試してみたところ、AutoptimizeとWP Fastest Cacheという2つのプラグインが応答速度改善に効果がありました。

基本的にAutoptimizeでできることはAutoptimizeに任せていますが、これだけだとページのロードが遅いです。
ページをキャッシュしてロードを早くするためにWP Fastest Cacheを使っています。

Autoptimize/WP Fastest Cacheは下記のように設定しました。

Autoptimize

WP Fastest Cache