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