/** 添加、替换 或 删除 mvMap 上的键值对 * Add, replace or remove a key-value pair. * * @param key the key (may not be null) * @param value new value, it may be null when removal is intended 新值,当打算删除时它可能为空 * @param decisionMaker command object to make choices during transaction. * @return previous value, if mapping for that key existed, or null otherwise */ public V operate(K key, V value, DecisionMaker<? super V> decisionMaker) { IntValueHolderunsavedMemoryHolder=newIntValueHolder(); intattempt=0; while(true) { RootReference<K,V> rootReference = flushAndGetRoot(); // 1.获取 mvMap 根节点引用 booleanlocked= rootReference.isLockedByCurrentThread(); // 2.1.判断当前线程是否已经锁定了根节点 if (!locked) { // 2.2.如果当前线程没有锁定根节点,则尝试加锁 if (attempt++ == 0) { beforeWrite(); } if (attempt > 3 || rootReference.isLocked()) { rootReference = lockRoot(rootReference, attempt); locked = true; } } Page<K,V> rootPage = rootReference.root; // 3.获取根节点 root page longversion= rootReference.version; // 写版本号 CursorPos<K,V> tip; V result; unsavedMemoryHolder.value = 0; try { CursorPos<K,V> pos = CursorPos.traverseDown(rootPage, key); // 2.从根节点开始,根据 key 遍历 btree,找到 key 的位置 pos if (!locked && rootReference != getRoot()) { continue; } Page<K,V> p = pos.page; // 3.获取操作(插入/删除..)位置对应的 pos 对应的 page intindex= pos.index; // 获取当前 key 对应的在 page 上的下标 tip = pos; pos = pos.parent; // 注意:这里需要获取当前 post 对应的父节点 pos(根节点的 parent pos 为 null),因为后面需要根据父节点来更新 root page result = index < 0 ? null : p.getValue(index); // 4.根据 index 获取 page 上的当前值(如果是插入操作当前值为 null) Decisiondecision= decisionMaker.decide(result, value, tip); // 5.根据 当前值、目标值 和 游标位置 决定操作类型
switch (decision) { // 6.具体 mvMap 操作 case REPEAT: decisionMaker.reset(); continue; case ABORT: if (!locked && rootReference != getRoot()) { decisionMaker.reset(); continue; } return result; case REMOVE: { // 6.1.remove 操作 if (index < 0) { if (!locked && rootReference != getRoot()) { decisionMaker.reset(); continue; } returnnull; }
if (p.getTotalCount() == 1 && pos != null) { // 如果当前页只有一个键且有父节点,则尝试移除父节点的键 int keyCount; do { p = pos.page; index = pos.index; pos = pos.parent; keyCount = p.getKeyCount(); // condition below should always be false, but older // versions (up to 1.4.197) may create // single-childed (with no keys) internal nodes, // which we skip here } while (keyCount == 0 && pos != null);
if (keyCount <= 1) { if (keyCount == 1) { // 如果父节点只有一个键,替换父节点为叶子节点 assert index <= 1; p = p.getChildPage(1 - index); } else { // if root happens to be such single-childed // (with no keys) internal node, then just // replace it with empty leaf p = Page.createEmptyLeaf(this); } break; } } p = p.copy(); // 复制页面并移除键 p.remove(index); // 删除 page 指定 index 的值 break; } case PUT: { // 6.2.put 操作 value = decisionMaker.selectValue(result, value); // 6.2.1.获取当前值(如果是事务commit操作,此处会获取 VersionedValueUncommitted 里的 defaultRow) p = p.copy(); // 6.2.2.copy page if (index < 0) { // 6.2.3.插入操作 p.insertLeaf(-index - 1, key, value); // 6.2.3.1.插入叶子节点 int keyCount; while ((keyCount = p.getKeyCount()) > store.getKeysPerPage() || p.getMemory() > store.getMaxPageSize() && keyCount > (p.isLeaf() ? 1 : 2)) { // 6.2.3.2.page 分裂: key数量超过每页最大键数量限制(默认48) 或 page内存超过最大page大小 longtotalCount= p.getTotalCount(); intat= keyCount >> 1; // 6.2.3.2.1.计算 page 分裂位置(中间位置) Kk= p.getKey(at); // 6.2.3.2.2.获取 page 中间位置的 key Page<K,V> split = p.split(at); // 6.2.3.2.3.分裂 page.执行完毕后 split 为分裂后的第二个页面, p 为分裂后的第一个页面 unsavedMemoryHolder.value += p.getMemory() + split.getMemory(); // 6.2.3.2.4.记录需要保存的内存 if (pos == null) { // 6.2.3.2.5.如果当前节点为根节点,则创建一个内部节点作为根节点 K[] keys = p.createKeyStorage(1); // 创建 key 存储空间(1) keys[0] = k; // 插入 key Page.PageReference<K,V>[] children = Page.createRefStorage(2); // 创建 children 存储空间(2) children[0] = newPage.PageReference<>(p); // 插入孩子节点0 page children[1] = newPage.PageReference<>(split); // 插入孩子节点1 split p = Page.createNode(this, keys, children, totalCount, 0); // 创建非叶子节点作为 root 节点, 传入孩子节点信息 break; } Page<K,V> c = p; p = pos.page; index = pos.index; pos = pos.parent; p = p.copy(); p.setChild(index, split); p.insertNode(index, k, c); } } else { p.setValue(index, value); // 6.2.4.更新操作,直接设置 page 指定下标对应的值 } break; } } rootPage = replacePage(pos, p, unsavedMemoryHolder); // 7.替换 root page (pos 为父节点的 pos) if (!locked) { // 未上锁 rootReference = rootReference.updateRootPage(rootPage, attempt); // 更新 root page 引用 if (rootReference == null) { // 没有更新成功 decisionMaker.reset(); // 重试 continue; // continue 重试插入 } } if (isPersistent()) { registerUnsavedMemory(unsavedMemoryHolder.value + tip.processRemovalInfo(version)); } return result; } finally { if(locked) { unlockRoot(rootPage); } } } }