図解でなっとく！トラブル知らずのシステム設計 アウトプット

[図解でなっとく！トラブル知らずのシステム設計](https://www.amazon.co.jp/%E5%9B%B3%E8%A7%A3%E3%81%A7%E3%81%AA%E3%81%A3%E3%81%A8%E3%81%8F%EF%BC%81%E3%83%88%E3%83%A9%E3%83%96%E3%83%AB%E7%9F%A5%E3%82%89%E3%81%9A%E3%81%AE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E8%A8%AD%E8%A8%88-%E9%87%8E%E6%9D%91%E7%B7%8F%E5%90%88%E7%A0%94%E7%A9%B6%E6%89%80-ebook/dp/B075ZLS5HR/ref=kwrp_li_std_nodl)

# 1章　一覧画面

OOM　「アウトオブメモリ」　メモリ領域あふれ

### 問題

- 全件表示で大量のデータにならないか
- 検索結果が大量のデータにならないか
    検索条件なしでも検索できる→全件表示になる
    利用者が意図しない検索をする→全件表示になる　(例)1000年~3000年　とか
- 同時に利用する人がたくさんいないか

### 対策

- ページング形式にする
- 表示する件数に上限を設ける
    見たいデータが見られなくならないよう注意
- 利用する人数の把握
- ソートの利用

# 2章　戻る操作

ブラウザ操作と、アプリの戻るボタンがある

### どこに戻るか？

- 設計で戻り先を決めてしまう
- 子画面（子画面が開いていると親画面は触れない）にしちゃう
- 戻るとスクロール位置まで元通りにする　(要望なら)

### 戻るの注意点

- ノーキャッシュの場合、戻るを押すと有効期限切れのため「再度送信しますか？」で送信してしまうと2重送信になってしまう。
    対策：戻れなくする。→キー防止、ツールバー非表示、キーボードの戻る無効
[https://www.pazru.net/js/location/4.html](https://www.pazru.net/js/location/4.html)
    対策：ブラウザの履歴をダミーURLで埋める jsでできる。
    対策：RPGパターンを利用する。2重送信されない。
[https://zenn.dev/imah/articles/3d186a6462ecc8](https://zenn.dev/imah/articles/3d186a6462ecc8)
    対策：トークンチェックで2回目の同じリクエストを無効にする。
    対策：セッションの中身を消す。

    ノーキャッシュのやり方：
[https://www.tagindex.com/html_tag/page/meta_pragma.html](https://www.tagindex.com/html_tag/page/meta_pragma.html)

キャッシュ：一度インターネットから読み込んだデータをディスクに保管しておき、素早くデータを表示できる。

セッション：ページを表示してから、ブラウザを閉じるまでの間のこと。情報はサーバ内に保管される。セッションIDが発行される。

トークン：一定時間ごとに発行されて、１回だけ使えるパスワードのこと。

# 3章　子画面

### 子画面の種類

1. モーダル　子画面を開いたら、親画面は触れなくなる。
意図的に親画面をいじらせたくない時に向いている。
2. モードレス　子画面を開いたら、親画面も子画面も触れる。
子画面と親画面を比べながら作業ができるが、その分意図しない操作が増える。

### モードレスにした結果、意図しない挙動となる

例）買い物カートの子画面A3,000円とB4,000円で、子画面Aを決済したところ、金額が4,000円となってしまった。

モーダルはJSでできそう。　
[https://syncer.jp/jquery-modal-window](https://syncer.jp/jquery-modal-window)

### 原因

- 買い物カートが子画面で複数開ける状態であった。
- 請求金額をセッションで管理していたので、Aの内容がBに上書きされた。

### 対策

- 子画面をやめる。（これでいいなら一番よい）
- モーダルにする。
- 子画面を複数開けないようにする。
- AとBのセッションを分ける
    画面毎にセッションを分けると肥大化する。OOMの危険。

- hidden方式を使う。

    肥大化に注意。
    情報が送信されるので、改ざんされる危険がある。→暗号化する。

# 4章　インデックス・テーブル結合

### レスポンスが遅すぎる問題を解決する

まずは速度為に実行計画を見る。※ANALYZEは処理も実行されちゃうので注意。

![スクリーンショット 2021-05-08 163545.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1044070/b02ffae4-ab4e-1690-a993-962916fbc7a3.png)


### インデックスを使う

- フルスキャン　全部順番見るから当然遅い。
- インデックス　木構造で探すから早い。

    `CREATE INDEX test1_id_index ON table(id);`

    `SELECT schemaname, tablename, indexname FROM pg_indexes;`(where indexname = 'XXX';)

インデックスについて

- 検索条件に使うものにインデックスを付けると早くなる。
- 複合インデックス(カラムABをまとめたインデックス)の場合、検索条件にBだけではなく、先にAも必要になる。
- 部分一致、後方一致は使えない。
- 前方一致でも1文字検索だと意味なし。（何文字以上でないと～の制約つける）
- NULLはインデックスにならない。
- テーブルの結合に使うカラムにインデックスを付ける。

**結合の種類**
Nested　Loop　駆動表で件数を絞り込んだ後、内部表を検索する時。主に使う。
Merge Join　件数を絞り込めないくらい大きいテーブル同士。結合条件でソート。
Hash Join　件数を絞り込めないくらい大きいテーブル同士

# 5章　非正規化・バインド

### 検索にどうしても時間がかかる

例）「今月購入実績がある会員」と抽出しようとすると、データが大量すぎて時間がかかる。

解決策：会員テーブルに「今月購入実績」のフラグカラムを作る。
　　　　あえてカラムを増やすことで解決。

### 同音異義語のカラムに注意

例）商品テーブルの「単価」と購入履歴テーブルの「単価」

一緒の単価だから一緒なのでは？？と思うが、「最新単価」と「購入時の単価」の違いがある為注意が必要。

同音異義語でも、意味を理解する必要がある。

### バインド

DBがSQLを実行するときは「解釈→最適化→実行計画作成→実行計画決定」をしている。

解析には負荷がかかるため、軽減するために同じ内容のSQLであれば解析自体を行わない仕組みをもっている。

- 軽減しないですべて実行すること　ハード解析
- 過去に解析したSQLを再利用すること　ソフト解析

ソフト解析を有効利用するために、バインド変数というものがある。

例）`select * from table where name = ?;`

前回このSQLを実行したことがあれば、バインド変数を`?:木村`として登録してある。
違う名前は`?:田中`をバインド変数とする。
名前を変えるだけなので早い。

**バインドピーク**

前回実行したことのあるSQLなので、バインド変数があって処理も早いはずなので、逆に遅くなる。こんな感じでバインド変数があることによって意図しない実行結果になること。

なんでそんなことになるの？

例）`select * from 会員テーブル where 名前 like '?' and 生年月日 like '?';`

最初の実行でバインド変数が `?:木村` `?:%(入力なし)` 
次の実行でバインド変数がが `?:%(入力なし)` `?:1994年3月31日`

2回目の実行では解析なしで実行されるので、「前回実行したとき使ったバインド変数を参考に実行計画が決定されている。

今回の例だと1回目の名前入力あり、生年月日入力なしの状態なので、次に逆verを実行すると遅くなる。

[『[Oracle] バインド・ピーク機能の落とし穴』](https://ameblo.jp/archive-redo-blog/entry-10042452810.html)

**バインドピークの対策**

動的SQLを使う。
入力条件によってwhereの実行内容を分けて、そもそも違うSQL文にすればよい。
JavaでMyBatisを使ったときはこんな感じで分けるとよいみたい。

[MyBatisで動的SQL文を作る【複数のワードで検索できるようにする】 - Qiita](https://qiita.com/oxxxkura/items/0f0e74e8cd25bf13d506)

結合するテーブルや結合順序が変わらない場合は動的SQLを使ってOK。
無策だとSQL文がどんどん増えるので注意。

# 感想

- 利用者が意図しない動作をすることを前提に考える（フールプルーフ：基本情報でやった！）
- 利用者の「やりたいこと」をうまくくみ取る。
- OOMの危険を考える。
- 要件を満たしているかだけではなく、速さや使いやすさを考える。
- DBの正規化を崩すことによるメリットもある。
- DBで困ったらフラグを立てて管理するとよいこともある。
- DBでうまくインデックスを使う。
- 動的SQLを使う。

知らん事ばっかりで為になりました。
無知って怖い。
口竹先輩がすきだった笑