osio_sioの日記

自分用メモ

C++の配列宣言時のメモリ確保についてメモ

c++の配列を宣言した時のメモリについて把握していなかった部分でエラーが発生したのでメモ✍️

発生した問題

ある時、

int main() {
   ~略~
   long long dp[109][100009];
}

という感じでmain関数内でdpを宣言したところ、segmentation faultになってしまい、何が原因かよくわからなかったが、以下のようにグローバル変数として宣言すると直った。

long long dp[109][100009];
int main() {
   ~略~
}

原因

c++のメモリの確保方法が原因だった。

関数内(ローカル変数)で宣言するとその配列はスタック領域に確保されるらしい。スタックは通常、そのサイズが限定されており(多くのシステムで数MB程度)、大きな配列を配置しようとするとスタックオーバーフローを引き起こし、結果的にセグメンテーションフォールトが発生することがある。

一方で、同じ配列をグローバル変数として宣言する場合、この配列はプログラムのデータセグメントに確保される。データセグメントはスタックよりも大きな容量を持っており、さらに、オペレーティングシステムによって動的にサイズが拡張されることがある。そのため、大きな配列も安全に確保することができる。

セグメンテーションフォルトを防ぐ解決策

  1. 上に述べたように、グローバル変数として宣言する

  2. 動的メモリ割り当てを使用する。vectorを使用してヒープメモリに確保すれば、ヒープは使用可能なメモリ(物理メモリと仮想メモリの合計)内で拡張が可能であるため、非常に大きなデータ構造も扱うことができる。

おまけ(初期値について)

c++において初期値も、グローバル変数として宣言するときとローカル変数で宣言する時に違いがある。 結論から言うと、グローバル変数の場合は初期化されるが、ローカル変数で宣言した時は未定義の値を持つので、注意が必要。

int a[10];

int main() {
    cout << a[3];
}

出力結果:0

int main() {
    int a[10];
    cout << a[3];
}

出力結果:32759

達人に学ぶDB設計徹底指南書 [正規化][パフォーマンス]

「達人に学ぶDB設計徹底指南書」を読んだので、改めて第3章の正規化、第5章のパフォーマンスについてメモ。

正規化

DBで保持するデータの冗長性を排除し、一貫性と効率性を保持するためのデータ形式

第1正規形

1つのセルの中には1つの値しか含まない。リレーショナルデータベースのテーブルはすでに満たしている。

なぜ1つのセルに複数の値を入れてはダメなのか?
→ セルに複数の値を許せば、主キーが各列の値を一意に決定できないため

関数従属性 ✍️
X列の値を決めれば、Y列の値が1つに決まる。正規化とは、テーブルのすべての列が、関数従属性を満たすように整理していくこと。

第2正規形

テーブル内で部分関数従属を解消し、完全関数従属のみのテーブルを作る。

部分関数従属 ✍️
たとえば、主キーが{会社コード、社員ID}の時、「会社名」の列は主キーの一部である「会社コード」に依存する。

解決策
テーブルの分割を行う。部分関数従属の関係にあるキー列と従属列だけ独立のテーブルにすればいい。

なぜ行うのか
以下のように、社員の情報が不明の会社を登録することができる。また、{C0001,A商事}というレコードの他に、{C0001,A商社}のような間違ったデータが登録される危険があるが、このようなデータ誤登録の危険を格段に減らすことができる。 (p.96より引用)

→ 「会社」と「社員」という、それぞれに異なるレベルの実体を、きちんとテーブルとしても分離してやる

第3正規形

上のテーブルを見てみると、
{社員ID} → {部署コード} → {部署名}
という推移的関数従属がある。そのため、たとえば社員が一人もいない部署を登録できない。

→テーブルを分割することで関数従属の関係を独立させる。(ここでは部署テーブルを新しく独立させる)

ちなみに、正規形はいつでも非正規形に戻せる。 (4次、5次正規形はここでは割愛する)

ざっくりまとめ

