気軽に楽しくプログラムと遊ぶ

自分が興味があってためになるかもって思う情報を提供しています。

Mac OS 開発環境初期設定

ターミナル

brew install

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew install cask

クライアントアプリ

アプリランチャー、コピペアプリ、 エディタ、docker、メニューカレンダー表示、ブルーライトカット

brew install --cask alfred
brew install --cask clipy
brew install --cask atom
brew install --cask docker
brew install --cask flux

DBクライアントツール. https://dbeaver.io/files/dbeaver-ce-latest-macos.dmg

Google 日本語入力 https://tools.google.com/dlpage/japaneseinput/eula.html?platform=mac

IDE

intellij https://www.jetbrains.com/ja-jp/idea/download/#section=mac

STS Spring | Tools

Mybatis キャッシュ設定

Mybatis キャッシュ設定

同じクエリを複数回呼ぶ場合、キャッシュ設定を入れると高速に処理できる可能性があります。

mybatis:
  configuration:
    cache-enabled: true # デフォルト有効だが、明示的に記載。
    default-executor-type: reuse

PreparedStatementをキャッシュし再利用

同一トランザクション内で同じSQLを複数回実行する場合は、 REUSEモードで実行すると、SIMPLEモードと比較して性能向上が期待できる。

これは、SQLを解析してPreparedStatementを生成する処理の実行回数を減らす事ができるためである。

default-executor-type: reuse 

設定できるモードは、他にSIMPLE、BATCHなどがある。
同一トランザクション内で更新系のSQLを連続して大量に実行する場合は、BATCHモードで実行するパフォーマンスが上がる。

Kotlin Coroutineの使い方

以下を写経しつつ、理解をしていきます。
Kotlin の Coroutine を概観する - Qiita

Coroutineとは?

  • 軽量なThreadのようなもの
  • 他の処理をブロックせずに並列処理を行える
  • Threadは無視できないコストがかかるが、Coroutineは気にしなくてもよいレベルのコストしかかからない

Coroutineの作成と利用

non-blocking関数とblocking関数があります。
違いは、main関数の処理をブロックするかどうか。
ブロックとは、一時停止してCoroutine処理を実行することを指す。

non-blocking関数 launch

以下のように実装することでCoroutineを生成。
Coroutineは、launch内の処理をmainスレッドをブロックすることなく、並列で処理することができる。

fun main(args: Array<String>) {
    println("start")
    launch {
        println("coroutine!")
    }
    // Thread.sleepがないとlaunchは実行スレッドをブロックしないため
    // coroutine!が表示されず、endが表示されて終了する
    Thread.sleep(1000)
    println("end")
}

実行結果は以下。Thread.sleepがあるので、coroutine!が表示されるのを待ってから、endが表示される。

start
coroutine!
end

他のコルーチン終了を待機する Job#join()

launchは戻り値でJobを返却するため、他のコルーチン終了を待機するには、以下のように実装可能です。

fun main(args: Array<String>) = runBlocking() {
    println("start")
    launch {
        println("coroutine!")
    }.join()
    println("end")
}

blocking関数 runBlocking

Coroutine(ラムダ)を生成し、実行すると実行スレッドをブロックしつつ、内部で並列処理を実行できる

fun main(args: Array<String>) {
    println("start")
    val text = runBlocking {
        "coroutine!"
    }
    println(text)
    println("end")
}

実行結果は以下。runBlockingは生成したラムダ内の処理結果を返却することができる。

start
coroutine!
end

注意点

  • runBlockingはCoroutineは、非同期処理内(Coroutine内)から呼び出してはいけない
  • 同期処理と非同期処理の境目で利用されることを想定しているため。

non-blocking関数 async/await

lauchのようにブロックしないで非同期処理を行いつつ、戻り値の結果を受け取りたい場合、asyncを利用する。

fun main(args: Array<String>) = runBlocking() {
    println("start")
    val text = async {
        "coroutine!"
    }.await()
    println(text)
    println("end")
}

asyncの戻り値のDeferred インタフェースに実装されているawaitを呼び出すと、Coroutine終了まで
現在のコルーチンを中断し、終了したCoroutineの戻り値を取得する。

Suspending Functionとは?

Coroutineを中断させることができる関数を「Suspending Function」と言う。

suspend fun doSomething() {
    delay(1000)
    println("something")
}

suspend modifierを付けることでCoroutineを中断できることを示します。
Coroutine内で呼び出す関数はsuspendをつけないとコンパイルエラーになります。

上記で呼び出しているdelayもSuspending Functionになります。

また、launch、runBlockingはラムダを用いてCoroutineを生成していましたが、これは無名のSuspending Lambdaになっています。

lauchの定義は以下の通りで、blockのところで無名のSuspending Lambdaを生成しています。

