C#でリレーショナルDBの再発明やってるけどさ

ビルの町並み・オフィス街のイラスト(いらすとや) 技術
出典:いらすとや(ビルの町並み・オフィス街) https://www.irasutoya.com/2013/11/blog-post_4111.html
1:名無しさん:2026/09/15 21:54:58 ID:izCOh8T70.net

AI使えばマジで簡単にできるなこれ
2:名無しさん:2026/09/15 21:55:29 ID:izCOh8T70.net

こんな単純なもんだったとは思わなんだ
3:名無しさん:2026/09/15 21:55:37 ID:tEPwdsdx0.net

AI使うと高度なプログラムが一瞬でできて面白いよな。
4:名無しさん:2026/09/15 21:56:40 ID:izCOh8T70.net

>>3
ちょっと前までは低レベルのプログラムは作れないと思った(情報が無いから)てたけど最近ちゃんと作れるようになってて成長を感じる
5:名無しさん:2026/09/15 21:57:49 ID:izCOh8T70.net

C#でメモリをそのまま保存して、再起動した時にメモリをそのままロードしたいんだが出来る?って言ったら普通にポインタ方式のメモリアロケータ作ってくれた
6:名無しさん:2026/09/15 21:58:15 ID:8rOUIebJ0.net

AI使ってやると
なにも考えなくなっちゃう
7:名無しさん:2026/09/15 21:58:52 ID:izCOh8T70.net

>>6
わかる
作ったの見るだけになる
8:名無しさん:2026/09/15 22:00:09 ID:izCOh8T70.net

流石にSQLパーサーとか作ろうとはならんけど外部からC#スクリプトでデータ取得できるところまでは頑張ろうと思う
9:名無しさん:2026/09/15 22:02:10 ID:kyEiJ2rqd.net

ググった時のAIは電力くうのかね
いつも表示されるから選択できたらと思ってしまう
14:名無しさん:2026/09/15 22:12:14 ID:BjRO9xt00.net

で、採用したシステムでハッキング被害とかあったら責任とれんの?
15:名無しさん:2026/09/15 22:13:23 ID:zWEmr65K0.net

何言ってんのかよう分からんけどC#ってC#.NETありきでSQLServerありきなところあるじゃん
18:名無しさん:2026/09/15 22:20:45 ID:izCOh8T70.net

>>15
そうそう
だからそのSQLServerを作るところから始めてみたってわけ
21:名無しさん:2026/09/15 22:22:43 ID:zWEmr65K0.net

>>18
ん?DBエンジン作ろうとしたの?
そりゃ頑張ったなw
28:名無しさん:2026/09/15 22:25:57 ID:izCOh8T70.net

>>21
意外と簡単にできた
正直ビビってる
20:名無しさん:2026/09/15 22:21:58 ID:izCOh8T70.net

逆に聞くけどMySQLやPostgreSQLがハッキング対策してると思ってる人?
オープンソースなのに?
22:名無しさん:2026/09/15 22:23:02 ID:mzPrXSgV0.net

>>20
そりゃしてるだろ
23:名無しさん:2026/09/15 22:23:11 ID:BjRO9xt00.net

>>20
逆になんでしてないと思ってんの?
25:名無しさん:2026/09/15 22:24:09 ID:zWEmr65K0.net

>>20
クラッキング経験あるけどDBエンジンにそこまで求めるのはかわいそうよ
アプリケーションサーバー側で防げよといいたい
31:名無しさん:2026/09/15 22:26:35 ID:izCOh8T70.net

>>25
これ
24:名無しさん:2026/09/15 22:23:52 ID:mzPrXSgV0.net

オープンソースだから脆弱性メンテナンスされてないと思ってるのか
むしろオープンソースこそ色んな人にめちゃくちゃチェックされてるだろ
26:名無しさん:2026/09/15 22:25:34 ID:izCOh8T70.net

最初に聞いたこと答えてくれたやつ
このあとソースコード作ってもらってそれで動いた

結論から言うと、**「現在のメモリ構成をそのままメモリコピーしてpageに振り分ける」だけでは、後で正しく読み戻せない可能性が高い**です。

### なぜそのままコピーだけではダメなのか

メモリ上のデータ構造(特にリレーショナルDBでよく使うもの)には、次のような「位置依存」の要素が普通に含まれます。

1. **ポインタ(アドレス)**
– レコード間のリンク、B+Treeの子ノード、ハッシュテーブルのバケットチェーンなど、ほぼすべてがポインタでつながっています。
– メモリコピーした時点のアドレスは、再ロードしたときに別の場所に配置されるため、無効になります。