正規化の利点 >>

  • データの冗長性が排除され、更新時の不整合を防止できる。

  • データの持つ意味が明確になり、開発者が理解しやすい。

正規化の欠点 >>

  • テーブルの数が増えるため、SQL文で結合を多用することになり、パフォーマンスが悪化する。(このため、パフォーマンスを求めて非正規化を行うこともあるが、あくまで最後の手段とすべき)

認証周りの知識メモ

知識周りは忘れがちなので、メモに残しておこうと思います。
ここでは認証周りを扱います。追記もあればしていきます。

firebase authentication

ユーザー認証機能を提供し、ユーザ情報をクラウドで保存してくれる、Google運営のサービス。
セキュリティリスクを回避できることや、さまざまな認証方法に対応していること、重要情報をクラウドに預けることによる安全性の向上など、さまざまなメリットがあります。

OAuth 2.0

サービスのユーザーが、サービス上にホストされている自分のデータへのアクセスを、自分のクレデンシャルズ (ID & パスワード) を渡すことなく、第三者のアプリケーションに許可するためのフレームワーク
以下のサイトがとてもわかりやすいです。
一番分かりやすい OAuth の説明 #OAuth - Qiita

jwt

JSON Web Token」の略で、Webアプリケーション間で情報を安全にJSONオブジェクトとして交換するためのコンパクトでURLセーフな手段を提供する。ヘッダ、ペイロード、署名の3つから成る。
流れの参考:【JWT】 入門 #認証 - Qiita
自分で実装する場合は以下参考。ただし、勉強のために実装するのはありかもしれませんが、セキュリティ面で考慮すべきことが多く、基本は外部サービスを使う方が良さそうです。 【Go】RedisとJWTでログイン認証システムを作ってみた話
GoでJWTを使って認証してみる #Go - Qiita
Go言語でJWT認証をやってみた!

SSO

Single Sign On、1度のユーザー認証で業務アプリケーションやクラウドサービスといった、複数のシステムの利用が可能になる仕組みのこと。 ref: https://www.ntt.com/bizon/glossary/e-s/sso.html

認証

誰であるか(Who one is.)を扱う。本人確認のこと。

認可

誰が誰に何の権限を与えるか(Who grants what permissions to whom.)を扱う。(「誰が」を決める処理が「認証処理」であるため、認可処理にはその一部として認証処理が含まれている) わかりやすいサイト:
OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る #OAuth - Qiita

MFA

多要素認証のこと。アプリケーション、オンラインアカウント、VPNなどのリソースへのアクセスを許可する前に、ユーザに2つ以上の認証要素の提供を求める認証方法です。MFAは、強力なIDおよびアクセス管理(IAM)ポリシーのコアコンポーネントです。MFAは、ユーザ名とパスワードだけでなく、1つ以上の追加の検証要素を要求します。これにより、サイバー攻撃が成功する可能性を小さくできます。
ユーザ名とパスワードは重要ですが、ブルートフォース攻撃に対して脆弱であり、第三者に盗まれる可能性があります。親指の指紋や物理的なハードウェアキーといったMFA要素の使用を強制することで、組織はサイバー犯罪から安全に守られているという自信を高めることができます。ユーザに求められる最も一般的なMFA要素の1つは、ワンタイムパスワード(OTP)が挙げられます。

Basic認証

HTTPに元々用意されている認証機能の一つ。簡易的な認証方式で、Webアプリケーションの特定のページやファイルに対して手軽にアクセス制限をかけることができます。
Basic認証によりアクセスが許可されるまでの流れ

  1. Basic認証でアクセス制限がかけられたページに、ユーザーがアクセスを試みる。
  2. アクセス制限がかけられているため、Webサーバーはブラウザに対して認証が必要であることを知らせる。
  3. 画面にダイアログが表示され、認証情報(IDとパスワード)の入力が求められる。
  4. 認証情報が正しいと認められれば、ページへのアクセスが可能となる。認証情報はブラウザに保存され、それ以後のアクセスでは認証情報が自動で送信される。