public fun launch(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job

Suspending Functionの利用方法は以下

fun main(args: Array<String>) {
    println("start")
    runBlocking {
        println("coroutine start")
        greet()
        println("coroutine end")
    }
    println("end")
}

suspend fun greet() { // suspendがないとエラーになる
    delay(1000)
    println("Hello, Coroutine!")
}

greetは、runBlockingで生成したCoroutineを中断し、処理が終わったら、Coroutineを再開します。
そして、実行スレッドに結果があれば戻すことも可能です。

MySQL ギャップロックとは?

現場でギャップロックという聞き慣れない用語を耳にしたので、

以下の記事を参考に理解する。

MySQLのギャップロックとネクストキーロック - 41から始めました

MySQLロック

ロックの種類

  • 排他ロック
  • 共有ロック
    • 排他ロックがかかったデータを参照する際にかかるロック
    • ロックを保持するトランザクションによる行の読み取りが許可、書き込み不可
    • 共有ロック中データに対して、他トランザクションでも共有ロックは可能。そういう意味で共有ロック

ロック中データ(行)の扱い

InnoDBトランザクション分離レベルによって、ロック中の行の書き込み不可や読み取り不可などにできる。
MySQLInnoDBのデフォルト分離レベルはREPEATABLE READで、ロック中の行の書き込み不可かつ、読み取りは可能な状態になっている。

トランザクション分離レベル

下に行くほど分離レベルが高い

分離レベル 説明 デフォルト設定採用のDMBS
READ UNCOMMITTED コミットされていない変更を他のトランザクションから参照可 -
READ COMMITTED コミットされた変更を他のトランザクションから参照可 OraclePostgreSQLSQL Server
REPEATABEL READ コミットされ、トランザクション終了後の変更を他のトランザクションから参照可 MySQL
SERIALIZABLE 強制的にトランザクションを順序付けて処理。 -

ギャップロックとは?

InnoDBには3種類のロックがあります。

  • レコードロック:単一レコードのロック
  • ギャップロック:各種ギャップに対するロック
  • ネクスキーロック:レコードロックとそのレコードの直前のギャップロックの組み合わせ

ギャップロックのギャップは以下の三つで、MySQLでは対象レコードと以下のギャップにあるレコードにロックがかかります。

  • index値を持つ行と行の間にあるギャップ
    • レコードIDが1、3をロックしたときにID=2(間のギャップ)へのレコード挿入がロック待ちとなる
  • 先頭のindex値を持つ行の前のギャップ
    • レコードIDが4、5をロックしたとき、ID=3(先頭のギャップ)へのレコード挿入がロック待ちとなる。ID=2なら挿入可
  • 末尾のindex値を持つ行の後のギャップ
    • レコードIDが4、5をロックしたとき、ID=6(末尾のギャップ)へのレコード挿入がロック待ちとなる。ID=7なら挿入可

MySQLでデットロックや更新タイムアウトが発生した際に、ギャップロックを思い出すと問題解決になるかもしれない。

Java8 ラムダ式でよく利用する関数型インターフェース

関数型インターフェースとは?

  • 単一メソッドを持つインターフェース群
  • ラムダ 式で利用するのに都合がいい
  • SE8からjava.util.functionパッケージに追加された

ラムダ式を引数として取る場合に、関数型インターフェースを指定して受け取る。
ラムダ式の種類毎に用意された入れ物が関数型インターフェースとも言える。

// 例として、filter()は関数型インターフェースを引数に取る
numbersList.stream().filter((i) -> { return i > 0; })

関数型インターフェースとして使用頻度の高いFuction、Consumer、Predicateを見ていく。

Function<T,R>

Function<T,R>のTはメソッドの引数の型、Rは戻り値の型を指定する。
メソッドはR apply(T)を使って、定義した処理に引数を渡して、結果を返却する。

// Functionにラムダ 式(メソッドオブジェクトのようなもの)を代入
Function<Interger, Integer> calcPlusOne = (i) -> { return i + 1 };

// calcPlusOneを使ってみる
Integer result =  calcPlusOne.apply(1); // result = 2

Function<T,R>を引数に取るStreamAPIのメソッドmap

ListString impNum = numbersList.stream().filter((i) -> { return i >= 0; })
                    .map((i) -> { return "*" + i + "*"; })
                    .forEach((s) -> { System.out.print(s + " "); });
                    // *2* *0* *8* 

Function<T,R>は、引数が1つで処理結果を返却したい場合のラムダ式に用いる。

Consumer

ConsumerのTはメソッドの引数の型を指定する。
メソッドはvoid accept(T)を使って、定義した処理に引数を渡して、結果を返却する。

Consumer<String> greeting = (name) -> { System.out.println(name + "さん、おはようございます。"); };
greeting.accept("太郎"); // 太郎さん、おはようございます。

Consumerを引数に取るStreamAPIのメソッドforEach

numbersList.stream().forEach((i) -> { System.out.print(i + " "); }); // Eachは要素の数だけ繰り返す。

Consumerは、引数が1つで処理結果を返却しない場合のラムダ式に用いる。

Predicate

PredicateのTはメソッドの引数の型を指定する。 メソッドは boolean test(T)を使って、定義した処理に引数を渡して、結果を返却する。

Predicate<T> checkBigNum = (num) -> { return num > 10000}
boolean result = checkBigNum(10); // result = false

Predicateを引数に取るStreamAPIのメソッドfilter

// numbersListに-1 と2と8あり
numbersList.stream().filter((i) -> { return i > 0; }) // 負の数を除外
                    .forEach((i) -> { System.out.print(i + " "); });
                    // 2 8 

Predicateは、引数が1つでbooleanを返却する場合のラムダ式に用いる。

Pythonの負荷テストツール Locust

Pythonで負荷テストコードを記載するLocustについて記載します。

特徴

  • シナリオを Python で記述
  • 分散&スケーラブル
  • Web ベース管理画面
  • 高いカスタマイズ性

本家サイト

動かしてみる

テストケース

from locust import HttpUser, TaskSet, task, between, constant

class UserBehavior(TaskSet):
    @task(1)
    def profile(self):
        self.client.get("/sample", verify=False) # パスにはプロトコルやホストは記載しない

class WebsiteUser(HttpUser):
    tasks = {UserBehavior:1}
    min_wait = 5000 #タスクの実行間隔はmin_waitとmax_waitの間のランダム値となる
    max_wait = 15000 # 上書き時に記述。デフォルトは min_wait=500, max_wait=1500 単位はミリ秒

起動

locust
# 以下の省略版
#  locust -f ./locustfile.py

http://localhost:8089でWeb UIにアクセス可能

f:id:tamata78:20210818184631p:plain

参考サイト