[{"data":1,"prerenderedAt":1037},["ShallowReactive",2],{"content:\u002Fja\u002Fprojects\u002Fmydb\u002Fmydb5":3,"series:content_ja":318},{"id":4,"title":5,"authorship":6,"body":7,"categories":289,"date":291,"description":292,"draft":293,"extension":294,"image":295,"meta":296,"navigation":298,"path":299,"permalink":300,"published":300,"readingTime":301,"recommend":300,"references":300,"seo":306,"seoDescription":307,"seoTitle":300,"sitemap":308,"stem":311,"tags":312,"type":316,"__hash__":317},"content_ja\u002Fposts\u002Fprojects\u002Fmydb\u002Fmydb5.md","MYDB 5. ページインデックスとDMの実装","human-only",{"type":8,"value":9,"toc":281},"minimark",[10,33,37,40,43,46,49,52,63,66,72,75,78,84,87,93,96,100,103,106,112,115,118,126,129,136,142,157,163,168,171,177,181,184,191,197,200,206,209,215,218,224,235,240,246,251,257,260,266,269,272,278],[11,12,13,14,21,22,21,27,32],"p",{},"この章で扱うコードは",[15,16,20],"a",{"href":17,"rel":18},"https:\u002F\u002Fgithub.com\u002FCN-GuoZiyang\u002FMYDB\u002Ftree\u002Fmaster\u002Fsrc\u002Fmain\u002Fjava\u002Ftop\u002Fguoziyang\u002Fmydb\u002Fbackend\u002Fdm\u002FpageIndex",[19],"nofollow","backend\u002Fdm\u002FpageIndex","、",[15,23,26],{"href":24,"rel":25},"https:\u002F\u002Fgithub.com\u002FCN-GuoZiyang\u002FMYDB\u002Ftree\u002Fmaster\u002Fsrc\u002Fmain\u002Fjava\u002Ftop\u002Fguoziyang\u002Fmydb\u002Fbackend\u002Fdm\u002FdataItem",[19],"backend\u002Fdm\u002FdataItem",[15,28,31],{"href":29,"rel":30},"https:\u002F\u002Fgithub.com\u002FCN-GuoZiyang\u002FMYDB\u002Ftree\u002Fmaster\u002Fsrc\u002Fmain\u002Fjava\u002Ftop\u002Fguoziyang\u002Fmydb\u002Fbackend\u002Fdm",[19],"backend\u002Fdm","にあります。",[34,35,36],"h3",{"id":36},"はじめに",[11,38,39],{},"今回はDM層の仕上げです。シンプルなページインデックスを紹介し、DMが上位へ提供する抽象化であるDataItemも実装します。",[34,41,42],{"id":42},"ページインデックス",[11,44,45],{},"ページインデックスは、各ページの空き容量をキャッシュします。上位モジュールが挿入するとき、ディスクやキャッシュ内の全ページを調べずに、十分な空きのあるページを素早く見つけるためのものです。",[11,47,48],{},"MYDBでは、ページの容量を40の区間に分ける、かなり大まかなアルゴリズムを使います。起動時に全ページの情報を走査し、空き容量に応じてこの40区間へ振り分けます。insertがページを要求するときは、必要な容量を切り上げて区間へ対応づけます。その区間からどのページを選んでも、必要な容量を満たせます。",[11,50,51],{},"PageIndexの実装も単純で、Listの配列です。",[53,54,60],"pre",{"className":55,"code":57,"language":58,"meta":59},[56],"language-java","public class PageIndex {\n    \u002F\u002F 将一页划成 40 个区间\n    private static final int INTERVALS_NO = 40;\n    private static final int THRESHOLD = PageCache.PAGE_SIZE \u002F INTERVALS_NO;\n\n    private List[] lists;\n}\n","java","",[61,62,57],"code",{"__ignoreMap":59},[11,64,65],{},"PageIndexからの取得も、区間番号を計算して取り出すだけです。",[53,67,70],{"className":68,"code":69,"language":58,"meta":59},[56],"public PageInfo select(int spaceSize) {\n    int number = spaceSize \u002F THRESHOLD;\n    if(number \u003C INTERVALS_NO) number ++;\n    while(number \u003C= INTERVALS_NO) {\n        if(lists[number].size() == 0) {\n            number ++;\n            continue;\n        }\n        return lists[number].remove(0);\n    }\n    return null;\n}\n",[61,71,69],{"__ignoreMap":59},[11,73,74],{},"返されるPageInfoには、ページ番号と空き容量が入っています。",[11,76,77],{},"選ばれたページは、PageIndexから直接取り除かれる点に注目してください。つまり、同じページへの並行書き込みは許可しません。上位モジュールが使い終えたら、そのページをPageIndexへ戻す必要があります。",[53,79,82],{"className":80,"code":81,"language":58,"meta":59},[56],"public void add(int pgno, int freeSpace) {\n    int number = freeSpace \u002F THRESHOLD;\n    lists[number].add(new PageInfo(pgno, freeSpace));\n}\n",[61,83,81],{"__ignoreMap":59},[11,85,86],{},"DataManagerを作成するときは、全ページを取得してPageIndexを埋めます。",[53,88,91],{"className":89,"code":90,"language":58,"meta":59},[56],"\u002F\u002F 初始化 pageIndex\nvoid fillPageIndex() {\n    int pageNumber = pc.getPageNumber();\n    for(int i = 2; i \u003C= pageNumber; i ++) {\n        Page pg = null;\n        try {\n            pg = pc.getPage(i);\n        } catch (Exception e) {\n            Panic.panic(e);\n        }\n        pIndex.add(pg.getPageNumber(), PageX.getFreeSpace(pg));\n        pg.release();\n    }\n}\n",[61,92,90],{"__ignoreMap":59},[11,94,95],{},"Pageを使い終えたら、すぐにreleaseしてください。解放しないとキャッシュを使い切るおそれがあります。",[34,97,99],{"id":98},"dataitem","DataItem",[11,101,102],{},"DataItemは、DMが上位モジュールへ提供するデータの抽象化です。上位モジュールはアドレスを指定してDMにDataItemを要求し、その中のデータを取得します。",[11,104,105],{},"DataItemの実装はシンプルです。",[53,107,110],{"className":108,"code":109,"language":58,"meta":59},[56],"public class DataItemImpl implements DataItem {\n    private SubArray raw;\n    private byte[] oldRaw;\n    private DataManagerImpl dm;\n    private long uid;\n    private Page pg;\n}\n",[61,111,109],{"__ignoreMap":59},[11,113,114],{},"dmへの参照を保持するのは、DataItemの解放をdmの解放処理に任せるためです。dm自体がキャッシュのインターフェースを実装し、DataItemをキャッシュしています。また、データ変更時にログを記録するためにも使います。",[11,116,117],{},"DataItemに保存するデータの構造は次のとおりです。",[53,119,124],{"className":120,"code":122,"language":123},[121],"language-text","[ValidFlag] [DataSize] [Data]\n","text",[61,125,122],{"__ignoreMap":59},[11,127,128],{},"ValidFlagは1バイトで、そのDataItemが有効かどうかを示します。削除するときは、有効フラグを0にするだけです。DataSizeは2バイトで、後ろに続くDataの長さを表します。",[11,130,131,132,135],{},"上位モジュールはDataItemを取得したら、",[61,133,134],{"code":134},"data()","で内容を取り出せます。返す配列はコピーではなくデータを共有するため、SubArrayを使います。",[53,137,140],{"className":138,"code":139,"language":58,"meta":59},[56],"@Override\npublic SubArray data() {\n    return new SubArray(raw.raw, raw.start+OF_DATA, raw.end);\n}\n",[61,141,139],{"__ignoreMap":59},[11,143,144,145,148,149,152,153,156],{},"DataItemを変更するには、所定の手順を守る必要があります。変更前に",[61,146,147],{"code":147},"before()","を呼び、変更を取り消すなら",[61,150,151],{"code":151},"unBefore()","、変更が完了したら",[61,154,155],{"code":155},"after()","を呼びます。主な目的は、変更前イメージを保存し、適切なタイミングでログを記録することです。DMはDataItemの変更がアトミックであることを保証します。",[53,158,161],{"className":159,"code":160,"language":58,"meta":59},[56],"@Override\npublic void before() {\n    wLock.lock();\n    pg.setDirty(true);\n    System.arraycopy(raw.raw, raw.start, oldRaw, 0, oldRaw.length);\n}\n\n@Override\npublic void unBefore() {\n    System.arraycopy(oldRaw, 0, raw.raw, raw.start, oldRaw.length);\n    wLock.unlock();\n}\n\n@Override\npublic void after(long xid) {\n    dm.logDataItem(xid, this);\n    wLock.unlock();\n}\n",[61,162,160],{"__ignoreMap":59},[11,164,165,167],{},[61,166,155],{"code":155},"は主にdmのメソッドを呼んで変更操作のログを記録します。ここでは詳しい説明を省きます。",[11,169,170],{},"DataItemを使い終えた後も、すぐにrelease()を呼んでキャッシュへの参照を解放します。DataItemをキャッシュするのはDMです。",[53,172,175],{"className":173,"code":174,"language":58,"meta":59},[56],"@Override\npublic void release() {\n    dm.releaseDataItem(this);\n}\n",[61,176,174],{"__ignoreMap":59},[34,178,180],{"id":179},"dmの実装","DMの実装",[11,182,183],{},"DataManagerはDM層のメソッドを外部へ直接提供するクラスで、DataItemオブジェクトのキャッシュでもあります。DataItemのキーは、ページ番号とページ内オフセットからなる8バイトの符号なし整数です。それぞれが4バイトを占めます。",[11,185,186,187,190],{},"DataItemキャッシュの",[61,188,189],{"code":189},"getForCache()","では、キーからページ番号を取り出し、pageCacheからページを取得して、オフセットに従ってDataItemを解析すれば十分です。",[53,192,195],{"className":193,"code":194,"language":58,"meta":59},[56],"@Override\nprotected DataItem getForCache(long uid) throws Exception {\n    short offset = (short)(uid & ((1L \u003C\u003C 16) - 1));\n    uid >>>= 32;\n    int pgno = (int)(uid & ((1L \u003C\u003C 32) - 1));\n    Page pg = pc.getPage(pgno);\n    return DataItem.parseDataItem(pg, offset, this);\n}\n",[61,196,194],{"__ignoreMap":59},[11,198,199],{},"DataItemのキャッシュ解放では保存元への書き戻しが必要です。ただしファイルの読み書きはページ単位なので、DataItemが属するページをreleaseするだけで済みます。",[53,201,204],{"className":202,"code":203,"language":58,"meta":59},[56],"@Override\nprotected void releaseForCache(DataItem di) {\n    di.page().release();\n}\n",[61,205,203],{"__ignoreMap":59},[11,207,208],{},"既存ファイルからDataManagerを作る場合と、空のファイルから作る場合では、手順が少し異なります。PageCacheとLoggerの作り方に加え、空のファイルでは先頭ページの初期化が必要です。既存ファイルでは先頭ページを検証し、復旧が必要か判断します。また、先頭ページのランダムなバイト列を生成し直します。",[53,210,213],{"className":211,"code":212,"language":58,"meta":59},[56],"public static DataManager create(String path, long mem, TransactionManager tm) {\n    PageCache pc = PageCache.create(path, mem);\n    Logger lg = Logger.create(path);\n    DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);\n    dm.initPageOne();\n    return dm;\n}\n\npublic static DataManager open(String path, long mem, TransactionManager tm) {\n    PageCache pc = PageCache.open(path, mem);\n    Logger lg = Logger.open(path);\n    DataManagerImpl dm = new DataManagerImpl(pc, lg, tm);\n    if(!dm.loadCheckPageOne()) {\n        Recover.recover(tm, lg, pc);\n    }\n    dm.fillPageIndex();\n    PageOne.setVcOpen(dm.pageOne);\n    dm.pc.flushPage(dm.pageOne);\n    return dm;\n}\n",[61,214,212],{"__ignoreMap":59},[11,216,217],{},"先頭ページの初期化と検証は、基本的にPageOneのメソッドを呼んで実現します。",[53,219,222],{"className":220,"code":221,"language":58,"meta":59},[56],"\u002F\u002F 在创建文件时初始化 PageOne\nvoid initPageOne() {\n    int pgno = pc.newPage(PageOne.InitRaw());\n    assert pgno == 1;\n    try {\n        pageOne = pc.getPage(pgno);\n    } catch (Exception e) {\n        Panic.panic(e);\n    }\n    pc.flushPage(pageOne);\n}\n\n\u002F\u002F 在打开已有文件时时读入 PageOne，并验证正确性\nboolean loadCheckPageOne() {\n    try {\n        pageOne = pc.getPage(1);\n    } catch (Exception e) {\n        Panic.panic(e);\n    }\n    return PageOne.checkVc(pageOne);\n}\n",[61,223,221],{"__ignoreMap":59},[11,225,226,227,230,231,234],{},"DM層は上位に読み取り・挿入・変更という3つの機能を提供します。変更は読み出したDataItemを通じて行うため、DataManagerに必要なのは",[61,228,229],{"code":229},"read()","と",[61,232,233],{"code":233},"insert()","だけです。",[11,236,237,239],{},[61,238,229],{"code":229},"はUIDに基づいてキャッシュからDataItemを取得し、有効フラグを確認します。",[53,241,244],{"className":242,"code":243,"language":58,"meta":59},[56],"@Override\npublic DataItem read(long uid) throws Exception {\n    DataItemImpl di = (DataItemImpl)super.get(uid);\n    if(!di.isValid()) {\n        di.release();\n        return null;\n    }\n    return di;\n}\n",[61,245,243],{"__ignoreMap":59},[11,247,248,250],{},[61,249,233],{"code":233},"はpageIndexから、挿入する内容を保存できるページの番号を取得します。ページを取得したら、まず挿入ログを書きます。その後にpageXでデータを挿入し、挿入位置のオフセットを返します。最後に、ページ情報をpageIndexへ戻す必要があります。",[53,252,255],{"className":253,"code":254,"language":58,"meta":59},[56],"@Override\npublic long insert(long xid, byte[] data) throws Exception {\n    byte[] raw = DataItem.wrapDataItemRaw(data);\n    if(raw.length > PageX.MAX_FREE_SPACE) {\n        throw Error.DataTooLargeException;\n    }\n\n    \u002F\u002F 尝试获取可用页\n    PageInfo pi = null;\n    for(int i = 0; i \u003C 5; i ++) {\n        pi = pIndex.select(raw.length);\n        if (pi != null) {\n            break;\n        } else {\n            int newPgno = pc.newPage(PageX.initRaw());\n            pIndex.add(newPgno, PageX.MAX_FREE_SPACE);\n        }\n    }\n    if(pi == null) {\n        throw Error.DatabaseBusyException;\n    }\n\n    Page pg = null;\n    int freeSpace = 0;\n    try {\n        pg = pc.getPage(pi.pgno);\n        \u002F\u002F 首先做日志\n        byte[] log = Recover.insertLog(xid, pg, raw);\n        logger.log(log);\n        \u002F\u002F 再执行插入操作\n        short offset = PageX.insert(pg, raw);\n\n        pg.release();\n        return Types.addressToUid(pi.pgno, offset);\n\n    } finally {\n        \u002F\u002F 将取出的 pg 重新插入 pIndex\n        if(pg != null) {\n            pIndex.add(pi.pgno, PageX.getFreeSpace(pg));\n        } else {\n            pIndex.add(pi.pgno, freeSpace);\n        }\n    }\n}\n",[61,256,254],{"__ignoreMap":59},[11,258,259],{},"DataManagerを正常終了するときは、キャッシュとログを閉じます。先頭ページの検証用バイト列の設定も忘れずに。",[53,261,264],{"className":262,"code":263,"language":58,"meta":59},[56],"@Override\npublic void close() {\n    super.close();\n    logger.close();\n\n    PageOne.setVcClose(pageOne);\n    pageOne.release();\n    pc.close();\n}\n",[61,265,263],{"__ignoreMap":59},[11,267,268],{},"これでDM層は完了です。",[11,270,271],{},"今日は2021年12月11日、A-SOULの1周年記念配信の日です。A-SOUL、1周年おめでとう！ 2周年も、3周年も、10周年もありますように！ 北京の「鳥の巣」スタジアムで会いましょう！！！",[11,273,274],{},[275,276],"img",{"alt":59,"src":277},"https:\u002F\u002Fblog-img.774352199.xyz\u002F2025\u002Fbee2e73291a2ecde2667bb41f2e2c5b6.jpg",[11,279,280],{},"私たちは、ASOUL！",{"title":59,"searchDepth":282,"depth":282,"links":283},4,[284,286,287,288],{"id":36,"depth":285,"text":36},3,{"id":42,"depth":285,"text":42},{"id":98,"depth":285,"text":99},{"id":179,"depth":285,"text":180},[290],"projects","2021-12-11 15:16:00","ページインデックスはDM層の重要な構成要素です。各ページの空き容量をキャッシュすることで挿入処理を効率化し、上位モジュールが長い探索をせずに適切なページを素早く見つけられるようにします。実装ではデータ項目（DataItem）の抽象化とも密接に連携し、データベースの効率的な動作を支えます。",false,"md","https:\u002F\u002Fblog-img.774352199.xyz\u002F22PSG1.webp",{"slots":297},{},true,"\u002Fprojects\u002Fmydb\u002Fmydb5",null,{"text":302,"minutes":303,"time":304,"words":305},"10 min read",9.86,591600,1972,{"title":5,"description":292},"MYDBのDM層を仕上げます。空き容量によるページ選択、DataItemの共有データと更新ログ、キャッシュ、読み書き、起動時の復旧確認、終了処理をまとめて実装します。",{"loc":299,"images":309},[310],{"loc":277},"posts\u002Fprojects\u002Fmydb\u002Fmydb5",[313,314,42,315],"MYDB","Java","データ管理","tech","tiU9Po4XxWBSVVjBQh8TDz1-IU81LQePPP6-FNkA9GQ",[319,337,354,372,389,408,426,444,461,477,494,510,526,542,559,577,592,609,623,640,658,675,690,707,723,742,759,775,793,807,825,843,859,874,888,903,918,933,948,962,976,980,995,1009,1023],{"categories":320,"date":322,"description":323,"image":324,"path":325,"readingTime":326,"recommend":300,"tags":331,"title":336,"type":316},[321],"daily","2024-09-01 22:03:10","子どもの頃からの胃腸の不調が大学時代には不思議と消えたものの、就職後の夜更かしと飲酒で再発。慢性胃炎との付き合いは一筋縄ではいきませんでした。吐き気や胃のむかつきが日常になり、何度か検査を受けて生活を振り返るうちに、ようやく腰を据えて治療に向き合うようになりました。","https:\u002F\u002Fblog-img.774352199.xyz\u002F0Hr9l5.webp","\u002Fdaily\u002Fanti-chronic-gastritis",{"text":327,"minutes":328,"time":329,"words":330},"7 min read",6.485,389100,1297,[332,333,334,335],"慢性胃炎","受診記録","胃内視鏡検査","健康記録","慢性胃炎の治療記録",{"categories":338,"date":339,"description":340,"image":341,"path":342,"readingTime":343,"recommend":300,"tags":348,"title":353,"type":316},[321],"2022-04-11 00:13:13","『三体』では、劉慈欣が壮大な歴史を通して個人と集団の関係を描いています。西側の民主主義への疑念と、大多数の人々に運命を委ねることへの悲観が、三部作を貫いています。英雄たちが力を尽くしても、その努力は歴史の流れに押し流される。文化大革命から始まる物語に、少数の傑出した人物と平凡な大衆の対立を読み、人間性と社会について考えました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FLTzbFP.webp","\u002Fdaily\u002Fpeople-in-three-body",{"text":344,"minutes":345,"time":346,"words":347},"19 min read",18.495,1109700,3699,[349,350,351,352],"三体","劉慈欣","SF文学","読書ノート","『三体』における「大多数の人々」",{"categories":355,"date":356,"description":357,"image":358,"path":359,"readingTime":360,"recommend":300,"tags":365,"title":371,"type":316},[321],"2024-01-04 02:06:51","2024年の学習目標は、日本語をN2レベルに引き上げること、SICPとTAPLを最後までやり切ること、POSIXを完全に実装したカーネルを作ること、そしてブログのテーマを更新すること。どれも簡単ではありませんが、今年取り組む方向は決まりました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FV3rSQC.webp","\u002Fdaily\u002Fplan2024",{"text":361,"minutes":362,"time":363,"words":364},"1 min read",0.345,20700,69,[366,367,368,369,370],"年間計画","日本語学習","SICP","TAPL","オペレーティングシステム","2024年の学習計画",{"categories":373,"date":374,"description":375,"image":376,"path":377,"readingTime":378,"recommend":300,"tags":383,"title":388,"type":316},[321],"2024-09-28 16:26:00","ByteDanceで働いて3年。変化の速い環境で、時間まで加速したように感じながらも、どうにか心のバランスを保ってきました。杭州のオフィスのベランダから遠くの山を眺めていた入社当初を振り返り、仕事の壁や心境の変化、忙しさの中で自分のペースを探してきた日々を記します。","https:\u002F\u002Fblog-img.774352199.xyz\u002FhnCaht.webp","\u002Fdaily\u002Fwork-for-3-years",{"text":379,"minutes":380,"time":381,"words":382},"20 min read",19.73,1183800,3946,[384,385,386,387],"ByteDance","エンジニアのキャリア","仕事の振り返り","メンタルヘルス","ByteDanceで3年働きながら、ある程度の正気を保つことも不可能ではない",{"categories":390,"date":392,"description":393,"image":394,"path":395,"readingTime":396,"recommend":300,"tags":401,"title":407,"type":316},[391],"fiddling","2025-05-28 22:09:00","ブログをAstroに移行したところ、従来のGoogle Analytics導入方法では性能面が気になるようになりました。headタグにJavaScriptを直接追加してイベントを送ることはできますが、ページの性能に影響します。そこでpartytownでスクリプトをメインスレッドから切り離し、読み込みを妨げないようにしました。サンプルコードに手を加えることでGoogle Analyticsの導入に成功し、性能とアクセス解析を両立できました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FQ0w4RN.webp","\u002Ffiddling\u002Fastro-google-tag-manager",{"text":397,"minutes":398,"time":399,"words":400},"4 min read",3.455,207300,691,[402,403,404,405,406],"Astro","Google Analytics","Google Tag Manager","Partytown","アクセス解析","AstroにGoogle Analyticsを導入する（Tag Manager編）",{"categories":409,"date":410,"description":411,"image":412,"path":413,"readingTime":414,"recommend":419,"tags":420,"title":425,"type":316},[391],"2023-04-08 13:16:36","新しいプログラミング言語の設計は、難しくも面白い作業です。複雑なコンパイラ理論や機能の実装詳細をいったん脇に置き、コードがコンピューターのどこで動くかに注目すると、言語の作り方が見えてきます。RISC-VI命令セットを出発点に、基盤となる構造、階層化されたシステム、仮想マシンのモデルをたどり、言語の本質を考えます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FuO420B.webp","\u002Ffiddling\u002Fchitchat-about-programming-language",{"text":415,"minutes":416,"time":417,"words":418},"28 min read",27.03,1621800,5406,2,[421,422,423,424],"プログラミング言語設計","型システム","コンパイラ設計","RISC-V","プログラミング言語についての雑談",{"categories":427,"date":428,"description":429,"image":430,"path":431,"readingTime":432,"recommend":282,"tags":437,"title":443,"type":316},[391],"2025-04-18 16:43:12","彼女が仕事で北京から上海に引っ越したので、回線の手配も手伝いました。ところが上海電信の500Mは杭州の1000Mより高く、なんとも納得がいきません。そこで上海での透過プロキシ、特定の通信の杭州経由での外部接続、両拠点のLAN相互接続を目指してネットワークを組むことにしました。杭州側はソフトウェアルーターと無線APのシンプルな構成で、普段から自宅経由で通信できる環境があり、次の挑戦に向けた準備はできています。","https:\u002F\u002Fblog-img.774352199.xyz\u002FO6cAGh.webp","\u002Ffiddling\u002Fcross-city-network-setup",{"text":433,"minutes":434,"time":435,"words":436},"11 min read",10.14,608400,2028,[438,439,440,441,442],"Tailscale","mihomo","拠点間ネットワーク","透過プロキシ","ホームネットワーク","上海と杭州の自宅ネットワークをつなぐ",{"categories":445,"date":446,"description":447,"image":448,"path":449,"readingTime":450,"recommend":300,"tags":454,"title":460,"type":316},[391],"2021-12-27 00:09:00","CSAPPの学習では実験が欠かせませんが、Linux環境の構築が壁になりがちです。とくに仮想マシンでは、インストール失敗、互換性、ネットワーク接続などの問題に悩まされます。Windows 10バージョン2004以降なら、WSL（Windows Subsystem for Linux）が有力な選択肢です。従来の仮想マシンの複雑さや性能面の負担を避けながら、Linux環境を手軽に利用できます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FvqOC7N.webp","\u002Ffiddling\u002Fcsapplab0",{"text":302,"minutes":451,"time":452,"words":453},9.585,575100,1917,[455,456,457,458,459],"CSAPP","WSL2","Linux","GDB","実験環境","CSAPP Labの環境構築",{"categories":462,"date":463,"description":464,"image":465,"path":466,"readingTime":467,"recommend":300,"tags":472,"title":476,"type":316},[391],"2024-07-13 17:49:00","Debianをサイドルーターに使うと、OpenWrtやLuCIに依存せず、より安定した柔軟な構成を作れます。Debianを直接設定することでシステムを細かく制御でき、GUIの制約や不安定さを避けられます。一般的なサイドルーター構成と比べて透過プロキシを安定して運用しやすく、性能や効率を重視する場合の選択肢になります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FpPRU5x.webp","\u002Ffiddling\u002Fdebian-as-bypass-router",{"text":468,"minutes":469,"time":470,"words":471},"18 min read",17.525,1051500,3505,[473,474,439,475,441],"Debian","サイドルーター","AdGuard Home","Debianをサイドルーターにする",{"categories":478,"date":479,"description":480,"image":481,"path":482,"readingTime":483,"recommend":300,"tags":488,"title":493,"type":316},[391],"2024-08-16 23:53:00","FakeIPを使った透過プロキシ構成で、従来のサイドルーターの単一障害点、性能不足、複雑なポート転送を改善します。プロキシコアをsing-boxに変えることで、転送性能を高めながら設定も簡単にできます。対応プロトコルが豊富で、従来のClashより最適化も進んでいます。同じ構成はClashでも実現できますが、sing-boxは柔軟な選択肢になります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FS2HHD5.webp","\u002Ffiddling\u002Ffake-ip-based-transparent-proxy",{"text":484,"minutes":485,"time":486,"words":487},"14 min read",13.1,786000,2620,[489,490,441,491,492],"FakeIP","sing-box","ポリシーベースルーティング","トラフィック振り分け","FakeIPを使った透過プロキシの振り分け",{"categories":495,"date":496,"description":497,"image":498,"path":499,"readingTime":500,"recommend":300,"tags":505,"title":509,"type":316},[391],"2024-08-15 23:50:00","サイドルーターを導入すると、メインルーターのポート転送が効かなくなることがあります。ゲートウェイをサイドルーターに変えることで通信経路が変わり、メインルーターを前提とした転送が正常に動かなくなるためです。ゲートウェイはアドレスを変換して内部ネットワークの通信を外部へ転送し、各端末は外部との通信にこれを必要とします。この仕組みを理解すると、ポート転送の問題を解決しやすくなります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FmRqws9.webp","\u002Ffiddling\u002Ffix-port-forward-in-bypass-router",{"text":501,"minutes":502,"time":503,"words":504},"6 min read",5.155,309300,1031,[474,506,507,508],"ポート転送","NAT","ネットワークのトラブルシューティング","サイドルーター環境でポート転送が効かない問題を解決する",{"categories":511,"date":512,"description":513,"image":514,"path":515,"readingTime":516,"recommend":300,"tags":521,"title":525,"type":316},[391],"2023-02-02 23:24:55","春節の帰省列車で、Goをベアメタルで動かす記事を読み、低レベルのシステムインターフェースに興味が湧きました。実際に動く実装を見て、高級言語でOSを作る可能性に胸が躍ります。関連研究を調べると先行例も見つかり、積もった熱意は期待に満ちたプロジェクトへと変わりました。しかし、最後は思いどおりにはいきませんでした。","https:\u002F\u002Fblog-img.774352199.xyz\u002FxB1Ni5.webp","\u002Ffiddling\u002Fgo-os",{"text":517,"minutes":518,"time":519,"words":520},"12 min read",11.81,708600,2362,[522,424,370,523,524],"Go","ベアメタルプログラミング","ランタイム","失敗に終わったプロジェクト：楽しくなかった春節の7日間",{"categories":527,"date":528,"description":529,"image":530,"path":531,"readingTime":532,"recommend":300,"tags":537,"title":541,"type":316},[391],"2022-08-15 01:05:01","システムのリファクタリングで、階層ごとのエンティティ変換に苦労しました。ビュー層の商品VO、ドメイン層のentity、永続化層のPOは似ていても、型のわずかな違いで直接変換できず、ディープコピーが面倒になります。そこでリフレクションを使った汎用的な変換処理を実装し、大量のassemblerメソッドを減らして、保守性と柔軟性を高めることにしました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FBlhm0I.webp","\u002Ffiddling\u002Fgolang-deepcopy-between-different-type",{"text":533,"minutes":534,"time":535,"words":536},"5 min read",4.14,248400,828,[522,538,539,540],"リフレクション","ディープコピー","構造体変換","Goで異なる型の構造体をディープコピーする",{"categories":543,"date":544,"description":545,"image":546,"path":547,"readingTime":548,"recommend":300,"tags":552,"title":558,"type":316},[391],"2025-03-31 23:51:00","Appleヘルスケアの心拍数を定期的にサーバーへ同期し、Grafanaで可視化して直感的に確認できるようにしました。Health Auto ExportのREST API機能でHTTPエンドポイントへデータを送り、InfluxDBに保存します。Grafanaのダッシュボードから、自分の心拍数の変化を追ったり分析したりできます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FF4qD2T.webp","\u002Ffiddling\u002Fheart-rate-to-grafana",{"text":397,"minutes":549,"time":550,"words":551},3.76,225600,752,[553,554,555,556,557],"Apple Watch","Health Auto Export","InfluxDB","Grafana","心拍数モニタリング","My Heart Beats for U：心拍数をGrafanaで可視化する",{"categories":560,"date":561,"description":562,"image":563,"path":564,"readingTime":565,"recommend":569,"tags":570,"title":576,"type":316},[391],"2025-06-10 20:18:00","macOSとiPadOS 26では、新しいデザイン言語Liquid Glassが導入され、アイコンやウインドウの印象が一新されました。とくにiPadのアプリをウインドウで扱えるようになったことは、生産性向上のための端末へ進む大きな一歩です。一方、透明なコントロールセンターやLaunchpadの統合には賛否があり、使い勝手には改善の余地もあります。課題はありつつも、今後につながる更新だと感じました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FTPSaLE.webp","\u002Ffiddling\u002Fmacos-26-trial",{"text":327,"minutes":566,"time":567,"words":568},6.94,416400,1388,5,[571,572,573,574,575],"macOS 26","iPadOS 26","Liquid Glass","Apple Intelligence","OS使用感","macOS 26とiPadOS 26を試してみた",{"categories":578,"date":579,"description":580,"image":581,"path":582,"readingTime":583,"recommend":300,"tags":587,"title":591,"type":316},[391],"2026-06-11 10:00:00","今年もWWDCでmacOS 27の新しいApple Intelligenceが登場しました。macOS 26時代の回避方法はもう使えず、引き続きAppleとの知恵比べです。","https:\u002F\u002Fblog-img.774352199.xyz\u002FfOFucm.webp","\u002Ffiddling\u002Fmacos-27-apple-intelligence-chatgpt",{"text":302,"minutes":584,"time":585,"words":586},9.74,584400,1948,[588,574,589,590],"macOS 27","ChatGPT","中国本土向けMac","中国本土向けMacでApple Intelligenceを使う：地域変更からChatGPTの取り出しまで",{"categories":593,"date":594,"description":595,"image":596,"path":597,"readingTime":598,"recommend":300,"tags":602,"title":608,"type":316},[391],"2025-07-20 23:32:00","MoonTVはNext.jsとReactで作った動画集約プラットフォームで、連続ドラマなどを手軽に楽しむためのものです。LibreTVを改善しようとしたことが始まりで、数か月の開発を経て多くの関心と利用を集めています。Cursorのおかげで効率よく開発できた一方、複数環境への対応や複雑なデータ依存関係には苦労しました。ユーザーが増える中、フィードバックを受けて改善を続けています。","https:\u002F\u002Fblog-img.774352199.xyz\u002FnIeONi.webp","\u002Ffiddling\u002Fmoontv-vibe-coding",{"text":433,"minutes":599,"time":600,"words":601},10.98,658800,2196,[603,604,605,606,607],"MoonTV","LunaTV","Vibe Coding","Cursor","AI支援開発","MoonTV：Vibe Codingを試してみた",{"categories":610,"date":611,"description":612,"image":613,"path":614,"readingTime":615,"recommend":300,"tags":619,"title":622,"type":316},[391],"2024-10-07 16:51:00","BGPをもとに中国国内・国外のIPを振り分けることで、透過プロキシの効率と精度を高めます。国外の宛先をFakeIPで識別すれば、メインルーターでより適切に通信を振り分けられます。sing-boxのDNS設定も見直し、DNSクエリを柔軟かつ効率的に処理して、ネットワーク全体の使い勝手を改善します。","https:\u002F\u002Fblog-img.774352199.xyz\u002FMOmM1s.webp","\u002Ffiddling\u002Fmore-accurate-chnroute",{"text":501,"minutes":616,"time":617,"words":618},5.75,345000,1150,[620,621,491,492],"BGP","IPアドレスデータベース","BGPで中国国内・国外IPの振り分けをより正確にする",{"categories":624,"date":625,"description":626,"image":627,"path":628,"readingTime":629,"recommend":300,"tags":634,"title":639,"type":316},[391],"2025-12-14 14:31:00","友人の一言からシミュレーターに興味が湧き、Microsoft Flight Simulatorを購入。地図やモデルはリアルタイム配信、アカウントは再ログインが必要、チュートリアルは見つけにくく、キーボード操作も難しい。結局フライトスティックを買い、Pico VRまで設定してようやく一通り体験できました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FlfhEuE.webp","\u002Ffiddling\u002Fmsfs2024-joystick-and-pico",{"text":630,"minutes":631,"time":632,"words":633},"9 min read",8.96,537600,1792,[635,636,637,638],"Microsoft Flight Simulator 2024","Thrustmaster TCA","PICO VR","フライトシミュレーション","Microsoft Flight Simulator 2024をあれこれ試す",{"categories":641,"date":642,"description":643,"image":644,"path":645,"readingTime":646,"recommend":300,"tags":651,"title":657,"type":316},[391],"2025-06-05 23:26:00","OnePlusからiPhoneへと何度も機種変更するうち、いじる楽しさからエコシステムへの依存へと関心が変わりました。最近、彼女の一言をきっかけに写真を改善しようとOPPO Find X8 Ultraを購入。Apple環境からの移行では、Androidアプリの質のばらつきと代替探しの難しさを改めて感じました。1か月をかけて、異なるプラットフォームの摩擦に折り合いをつけた記録です。","https:\u002F\u002Fblog-img.774352199.xyz\u002F19NIhZ.webp","\u002Ffiddling\u002Fone-month-using-android",{"text":647,"minutes":648,"time":649,"words":650},"15 min read",14.05,843000,2810,[652,653,654,655,656],"Android","OPPO","iOS","スマートフォン使用感","エコシステム移行","Androidに乗り換えて1か月",{"categories":659,"date":660,"description":661,"image":662,"path":663,"readingTime":664,"recommend":300,"tags":668,"title":674,"type":316},[391],"2026-05-11 22:43:00","GitHub Trendingで旅行計画ツールを見つけ、自分でも動かしたくなりました。どうせ新しいVPSを借りるなら、ついでにもう少し、いや、だいぶ色々入れてしまおうと。","https:\u002F\u002Fblog-img.774352199.xyz\u002FBBdDWW.webp","\u002Ffiddling\u002Fone-trek-twenty-stacks",{"text":433,"minutes":665,"time":666,"words":667},10.34,620400,2068,[669,670,671,672,673],"TREK","Docker Compose","Dockge","VPS","セルフホスティング","TREKひとつのつもりが、20個のスタックに",{"categories":676,"date":677,"description":678,"image":679,"path":680,"readingTime":681,"recommend":300,"tags":685,"title":689,"type":316},[391],"2025-01-16 23:09:00","OPNsenseは、美しい画面と充実した機能を備えるオープンソースのファイアウォール・ルーターです。いくつかの構成を試した末に、透過プロキシやトラフィック振り分けでの可能性に気づきました。BGPベースの振り分けと組み合わせることで安全性と安定性を高められ、IPリストの自動更新で管理もしやすくなります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FxA8C1E.webp","\u002Ffiddling\u002Fopnsense-transparent-proxy",{"text":517,"minutes":682,"time":683,"words":684},11.38,682800,2276,[686,687,439,688,441,492],"OPNsense","FreeBSD","tun2socks","OPNsenseで透過プロキシとトラフィック振り分けを実現する",{"categories":691,"date":692,"description":693,"image":694,"path":695,"readingTime":696,"recommend":285,"tags":700,"title":706,"type":316},[391],"2025-03-15 20:35:00","構文解析では、ユーザー定義型名と通常の変数名を見分けることが課題になります。たとえば`a*b;`は算術式とも宣言とも解釈できます。とくに型指定子に関する文法規則によって変数名が型名と解釈され、正確さや読みやすさに影響します。初期化を伴わない変数宣言は多いため、この曖昧さはソースコードの随所に現れます。","https:\u002F\u002Fblog-img.774352199.xyz\u002F2VKHK9.webp","\u002Ffiddling\u002Fparser-type-variable-ambiguity",{"text":484,"minutes":697,"time":698,"words":699},13.58,814800,2716,[701,702,703,704,705],"構文解析","GLR","シンボルテーブル","スコープ","曖昧性解消","構文解析で型名と変数名の曖昧さを解消する",{"categories":708,"date":709,"description":710,"image":711,"path":712,"readingTime":713,"recommend":300,"tags":717,"title":722,"type":316},[391],"2023-05-24 17:51:09","RISC-Vツールチェーンをインストールするには、まずriscv-gnu-toolchainのソースを取得します。clone時に`--depth=1`を付けるとダウンロード量を減らせます。READMEのPrerequisitesを確認し、必要な依存関係をそろえてから進めます。Debianではコマンドで依存パッケージをまとめて導入できます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FrWNOKx.webp","\u002Ffiddling\u002Fspike-install",{"text":397,"minutes":714,"time":715,"words":716},3.89,233400,778,[424,718,719,720,721],"Spike","riscv-pk","クロスコンパイル","ツールチェーン","RISC-Vツールチェーンとエミュレーターのインストール",{"categories":724,"date":725,"description":726,"image":727,"path":728,"readingTime":729,"recommend":733,"tags":734,"title":741,"type":316},[391],"2026-09-09 23:59:00","これも一種のNTRですよね","https:\u002F\u002Fblog-img.774352199.xyz\u002FNDPUwc.webp","\u002Ffiddling\u002Fsteamdeck-switch-60fps",{"text":484,"minutes":730,"time":731,"words":732},13.905,834300,2781,7,[735,736,737,738,739,740],"Steam Deck","Nintendo Switch","EmuDeck","Eden","Lossless Scaling","フレーム生成","Steam Deckの第二の人生：Switchエミュレーター導入からフレーム補間で60FPSまで",{"categories":743,"date":744,"description":745,"image":746,"path":747,"readingTime":748,"recommend":300,"tags":753,"title":758,"type":316},[391],"2024-06-23 15:31:32","GFWは単に出口のゲートウェイを監視するのではなく、通信経路の脇から国際通信を傍受します。出入国するIPパケットをクラスタへ複製し、詳しく分析・フィルタリングする仕組みです。どこで何をしているかを理解することは、検閲回避の検討に欠かせません。ネットワーク構成を調べることで、遮断の経路や技術、対処方法が見えてきます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FsOpJuL.webp","\u002Ffiddling\u002Ftech-about-gfw",{"text":749,"minutes":750,"time":751,"words":752},"25 min read",24.975,1498500,4995,[754,755,756,757],"GFW","DNSポイズニング","TCP","インターネット検閲","GFWの仕組みを探る",{"categories":760,"date":761,"description":762,"image":763,"path":764,"readingTime":765,"recommend":300,"tags":769,"title":774,"type":316},[391],"2022-04-16 00:01:28","Javaでは`this`を使うことで、コンパイラの定数最適化が効かなくなる場合があります。このコード例の`ab1`と`ab2`は同じstatic final変数`s`を参照しているように見えますが、比較結果は異なります。静的変数を直接参照して文字列を連結する`ab1`に対し、`this`経由の`ab2`では同じ定数伝播の最適化が行われません。わずかな構文の違いが、コンパイル結果を変える例です。","https:\u002F\u002Fblog-img.774352199.xyz\u002FgKtkYe.webp","\u002Ffiddling\u002Fthis-in-javac-string-concat",{"text":397,"minutes":766,"time":767,"words":768},3.88,232800,776,[314,770,771,772,773],"javac","文字列連結","定数伝播","バイトコード","Javaのthisキーワードでコンパイル時の定数伝播が効かなくなる問題",{"categories":776,"date":777,"description":778,"image":779,"path":780,"readingTime":781,"recommend":300,"tags":786,"title":792,"type":316},[391],"2025-01-29 21:58:00","ひとこと投稿機能があれば、静的ブログの記事公開に伴う手間や心理的な負担を減らし、短い思いつきをいつでも共有できます。Cloudflare Workersでバックエンドを実装し、KVで投稿を保存・管理します。フロントエンドはVitePressにVueコンポーネントを埋め込み、投稿を手軽に表示してブログに動きと交流の場を加えます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FhZX6hr.webp","\u002Ffiddling\u002Fvitepress-memos-component",{"text":782,"minutes":783,"time":784,"words":785},"24 min read",23.345,1400700,4669,[787,788,789,790,791],"VitePress","Vue","Cloudflare Workers","Cloudflare KV","マイクロブログ","VitePressにひとこと投稿機能を実装する",{"categories":794,"date":795,"description":796,"image":797,"path":798,"readingTime":799,"recommend":300,"tags":803,"title":806,"type":316},[391],"2025-03-15 16:24:00","ある平凡な午後、Telegramの通知で魅力的なVPSプランを見つけました。中国電信CN2直結、帯域2.5Gという好条件です。IPv6付きで動画配信サービスの地域制限解除にも便利ですが、すべての通信をWARP経由にする必要はありません。以前のスクリプトは手軽な一方、速度と振り分けに不満があり、もっと柔軟な方法を探すことにしました。","https:\u002F\u002Fblog-img.774352199.xyz\u002FMcjrrF.webp","\u002Ffiddling\u002Fvps-warp-ipv6",{"text":533,"minutes":800,"time":801,"words":802},4.09,245400,818,[804,805,672,491,492],"Cloudflare WARP","IPv6","VPSの特定の通信をWARP経由のIPv6で外に出す",{"categories":808,"date":809,"description":810,"image":811,"path":812,"readingTime":813,"recommend":817,"tags":818,"title":824,"type":316},[391],"2026-08-17 23:59:21","いじればいじるほど、もっといじりたくなります。年を取ってもまだまだ元気、まだまだ元気です。","https:\u002F\u002Fblog-img.774352199.xyz\u002FqfxB0h.webp","\u002Ffiddling\u002Fxiaomi17-root-and-hide-root",{"text":630,"minutes":814,"time":815,"words":816},8.83,529800,1766,6,[819,820,821,822,823],"Xiaomi 17","Bootloader","Android Root","ファームウェア書き換え","Rootの隠蔽","Xiaomi 17を普段使いに：ロック解除、ROM書き込み、root化とroot隠し",{"categories":826,"date":828,"description":829,"image":830,"path":831,"readingTime":832,"recommend":300,"tags":837,"title":842,"type":316},[827],"notes","2022-01-20 22:29:00","Lab 1では、masterとworkerという2つの主要な部分からなるMapReduceシステムを実装します。GoのRPCと並行プログラミングを十分に使いこなし、MapReduceの処理の流れも深く理解する必要があります。実装は2つの版を作り、mutexでロックする方式から、よりすっきりしたchannelによる明示的なロックを使わない方式へと改めました。後者のほうが設計もシンプルで分かりやすくなっています。実習を理解する鍵は、関連資料、特にフローチャートとその説明をしっかり読むことです。","https:\u002F\u002Fblog-img.774352199.xyz\u002FibVwPJ.webp","\u002Fnotes\u002F65840\u002Fmapreducelab",{"text":833,"minutes":834,"time":835,"words":836},"13 min read",12.39,743400,2478,[838,839,522,840,841],"MIT 6.5840","MapReduce","RPC","並行プログラミング","6.5840 Lab 1：MapReduce",{"categories":844,"date":845,"description":846,"image":847,"path":848,"readingTime":849,"recommend":300,"tags":853,"title":858,"type":316},[827],"2022-01-16 17:32:00","MapReduceは、大規模なデータセットの処理を簡単にするための効率的な並列計算モデルです。利用者はMapとReduceという2つの主要な関数を定義することで、複雑なタスクを簡単な操作に分解できます。データの分配やタスクの割り当てはフレームワークが自動で管理するため、開発者は低レベルの細部に気を取られず、アルゴリズムそのものに集中できます。分散システムで広く使われていることからも、その柔軟性と実用性が分かります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FApIDdC.webp","\u002Fnotes\u002F65840\u002Fmapreducepaper",{"text":630,"minutes":850,"time":851,"words":852},8.085,485100,1617,[839,854,855,856,857],"分散システム","論文ノート","並列計算","耐障害性","MapReduce論文を読む",{"categories":860,"date":861,"description":862,"image":863,"path":864,"readingTime":865,"recommend":300,"tags":869,"title":873,"type":316},[827],"2022-12-16 02:06:10","Lab 2Aでは、Raftのリーダー選出とハートビートを実装し、さまざまな厳しい条件でも任期の交代や選挙が正常に行われるようにします。実習全体は4段階からなり、この後に実装する分散KVストアの土台になります。明示的なロックを使わない設計によって、Raft構造体を簡潔にします。実習の手引きには必要な背景知識が載っていますが、前の実習と比べると参考資料に頼れる部分はほとんどなく、自力で実装することが重視されます。","https:\u002F\u002Fblog-img.774352199.xyz\u002Fc11Uk4.webp","\u002Fnotes\u002F65840\u002Fraftlab2a",{"text":468,"minutes":866,"time":867,"words":868},17.515,1050900,3503,[838,870,522,871,872],"Raft","リーダー選出","分散合意","6.5840 Lab 2A：リーダー選出",{"categories":875,"date":876,"description":877,"image":878,"path":879,"readingTime":880,"recommend":300,"tags":885,"title":887,"type":316},[827],"2022-12-03 21:40:09","Raftは、ログレプリケーションの効率を高めるために設計された合意アルゴリズムです。複数のマシンからなるクラスタに適しており、一部のマシンが故障してもサービスを継続できます。複製状態機械モデルを使い、ログに命令の順序を記録することで、クラスタ内の各マシンが同じ状態に到達できるようにします。論文『In Search of an Understandable Consensus Algorithm』は、Raftの設計思想とPaxosとの比較を詳しく論じ、その理解しやすさを示すとともに、信頼できる大規模なソフトウェアシステムを構築するための土台を提供しています。この記事は、その中心的な概念と応用を理解するための読書メモです。","https:\u002F\u002Fblog-img.774352199.xyz\u002F7mmvIZ.webp","\u002Fnotes\u002F65840\u002Freftextendedpaper",{"text":881,"minutes":882,"time":883,"words":884},"30 min read",29.245,1754700,5849,[870,872,886,855],"ログ複製","Raft論文を読む",{"categories":889,"date":890,"description":891,"image":892,"path":893,"readingTime":894,"recommend":898,"tags":899,"title":902,"type":316},[290],"2021-11-27 14:43:00","MYDBは、データベースの基本原理を学び、実装してみるための個人プロジェクトです。空き時間を使い、半月あまりかけて作りました。大学のデータベースシステムの授業で基礎には触れたものの、インターン中は授業を口実にサボってばかりでした。面接で正直に答えても大きな問題にはなりませんでしたが、それをきっかけに自分の知識を見直し、実際に手を動かして学ぼうと考えるようになりました。そうして生まれたのがこのプロジェクトです。","https:\u002F\u002Fblog-img.774352199.xyz\u002Fxfci2J.webp","\u002Fprojects\u002Fmydb\u002Fmydb0",{"text":630,"minutes":895,"time":896,"words":897},8.03,481800,1606,1,[313,314,900,901],"データベース実装","データベースアーキテクチャ","MYDB 0. プロジェクト構成と、先に伝えておきたいこと",{"categories":904,"date":905,"description":906,"image":907,"path":908,"readingTime":909,"recommend":300,"tags":914,"title":917,"type":316},[290],"2021-11-28 16:10:00","MYDBではXIDファイルでトランザクションを管理します。各トランザクションには1から増える一意のXIDがあり、XID 0は常にコミット済みのスーパートランザクションです。TransactionManagerがこのファイルを管理し、実行中・コミット済み・中止済みという3つの状態を記録します。この仕組みにより状態を正確に照会・管理でき、システムの安定性と信頼性を支えます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FH4zZAK.webp","\u002Fprojects\u002Fmydb\u002Fmydb1",{"text":910,"minutes":911,"time":912,"words":913},"8 min read",7.42,445200,1484,[313,314,915,916],"トランザクション管理","XID","MYDB 1. まずはシンプルなTMから",{"categories":919,"date":920,"description":921,"image":922,"path":923,"readingTime":924,"recommend":300,"tags":928,"title":932,"type":316},[290],"2021-12-25 18:26:00","MYDBはMySQLに似たC\u002FS構成を採用し、複数のクライアントがソケットでサーバーへ接続してSQLを実行し、結果を受け取れます。通信には専用のバイナリー形式を使いますが、実装を簡単にするならプレーンテキストも選択肢です。サーバーとクライアントの基本的な転送構造によって、データのやり取りと処理を行います。","https:\u002F\u002Fblog-img.774352199.xyz\u002FPAHrUZ.webp","\u002Fprojects\u002Fmydb\u002Fmydb10",{"text":501,"minutes":925,"time":926,"words":927},5.76,345600,1152,[313,314,929,930,931],"Socket","クライアントサーバー構成","通信プロトコル","MYDB 10. サーバー・クライアントの実装と通信プロトコル",{"categories":934,"date":935,"description":936,"image":937,"path":938,"readingTime":939,"recommend":300,"tags":943,"title":947,"type":316},[290],"2021-11-30 23:18:00","データマネージャー（DM）は上位モジュールとファイルシステムをつなぎ、ページとキャッシュを管理しながら、データの安全性と復旧を支えます。キャッシュには従来のLRUではなく参照カウント方式を採用し、汎用性と効率を高めて、この先のデータ操作の土台を作ります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FWdIGoG.webp","\u002Fprojects\u002Fmydb\u002Fmydb2",{"text":433,"minutes":940,"time":941,"words":942},10.395,623700,2079,[313,314,944,945,946],"参照カウント","キャッシュ設計","共有メモリ","MYDB 2. 参照カウント方式のキャッシュ基盤と共有バイト配列",{"categories":949,"date":950,"description":951,"image":952,"path":953,"readingTime":954,"recommend":300,"tags":958,"title":961,"type":316},[290],"2021-12-05 15:28:00","DMはファイルシステムをページとして抽象化し、ページ単位で読み書きとキャッシュを行います。データページのデフォルトサイズは8Kで、大量の書き込みではサイズの調整によって性能を高められます。実装済みの汎用キャッシュ基盤を使い、ここではページ構造を具体的に定義して、効率よくページを管理します。","https:\u002F\u002Fblog-img.774352199.xyz\u002FjlFC4E.webp","\u002Fprojects\u002Fmydb\u002Fmydb3",{"text":910,"minutes":955,"time":956,"words":957},7.14,428400,1428,[313,314,959,960],"データページ","キャッシュ管理","MYDB 3. データページのキャッシュと管理",{"categories":963,"date":964,"description":965,"image":966,"path":967,"readingTime":968,"recommend":300,"tags":972,"title":975,"type":316},[290],"2021-12-08 22:55:00","MYDBでは、クラッシュ後にデータを復旧できるよう、ログファイルが重要な役割を担います。DMは下位のデータを操作するたびにログを生成・記録し、連続したログ列を作ります。チェックサムと各操作の記録を含む所定のバイナリー形式で保存することで、再起動時にデータの状態を正確に再構築し、整合性と完全性を保ちます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FTRcbsj.webp","\u002Fprojects\u002Fmydb\u002Fmydb4",{"text":833,"minutes":969,"time":970,"words":971},12.685,761100,2537,[313,314,973,974],"データベースログ","クラッシュリカバリ","MYDB 4. ログファイルと復旧戦略",{"categories":977,"date":291,"description":292,"image":295,"path":299,"readingTime":978,"recommend":300,"tags":979,"title":5,"type":316},[290],{"text":302,"minutes":303,"time":304,"words":305},[313,314,42,315],{"categories":981,"date":982,"description":983,"image":984,"path":985,"readingTime":986,"recommend":300,"tags":990,"title":994,"type":316},[290],"2021-12-18 14:58:00","VMは二相ロックでスケジュールの直列化可能性を保証し、多版型同時実行制御（MVCC）を導入して読み取りと書き込みの相互ブロックを解消します。また、データベース操作の競合を定義し、特に更新と読み取りの関係を整理することで、トランザクションの分離レベルを理解する土台を作ります。","https:\u002F\u002Fblog-img.774352199.xyz\u002F8YzotA.webp","\u002Fprojects\u002Fmydb\u002Fmydb6",{"text":647,"minutes":987,"time":988,"words":989},14.665,879900,2933,[313,314,991,992,993],"MVCC","トランザクション分離","二相ロック","MYDB 6. レコードのバージョンとトランザクション分離",{"categories":996,"date":997,"description":998,"image":999,"path":1000,"readingTime":1001,"recommend":300,"tags":1005,"title":1008,"type":316},[290],"2021-12-23 21:20:00","VMでは、MVCCに伴うバージョンの飛び越しとデッドロックに対処する必要があります。MYDBはトランザクションに印を付けるだけで取り消しやロールバックを行い、abortedなトランザクションのデータがほかへ影響しないようにします。この設計により並行処理の効率と信頼性を高め、従来の2PLで起こりがちなデッドロックを避けて、システム全体の安定性と性能を向上させます。","https:\u002F\u002Fblog-img.774352199.xyz\u002FBF3yDW.webp","\u002Fprojects\u002Fmydb\u002Fmydb7",{"text":517,"minutes":1002,"time":1003,"words":1004},11.32,679200,2264,[313,314,1006,1007],"デッドロック検出","バージョン管理","MYDB 7. デッドロック検出とVMの実装",{"categories":1010,"date":1011,"description":1012,"image":1013,"path":1014,"readingTime":1015,"recommend":300,"tags":1019,"title":1022,"type":316},[290],"2021-12-24 21:01:00","MYDBはB+木によるクラスタ化インデックスを実装します。IMはバージョンマネージャー（VM）を経由せず、データマネージャー（DM）と直接やり取りし、インデックスデータをデータベースファイルへ直接書き込みます。この章では二分木インデックスの構造を詳しく説明し、葉フラグ、キー数、兄弟ノードの識別子など、インデックス検索の基盤となる要素を扱います。","https:\u002F\u002Fblog-img.774352199.xyz\u002Ff92X4o.webp","\u002Fprojects\u002Fmydb\u002Fmydb8",{"text":327,"minutes":1016,"time":1017,"words":1018},6.53,391800,1306,[313,314,1020,1021],"B+木","データベースインデックス","MYDB 8. インデックス管理",{"categories":1024,"date":1025,"description":1026,"image":1027,"path":1028,"readingTime":1029,"recommend":300,"tags":1033,"title":1036,"type":316},[290],"2021-12-25 15:44:00","テーブルマネージャー（TBM）は、フィールドとテーブルの構造を管理します。ParserはSQL風の文を構造化して解析し、文に含まれる情報を対応するクラスへまとめることで、その後の操作を簡単にします。この章ではMYDBのSQL構文も紹介し、管理処理全体を理解する土台を作ります。","https:\u002F\u002Fblog-img.774352199.xyz\u002FzOMyv5.webp","\u002Fprojects\u002Fmydb\u002Fmydb9",{"text":910,"minutes":1030,"time":1031,"words":1032},7.99,479400,1598,[313,314,1034,1035],"SQL解析","テーブル管理","MYDB 9. フィールドとテーブルの管理",1789914059966]