しかし、認証情報(IDとパスワード)はBase64エンコードされてブラウザに保存されるのですが、パスワードが容易に復号されてしまう可能性があり、Basic認証は一般的には現在はあまり利用を推奨されていないようです。

Basic認証とは何ぞや? ~ざっくりと解説~ #Web - Qiita

ECS/EKSについての事前学びメモ

ECS/EKSを使用する1dayイベントがあるのですが、インフラ周りの知識が全くなく、ECS/EKSが何かも全然わかっていないので、軽く勉強してメモしておこうと思います。

ECS/EKSとは

EKS:
Amazon Web Services (AWS) 上で、独自の Kubernetes コントロールプレーンをインストール、運用、保守する必要がないマネージド型サービス。EKSはとにかく運用コストが高い。ちなみにKubernetes は、コンテナ化されたアプリケーションの管理、スケーリング、デプロイを自動化するオープンソースシステム
ECS:
AWSが開発したコンテナ管理サービス

webアプリケーションとDBを動かすときのAWSの構成

(ref: udemyのAWSコンテナサービス入門―AWSの基本からECS・Copilot CLI・CI/CD・App Runnerまで)

  1. VPC(ネットワーク)の中にEC2(サーバ)を1台用意して、その中でwebアプリとDBを動かす
    →パッチやバックアップなど、対応すべきことが非常に多い

  2. AWSにはRDSというデータベースのサービスがある
    →RDSを使うとDBのパッチ適用などをAWSに任せることができる
    RDSのようにAWSなどのクラウド事業者に管理を任せられるサービスを「マネージドサービス」という。

  3. EC2を並列稼働させ、webアプリの処理能力を高める(スケールアウト) →CPU使用率などに応じて、自動でスケールさせることが可能

  4. EC2を並列稼働させる場合は、前段にELB(ユーザからのリクエストをEC2などに分配するロードバランサのマネージドサービス)を配置するのが一般的

→最近はEC2をECS on Fargateに置き換えた構成が非常に一般的

なぜECS on Fargateを使うのか

そもそもコンテナを使うわけ
環境構築済みのイメージを運搬して実行するため、他の環境でテストした動作を本番でも保証しやすいこと、サーバよりも軌道が高速で開発サイクルの高速化やオートスケールといった面で有利なこと、サーバ内で環境を分離でき1つのサーバ内でさまざまなアプリケーションを動かしても競合しにくいなどの利点がある

1台のサーバを増強してたくさんのコンテナを動かす方針では、サーバのスペックの限界がある。また、サーバが1台停止するだけで、その中でコンテナとして動いているwebアプリケーションなどにアクセスできなくなる

複数台のサーバでたくさんのコンテナを使えば、コンテナの数が増えても、その分サーバを増やすことで対応可能。サーバ間でコンテナを冗長化させておけば、サーバが1台停止してもwebアプリケーションは全体としては停止しない

複数のサーバでたくさんのコンテナを使うためには、どのサーバにどのコンテナを配置するのが適切か考えるのが大変

コンテナオーケストレーションのツールを使えば、クラスタに対して、コンテナを適当に配置してくれる
コンテナオーケストレーションのツール
AWSを使う場合は、以下の2つの選択肢がある
EKS : k8sのマネージドサービス
ECS:AWS独自のコンテナオーケストレーションサービス

ECS on EC2を使う場合、コンテナとサーバの両方の考慮が必要

ECS on Fargateを使うと、サーバ管理なしでコンテナを実行できる

ECSの基本概念

クラスタ :
まとめて扱う複数のコンピュータの集まりのこと。ECSではクラスタという単位でサーバをまとめておいて、そのサーバに対して適当にコンテナを配備してもらう。ECS on Fargateを使うとき、サーバはAWSが管理してくれるので空のクラスタを作成する

