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