2. **相対アドレスやオフセットが混在している場合**
– 一部の実装では「ページ内オフセット」を使っていますが、完全にポインタで書かれている場合は修正が必要です。

3. **アロケータ依存の構造**
– スラブアロケータやオブジェクトプールを使っていると、メタデータやフリーリストもアドレスに依存しています。

4. **エンディアンやアラインメント**
– 同じマシンなら問題になりにくいですが、将来的に別環境で動かす場合は影響します。

つまり「そのままmemcpyしてディスクに書いたものを、別のアドレス空間に読み込んだらそのまま動く」という保証はありません。

### 正しくやるための一般的なアプローチ

| 方法 | 内容 | 速度感 | 実装難易度 |
|——|——|——–|————|
| **ポインタのスウィズル(swizzling)** | 保存時にポインタを「ページID + オフセット」に変換し、読み込み時に実際のアドレスに戻す | 比較的高速 | 中~高 |
| **完全に相対アドレスで設計する** | 最初からポインタを使わず、すべて「ベースアドレス + オフセット」で表現する | 最速に近い | 高(設計段階から必要) |
| **シリアライズ(flatten)する** | メモリ構造を一度線形なバイト列に変換して保存し、読み込み時に再構築する | やや遅い | 中 |
| **mmap + 固定アドレスマッピング** | ページを常に同じ仮想アドレスにマップする(稀) | 非常に高速 | 高(OS依存が強い) |

実務的には**「ポインタスウィズル」か「最初から相対アドレス設計」**のどちらかが主流です。

### おすすめの考え方

– **速度を最優先したい場合**
「ページ内はすべて相対オフセットで持つ」設計にすると、memcpyでほぼそのまま書き出せます。読み込み後もアドレス変換が最小限で済みます。

– **実装を簡単にしたい場合**
まず普通にシリアライズ(レコードを1つずつ書いていく)方式で動くものを作り、後から「メモリイメージをそのまま書く」方式に移行するのが安全です。

– **検証方法**
1. メモリ上でデータを作る
2. ページに書き出す
3. 完全にメモリを解放する
4. 別のアドレス空間に読み込む
5. すべてのポインタを辿ってデータが壊れていないかチェックする

この「完全に解放してから読み戻す」テストをしないと、偶然同じアドレスに載って動いてしまうことがあります。

### まとめ

– そのままメモリコピー → **そのままでは正しく取得できない可能性が高い**(ポインタが壊れる)
– 正しくやるには **ポインタを相対化(スウィズル)する** か **最初から相対アドレスで設計する** 必要がある
– 速度を求めるなら「ページ内相対オフセット」設計が最も有利

もし今のメモリ構造(B+Treeなのか、ハッシュなのか、レコードの持ち方など)がもう少し具体的に分かれば、よりピンポイントなアドバイスができます。どんな構造を想定していますか?

34:名無しさん:2026/09/15 22:31:33 ID:izCOh8T70.net

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

// ============================================================
// 定数・共通型
// ============================================================
public static class DbConst
{
public const int PageSize = 4096;
public const int PageHeaderSize = 64;
}

public enum PageType : byte
{
Meta = 0,
Index = 1, // B+Treeノード
Data = 2, // レコード・テーブルヘッダなど
Catalog = 3, // テーブルカタログ用
Free = 255
}

