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