タスク: アプリケーションの実行単位。1つ以上のコンテナから構成される。 タスク定義としては、CPUやメモリのサイズ、コンテナのポートや環境変数がある。 ECSでコンテナを実行するときは、タスク定義という設定をもとにタスクを起動する

サービス: タスク定義からタスクを起動する方法の一つ。起動するタスクの数やロードバランサーなどを設定

ELB

ロードバランサーのサービスで、HTTPなどのアクセスを複数のサーバやコンテナに負荷分散できる。webアプリの場合、HTTPに特化したALBを使うことが多い

ref: EKSとECSの違い #AWS - Qiita
Amazon EKS とは - Amazon EKS
今さら聞けないAWS ECSとは?Fargateとは? #AWS - Qiita
AWS入門 - ECS・Fargate 使ってみる
AWSのロードバランサ機能・使い分けについて(ALB作成手順付き)|クラウドテクノロジーブログ|ソフトバンク

RDBとNoSQLの違いまとめ

RDBとNoSQLの違いについて、学んだことをまとめてみる。

そもそもRDB、NoSQLとは?

RDB :
表形式の複数データを関連付けて使えるようにしたデータベースのこと。データを取り出したいときは、RDBMSリレーショナルデータベース管理システム)という専用のシステムで、必要なデータを引き出すことができる。
NoSQL :
RDB以外のデータベース。NoSQLは従来のRDBでは扱いきれないビッグデータや音声データや画像データといったデータを扱うことができる。

それぞれの特徴

RDB :

  1. RDBのデータを操作するときは「SQL」というデータベース言語を使い、複雑な検索・集計といった高度なデータ処理ができる

  2. データに一貫性がある(=データの重複や欠落がなくデータの整合性が保たれた状態)
    RDBMSは、RDBのデータの一貫性を維持するためにACIDという特性を持って運用を行う

NoSQL:
RDBには、以上のような利点がある一方で、ひとつのサーバーで稼働することを前提にしており、システムが大規模になったりクラウドシステムで稼働させたりすると処理速度が遅くなるといった欠点がある。その欠点を補うデータベースとしてSQLを使わないデータベースであるNoSQLがある。さまざまな形式でデータを格納できる。

例:

  1. キー・バリュー型
    高速に処理でき、容量も少なくてすむが、複雑な検索はできない。
    キャッシュサーバーやログの蓄積に向く。

2.カラム指向型
「キー・バリュー型」にカラム(列)を加えたデータベース型。
列方向の操作が容易にでき、大規模データの処理、ログの蓄積や分析に向いている。

  1. ドキュメント指向型
    XMLJSONなどのドキュメント形式でデータを持つデータベース型。
    複雑なデータをそのまま扱えるうえ、データ形式の変更がしやすい。Webシステムやオンラインゲーム、ログ分析などで活用。

RDBの向き不向き

RDBMSトランザクションという処理単位で実行される。上でも少し触れたが、トランザクション処理は ACID 特性に基づいて実装される。

  • Atomicity(原子性)
  • Consistency(一貫性/整合性)
  • Isolation(独立性)
  • Durability(永続性)

一方でNoSQLはCAP 定理 (CAP定理とは何ですか?| IBM)

という考えを採用している。CAP は 以下の 3 点を全て満たすことは不可能という定理。

  • Consistency(一貫性/整合性)
  • Availability(可用性)
  • Partition-tolerance(分断体制)

RBMS は CAP 定理における C と A に対応しているが、DB の用途によっては A と P の部分が重要になる可能性もある。そこで、NoSQL は BASE 特性に基づいて実装される。

BASE(Basically Available, Soft-state, Eventual consistency)特性

  • Basically Available:基本的に利用可能
  • Soft-state:厳密でない状態
  • Eventual consistency:結果整合性
  • 最終的な整合性を担保する

可用性を重視するために、複数の DB サーバーで冗長的にデータを保持する際に一時的に一貫性のない状態になりうる(結果整合)ことがあり、その際に CAP の C の部分が失われる可能性がある。

それぞれの使い分け