public enum ColumnType : byte
{
Int32 = 1,
String = 2
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PagePtr
{
public int PageId;
public ushort Offset;

public static PagePtr Null => new() { PageId = -1, Offset = 0 };
public bool IsNull => PageId < 0;
public override string ToString() => IsNull ? “Null” : $”({PageId}:{Offset})”;
}

35:名無しさん:2026/09/15 22:31:44 ID:aLdp4UPY0.net

メモリ上で読み書きするって普通に昔からあるベタな手法だろ
半沢直樹2期の0話で吉沢亮がクライアントに提案してたシステム高速化手法にもそれ入ってた
41:名無しさん:2026/09/15 22:38:07 ID:WDgp5J1E0.net

>>35
昔はエムアロックやらフリー関数でシコシコやってたなw
ていうかなんかイッチくんがせっせとコード貼ってるの尊いわw
36:名無しさん:2026/09/15 22:33:44 ID:Nm9eNjZV0.net

まあ楽しそうでなにより
37:名無しさん:2026/09/15 22:34:01 ID:izCOh8T70.net

// ============================================================
// ページ
// ============================================================
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PageHeader
{
public int PageId;
public PageType Type;
public ushort Used;
public ushort ItemCount;
public int NextPageId; // 汎用(Catalogの場合は次のカタログページ)
public int Checksum;
}

public sealed class Page
{
public byte[] Data { get; } = new byte[DbConst.PageSize];

public ref PageHeader Header =>
ref Unsafe.As<byte, PageHeader>(ref Data[0]);

public Page(int pageId, PageType type)
{
Header.PageId = pageId;
Header.Type = type;
Header.Used = (ushort)DbConst.PageHeaderSize;
Header.ItemCount = 0;
Header.NextPageId = -1;
Header.Checksum = 0;
}

public Page(byte[] raw)
{
if (raw.Length != DbConst.PageSize)
throw new ArgumentException(“Invalid page size”);
Buffer.BlockCopy(raw, 0, Data, 0, DbConst.PageSize);
}

public int Allocate(int size, int alignment = 8)
{
int aligned = (Header.Used + alignment – 1) & \~(alignment – 1);
if (aligned + size > DbConst.PageSize)
return -1;

int offset = aligned;
Header.Used = (ushort)(aligned + size);
Header.ItemCount++;
return offset;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Get<T>(int offset) where T : unmanaged
=> ref Unsafe.As<byte, T>(ref Data[offset]);

public Span<byte> GetBytes(int offset, int length)
=> Data.AsSpan(offset, length);

public byte[] ToArray() => (byte[])Data.Clone();
}

39:名無しさん:2026/09/15 22:37:55 ID:aLdp4UPY0.net

「インメモリDB」ってやつがそれ
43:名無しさん:2026/09/15 22:41:01 ID:izCOh8T70.net

>>39
インメモリDBは知ってる
高速なのも知ってる
ただその仕様上電源を落とすとデータが飛ぶから永続で使用できるようにストレージ方式にした
あとメモリが多くないと大きなデータは保存できないからね
40:名無しさん:2026/09/15 22:37:59 ID:izCOh8T70.net

すまん、改行が多すぎてソース乗せられないわ
42:名無しさん:2026/09/15 22:39:14 ID:LXz4WqsE0.net

そういえばRDBとかここ10年使ってないな
44:名無しさん:2026/09/15 22:42:01 ID:izCOh8T70.net

>>42
今めっちゃ使ってる
オブジェクトDBって結合(Join)出来ないけどその辺不満ない感じ?
オブジェクトから全部辿って行く感じなのか?
47:名無しさん:2026/09/15 22:47:41 ID:WDgp5J1E0.net

>>44
ORマッパーのこと?それRDBとベッタリしてるやつじゃん☺ベタベタだよ
48:名無しさん:2026/09/15 22:51:13 ID:izCOh8T70.net

>>47
ORマッパーはSQLのデータをプログラムのデータ構造に入れるやつだよね?
オブジェクト型DBってのが最近あってこいつがORマッパーが無いけどORマッパーやったみたいにプログラムで扱える便利なやつって認識で最近使われ出してる
聞くところによるとJoinみたいなテーブル同士で結合するのがあんま得意でないというか出来ないからその辺どうやってんだろうな?って思って
45:名無しさん:2026/09/15 22:45:36 ID:WUwmS2e3d.net

>>42
nosqlとかハドゥープとかまだ生きてるの?
最近めっきり聞かなくなった…
それとは違ったやつが最前線では台頭して来てたりするのかしら?
46:名無しさん:2026/09/15 22:47:31 ID:izCOh8T70.net

俺の職場古いシステム使ってるからSQLから逃れられない(電力系)
49:名無しさん:2026/09/15 23:00:24 ID:Nm9eNjZV0.net

まあコイツがEF Coreの呪いを解くかもしれんし
50:名無しさん:2026/09/15 23:01:47 ID:WDgp5J1E0.net

東電のシステム会社さまとかフレームワークを独自にカスタマイズして引きこもり仕様にしてるの見たことあるけど
そんなやつだろうな
知らんけどw
複雑な結合いらんRDBもあるんよインフラなんてw
52:名無しさん:2026/09/15 23:03:48 ID:izCOh8T70.net

>>50
いや俺はただのSIerだよ
派遣で既存のシステムを使わざるを得ない人
派遣先が電力系ってだけ
54:名無しさん:2026/09/15 23:06:38 ID:izCOh8T70.net

電力系って言ってもその会社で使われる事務的システムの開発な
この機械使う人が予約してるからこの機械は今使えませーんみたいなアプリを開発したり保守したり
まぁ基本保守がメイン

引用元URL:https://viper.2ch.sc/news4vip/1789476898/l50

コメント