RDBMS のメリットとしてテーブル同士がさまざまな関係・関連を持つシステムには強い。一方で、処理性能を向上させることは難しい。データの関連などを表現するのには向いているが、サーバーの分散性ができず、データの拡張性が低い。逆に、NoSQL はデータの整合性を保つことは放棄しているため、複数サーバーへの水平分散が可能で、拡張性も高い。大規模のアクセス、水平スケールなどが得意な一方、テーブル間の複雑な関連は難しい。

ざっくりしたまとめ

RDBはデータの一貫性を保ち、高度な検索・集計処理をしたい場合、NoSQLは大容量データや多様なデータの高速処理に向いている。

参考記事:
business.ntt-east.co.jp

apiのパスの修正について考えた

func main() {
    // 略

    engine.POST("/todo/create", todoHandler.HandleTodoCreate)
    engine.GET("/todo/get", todoHandler.HandleTodoGet)
    engine.POST("/todo/update/:id", todoHandler.HandleTodoUpdate)
    engine.DELETE("/todo/delete/:id", todoHandler.HandleTodoDelete)

    /* ===== サーバの起動 ===== */
    log.Println("Server running...")
    engine.Run(":8080")
}

こんな感じでコードを書いていたのですが、「apiのパスが一般的なrestのpath設計となっていない」というコメントを頂き、何が良くないのか考えてみました。

REST APIエンドポイントの命名について✍️

調べていくうちにわかったこと:

  1. 一般にURIは、実行される機能を表す動詞を付加するのではなく、リソースの内容を特定する名詞での命名が望ましい。 例えば、engine.POST("/todo/create", todoHandler.HandleTodoCreate)におけるcreateは必要ないということです。これは、CRUD機能がすでにPOSTのように、httpリクエストで指定されているためです。 (ただし、REST の構造にマッチさせられないアクションは、/search のようなエンドポイントを作ることもあります)
  2. 直感的でわかりやすく、省略形でない名称を使う
  3. _やキャメルケースを使うのではなく、ハイフンを使う
  4. 小文字を使う
  5. ファイル拡張子を避ける
  6. KISS の原則に従い、エンドポイントは一貫して複数形を使う

以上のことから、

engine.POST("/todos", todoHandler.HandleTodoCreate)
engine.GET("/todos", todoHandler.HandleTodoGet)
engine.PUT("/todos/:id", todoHandler.HandleTodoUpdate)
engine.DELETE("/todos/:id", todoHandler.HandleTodoDelete)

このように直すと良さそうだとわかりました。

qiita.com

GOの`json.Unmarshal`と`json.NewDecoder`の違い

はじめに

調べていると、JSON ファイル → Go オブジェクトの際に、このサイトのようにNewDecoderを使う場合と、このサイトのようにUnmarshalを使う場合があり、違いがよくわからなかったのでまとめてみました✍️

参考記事:https://qiita.com/Coolucky/items/44f2bc6e32ca8e9baa96

結論

記事にある通りなのですが、 ストリームから情報を読み込んだ時はjson.NewDecoder、 それ以外の場合はjson.Unmarshalを使います。

理由

ストリームから情報を読み込んだ時は、json.Unmarshal() ではデータを途中で持たなくてはならないため、基本NewDecoderを使うことになるようです(一応 json.NewDecoder() も内部的にデータを持っているが、データの持ち方が効率化されているので、こちらの方が良いらしい)。

イメージとしては、
NewDecoderの場合
ファイル→(os.Open()) →io.Reader →json.NewDecoder()→data
Unmarshalの場合
ファイル→(os.ReadFile())→byte →json.Unmarshal →data

といった流れになっていて、上の場合はjson.NewDecoderでデータの読み取り、structへのmapを行っています。一方下の場合はos.ReadFile()でデータの読み取りを行い、json.Unmarshal()でstructへのmapを行っていて、byteでデータを保持する上でメモリを使う必要があります。 そのため、このような使い分けをするといいようです。