首页 文章 精选 留言 我的

精选列表

搜索[权限与责任],共10000篇文章
优秀的个人博客,低调大师

h2database BTree 设计实现与查询优化思考 | 京东云技术团队

h2database 是使用Java 编写的开源数据库,兼容ANSI-SQL89,既实现了常规基于 BTree 的存储引擎,又支持日志结构存储引擎。功能非常丰富(死锁检测机制、事务特性、MVCC、运维工具等),数据库学习非常好的案例。 本文理论结合实践,通过BTree 索引的设计和实现,更好的理解数据库索引相关的知识点以及优化原理。 BTree 实现类 h2database 默认使用的 MVStore 存储引擎,如果要使用 基于 BTree 的存储引擎,需要特别指定(如下示例代码 jdbcUrl)。 以下是常规存储引擎(BTree 结构) 相关的关键类。 org.h2.table.RegularTable org.h2.index.PageBtreeIndex(SQL Index 本体实现) org.h2.store.PageStore(存储层,对接逻辑层和文件系统) BTree 的数据结构可以从网上查到详细的描述和讲解,不做过多赘述。 需要特别说明的是:PageStore。我们数据查询和优化关键的缓存、磁盘读取、undo log都是由 PageStore 完成。可以看到详细的文档和完整的实现。 BTree add index entry 调用链 提供索引数据新增的调用链。同样的,索引的删除和查询都会涉及到,方便 debug 参考。 org.h2.command.dml.Insert#insertRows (Insert SQL 触发数据和索引新增) org.h2.mvstore.db.RegularTable#addRow (处理完的数据Row, 执行新增) org.h2.index.PageBtreeIndex#add (逻辑层增加索引数据) org.h2.index.PageDataIndex#addTry (存储层增加索引数据) org.h2.index.PageDataLeaf#addRowTry (存储层新增实现) // 示例代码 // CREATE TABLE city (id INT(10) NOT NULL AUTO_INCREMENT, code VARCHAR(40) NOT NULL, name VARCHAR(40) NOT NULL); public static void main(String[] args) throws SQLException { // 注意:MV_STORE=false,MVStore is used as default storage Connection conn = DriverManager.getConnection("jdbc:h2:~/test;MV_STORE=false", "sa", ""); Statement statement = conn.createStatement(); // CREATE INDEX IDX_NAME ON city(code); 添加数据触发 BTree 索引新增 // -- SQL 实例化为:IDX_NAME:16:org.h2.index.PageBtreeIndex statement.executeUpdate("INSERT INTO city(code,name) values('cch','长春')"); statement.close(); conn.close(); } Code Insight 结合上述的示例代码,从索引新增的流程实现来了解BTree 索引的特性以及使用的注意事项。从底层实现分析索引的运行,对 SQL 索引使用和优化有进一步认识。 表添加数据 public void addRow(Session session, Row row) { // MVCC 控制机制,记录和比对当前事务的 id lastModificationId = database.getNextModificationDataId(); if (database.isMultiVersion()) { row.setSessionId(session.getId()); } int i = 0; try { // 根据设计规范,indexes 肯定会有一个聚集索引(h2 称之为scan index)。① for (int size = indexes.size(); i < size; i++) { Index index = indexes.get(i); index.add(session, row); checkRowCount(session, index, 1); } // 记录当前 table 的数据行数,事务回滚后会相应递减。 rowCount++; } catch (Throwable e) { try { while (--i >= 0) { Index index = indexes.get(i); // 对应的,如果发生任何异常,会移除对应的索引数据。 index.remove(session, row); } } throw de; } } ① 同Mysql InnoDB 数据存储一样,RegularTable 必有,且只有一个聚集索引。以主键(或者隐含自增id)为key, 存储完整的数据。 聚集索引添加数据 索引中的 key 是查询要搜索的内容,而其值可以是以下两种情况之一:它可以是实际的行(文档,顶点),也可以是对存储在别处的行的引用。在后一种情况下,行被存储的地方被称为堆文件(heap file) ,并且存储的数据没有特定的顺序(根据索引相关的)。 从索引到堆文件的额外跳跃对读取来说性能损失太大,因此可能希望将被索引的行直接存储在索引中。这被称为聚集索引(clustered index)。 基于主键扫描即可唯一确定、并且获取到数据,聚集索引性能比非主键索引少一次扫描 public void add(Session session, Row row) { // 索引key 生成 ② if (mainIndexColumn != -1) { // 如果主键非 long, 使用 org.h2.value.Value#convertTo 尝试把主键转为 long row.setKey(row.getValue(mainIndexColumn).getLong()); } else { if (row.getKey() == 0) { row.setKey((int) ++lastKey); retry = true; } } // 添加行数据到聚集索引 ③ while (true) { try { addTry(session, row); break; } catch (DbException e) { if (!retry) { throw getNewDuplicateKeyException(); } } } } ② 对于有主键的情况,会获取当前 row 主键的值,转为long value。对于没有指定主键的情况,从当前聚集索引属性 lastKey 自增得到唯一 key。 只有指定主键的情况,才会校验数据重复(也就是索引key 重复,自增 lastKey 是不会有重复值的问题)。 ③ 聚集索引 PageDataIndex 按照BTree 结构查找对应的key 位置,按照主键/key 的顺序,将 Row 存储到page 中。非聚集索引 PageBtreeIndex 也是这样的处理流程。 这其中涉及到三个问题: 如何查找 key 的位置,也就是 BTree 位置的计算? 如何计算 Row (实际数据)存储 Page 中的 offsets? Row 是怎样写入到磁盘中的,何时写入的? 索引数据存取实现 B 树将数据库分解成固定大小的块(block)或分页(page) ,传统上大小为 4KB(有时会更大),并且一次只能读取或写入一个页面。 每个页面都可以使用地址或位置来标识,这允许一个页面引用另一个页面 —— 类似于指针,但在硬盘而不是在内存中。(对应h2 database PageBtreeLeaf 和 PageBtreeNode) 不同于 PageDataIndex ,PageBtreeIndex 按照 column.value 顺序来存储。添加的过程就是比对查找 column.value,确定在块(block)中offsets 的下标 x。剩下就是计算数据的offset 并存入下标 x 中。 /** * Find an entry. 二分查找 compare 所在的位置。这个位置存储 compare 的offset。 * org.h2.index.PageBtree#find(org.h2.result.SearchRow, boolean, boolean, boolean) * @param compare 查找的row, 对应上述示例 compare.value = 'cch' * @return the index of the found row */ int find(SearchRow compare, boolean bigger, boolean add, boolean compareKeys) { // 目前 page 持有的数据量 ④ int l = 0, r = entryCount; int comp = 1; while (l < r) { int i = (l + r) >>> 1; // 根据 offsets[i],读取对应的 row 数据 ⑤ SearchRow row = getRow(i); // 比大小 ⑥ comp = index.compareRows(row, compare); if (comp == 0) { // 唯一索引校验 ⑦ if (add && index.indexType.isUnique()) { if (!index.containsNullAndAllowMultipleNull(compare)) { throw index.getDuplicateKeyException(compare.toString()); } } } if (comp > 0 || (!bigger && comp == 0)) { r = i; } else { l = i + 1; } } return l; } ④ 每个块(page)entryCount ,两个方法初始化。根据块分配和实例创建初始化,或者 PageStore 读取块文件,从Page Data 解析得到。 ⑤ 反序列化过程,从page 文件字节码(4k的字节数组),根据协议读取数据并实例化为 row 对象。参考: org.h2.index.PageBtreeIndex#readRow(org.h2.store.Data, int, boolean, boolean) 。 ⑥ 全类型支持大小比对,具体的规则参考:org.h2.index.BaseIndex#compareRows ⑦ 如果数据中存在重复的键值,则不能创建唯一索引、UNIQUE 约束或 PRIMARY KEY 约束。h2database 兼容多种数据库模式,MySQL NULL 非唯一,MSSQLServer NULL 唯一,仅允许出现一次。 private int addRow(SearchRow row, boolean tryOnly) { // 计算数据所占字节的长度 int rowLength = index.getRowSize(data, row, onlyPosition); // 块大小,默认 4k int pageSize = index.getPageStore().getPageSize(); // 块文件可用的 offset 获取 int last = entryCount == 0 ? pageSize : offsets[entryCount - 1]; if (last - rowLength < start + OFFSET_LENGTH) { // 校验和尝试分配计算,这其中就涉及到分割页面生长 B 树的过程 ⑧ } // undo log 让B树更可靠 ⑨ index.getPageStore().logUndo(this, data); if (!optimizeUpdate) { readAllRows(); } int x = find(row, false, true, true); // 新索引数据的offset 插入到 offsets 数组中。使用 System.arraycopy(x + 1) 来挪动数据。 offsets = insert(offsets, entryCount, x, offset); // 重新计算 offsets,写磁盘就按照 offsets 来写入数据。 add(offsets, x + 1, entryCount + 1, -rowLength); // 追加实际数据 row rows = insert(rows, entryCount, x, row); entryCount++; // 标识 page.setChanged(true); index.getPageStore().update(this); return -1; } ⑧如果你想添加一个新的键,你需要找到其范围能包含新键的页面,并将其添加到该页面。如果页面中没有足够的可用空间容纳新键,则将其分成两个半满页面,并更新父页面以反映新的键范围分区 ⑨为了使数据库能处理异常崩溃的场景,B 树实现通常会带有一个额外的硬盘数据结构:预写式日志(WAL,即 write-ahead log,也称为重做日志,即 redo log)。这是一个仅追加的文件,每个 B 树的修改在其能被应用到树本身的页面之前都必须先写入到该文件。当数据库在崩溃后恢复时,这个日志将被用来使 B 树恢复到一致的状态。 实践总结 查询优化实质上就是访问数据量的优化,磁盘IO 的优化。 如果数据全部缓存到内存中,实际上就是计算量的优化,CPU 使用的优化。 索引是有序的,实际上就是指块文件内的 offsets 是以数组形式体现的。特殊的是,在h2database 中,offsets数组元素也是有序的(例如:[4090, 4084, 4078, 4072, 4066, 4060, 4054, 4048, 4042]),应该是方便磁盘顺序读,防止磁盘碎片化。 理论上,聚集索引扫描 IO 比 BTree 索引要多,因为同样的块文件内,BTree 索引 存储的数据量更大,所占的块文件更少。如果一个table 列足够少,聚集索引扫描效率更高。 建表需要谨慎,每个列的字段长度尽可能的短,来节省页面空间。 合理使用覆盖索引查询,避免回表查询。 如述示例,select id from city where code = 'cch',扫描一次 BTree 索引即可得到结果。如果select name from city where code = 'cch', 需要扫描一次 BTree 索引得到索引key (主键),再遍历扫描聚集索引,根据 key 得到结果。 合理的使用缓存,让磁盘IO 的影响降到最低。 比如合理配置缓存大小,冷热数据区分查询等。 其他知识点 分支因子为 500 的 4KB 页面的四层树可以存储多达 256TB 的数据)。(在 B 树的一个页面中对子页面的引用的数量称为分支因子(branching factor) 。 参考 ddia/ch3.md B树 作者:京东物流 杨攀 内容来源:京东云开发者社区

优秀的个人博客,低调大师

Taro 3.6.8 发布,BAT 小程序、H5 与 RN 端统一框架

Taro 3.6.8 现已发布。Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 等应用。 此版本具体更新内容如下: 特性 H5 支持配置页面滚动方式(全局滚动或局部滚动) 新增 API:saveVideoToPhotosAlbum、saveImageToPhotosAlbum、addPhoneRepeatCalendar、addPhoneCalendar、chooseMedia、getVideoInfo、canIUse 修复 修复 Webpack5 启动报错:Invalid option from onResolve() callback in plugin "scanImports": "importer",#13767 修复 Webpack5 开启cache功能后报 warning 的问题,#13798 修复 CSS 内静态资源路径alias不生效的问题,#13906 小程序 修复抽取sub-common模块时没有输出sourcemap的问题,#13873 修复 Vue2 中使用 JSX 报错找不到template的问题,#13749 修复 common 公共样式问题,#8168 修复 Vue3 ref 引用的元素不能正确触发更新的问题 修复InputTextarea的cursor属性初始值不正确的问题,#13809 H5 修复Taro.showModal当浏览器退回到上一页面时不消失的问题和遮罩层滚动穿透的问题,#13833 修复MPA模式下onReady钩子不执行问题 修复使用 React 框架时,classnames赋值为空时错误判断导致的问题,#13781 修复地图样式抖动和重复注入等问题,#13808 修复Picker组件事件捕获问题,优化columnChange事件定义问题,并提示不支持的mode,#13696 优化Checkbox值获取类型问题,#13694 修复CustomWrapper没有生成 DOM 节点的问题 修复使用 ref 调用Input、Textarea组件的focus方法失败的问题,#13935 修复含副作用生命周期注册问题 @tarojs/plugin-html 修复编译报错:@tarojs/plugin-html tried to access @babel/generator, but it isn't declared in its dependencies; @tarojs/plugin-http 修复发送请求时未正确携带包含httpOnly的cookie的问题,#13941 Typings 增加 Vue3Button组件获取电话号码的示例,#11445 补充functionalPages类型 详情可查看更新说明。

优秀的个人博客,低调大师

Taro 3.6.7 发布,BAT 小程序、H5 与 RN 端统一框架

Taro 3.6.7 现已发布。Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 等应用。 此版本具体更新内容如下: 特性 Picker组件新增textProps属性,#12489 RN RN 入口支持函数式组件 入口组件支持useLaunch,useDidShow,usePageNotFound生命周期 hooks 小程序 消息机制支持Symbol类型作为事件名 修复 小程序 完善 Taro React Reconciler 的事件处理,修复Input、Textarea组件在受控情况下,value 值无法正常显示的问题,#13120 修复 app 样式引用 common 样式的语法问题,#13764 修复小程序设置自定义环境变量失败的问题 完善Taro.getAppInfoAPI H5 修复从普通页面返回 tabbar 页面时动画会消失的问题 修复InnerAudioContext暂停后再播放没有继续上次进度播放的问题,#13840 修改Taro.request传入array类型数据序列化问题 修复 windows 下开启multi router后页面不显示的问题,#13134,by@biorz 为部分暂不支持的 API 补充提示,by@smileying 为Text组件补充userSelect、space、numberOfLines参数支持,#13747 为RichText组件补充userSelect、selectable、space参数支持 修复设置enablePullDownRefresh后,onPageScroll和onPullDownRefresh方法失效的问题,#13782 RN 修复Taro.updateFile的参数不传formData时报错的问题,#13513 Webpack5 修复 prebundle 报错,#13767 @tarojs/plugin-mini-ci 上传、预览失败时终结应用,#13761 @tarojs/plugin-http 优化事件属性的实现,#13824 Typings 修复Textarea组件的类型定义 LivePlayer增加投屏接口 更新说明:https://github.com/NervJS/taro/releases/tag/v3.6.7

优秀的个人博客,低调大师

Taro 3.6.6 发布,BAT 小程序、H5 与 RN 端统一框架

Taro 3.6.6 现已发布。Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 等应用。 此版本具体更新内容如下: 特性 新增Taro.interceptorifyAPI 新增Taro.getAppInfoAPI 小程序 支付宝小程序支持使用自定义 Tabbar,#13610 新增getRenderer方法获取页面渲染引擎类型,#13587 新增injectNewStyleProperties运行时钩子,用于注入需要额外支持的 CSS 属性 CLI 新增了全局插件和插件集配置,可以在没有项目 config 文件的情况下运行 Taro 相关插件,参考文档 babel-preset-taro 支持修改 react presets 的配置 修复 小程序 修复virtual-list导出问题,#13360#13442#13702 修复微信小程序视频号相关 API 没有 Promise 化的问题 修复logInterceptor在生产环境没有 Log 输出的问题,#11576 修复history.state的返回不符合 web 规范的问题,#13723 支持设置aspectRatio样式属性 H5 修复Swiper组件衔接模式的initial值,#13589 支持Textarea组件的confirm事件,#12911 补充cleanInterceptors方法,#11339 修复Swiper组件轮播显示问题,#13589 修复multi路由模式跳转错误,#13685 postcss-pxtransform H5 支持px单位,小程序支持rem和px单位,#7637#10212 @tarojs/plugin-http 兼容 Webpack4 @tarojs/create-app 修复npm create导致项目初始化后自动安装依赖报错的问题 Typings 修复View组件disableScroll属性的类型定义 修复getRandomValues方法的类型问题,#13673 修复designWidth类型 Perf 优化小程序环境调用getElementsByClassName方法的性能 更新说明:https://github.com/NervJS/taro/releases/tag/v3.6.6

优秀的个人博客,低调大师

Taro 3.6.5 发布,BAT 小程序、H5 与 RN 端统一框架

Taro 3.6.5 现已发布。Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 等应用。 此版本具体更新内容如下: 特性 H5 支持根据 html 模板自定义节点位置 in#13630 RN ScrollView 组件支持透传 scrollEventThrottle by@peterczgin#13496 修复 小程序 修复 vue 2 模版下 eslint-plugin-vue 版本问题导致的 npm 依赖安装报错 in#13620 修复部分场景小程序热更新问题 in#13625 H5 修补 tabbar 生命周期错误 in#13597 修复 taroApis 导出错误 in#13596 修复 ready 页面在部分场景下不触发问题 in#13622 修复部分场景 wasm 错误 in#13625 修复新版本部分环境下下拉组件未注册问题 in 13635 RN 修复 socketTask 缺少字段 by@linhaobinin#13645 其他 CLI 调整配置文件读取逻辑 in#13653#13655 模板 默认模板使用 React 函数组件 by@AdvancedCatin#13632 Typings 修复 bluetooth-ble 回调错误 by@huang6349in#13567 完善 runner 中类型推断 by@AdvancedCatin#13594 ScrollView 的 type 参数调整为可选 by@linhaobinin#13642 更新说明:https://github.com/NervJS/taro/releases/tag/v3.6.5

优秀的个人博客,低调大师

Apinto V0.12 发布:新增流量镜像与 Mock 插件,路由特性更丰富

Hello~ 各位开发者朋友们好呀, Eolink 旗下开源网关 Apinto 本周又更新啦!这次的更新我们给大家带来了 2个好用的插件,且目前已经支持静态资源路由了!希望新的功能能让大家的开发工作更加高效 ~ 1、新增流量镜像插件 流量镜像(eolinker.com:apinto:proxy_mirror) 插件提供了镜像客户端请求的能力。流量镜像是将线上真实流量拷贝到镜像服务中,以便在不影响线上服务的情况下,对线上流量或请求内容进行具体的分析。 使用流量镜像插件前,请求调用链如下图: 使用流量镜像插件后,请求调用链如下图: 2、新增 Http Mocking插件 API Mock 是一种技术,它允许程序员在不依赖后端数据的情况下,模拟 web 服务器端 API 的响应。通常使用 API Mock 来测试前端应用程序,而无需等待后端程序员构建完成。API Mock 可以模拟任何 HTTP 请求方法,并进行响应测试。 Apinto 提供HTTP Mocking 插件(eolinker.com:apinto:http_mocking)来模拟 Api Mock 请求响应数据,无需等到后端接口上线,通过模拟数据进行前端应用程序调试。 3、丰富 http 路由特性,支持静态资源路由 在 Apinto V0.12 版本之前,若客户端想获取静态资源,仍然采取代理转发到上游服务获取资源,耗时费力,且增加上游服务压力,不可取。为了解决这一痛点,Apinto V0.12 版本新增了静态资源路由,可以设置接口的默认返回,满足客户端需求,这包括静态 HTML 页面、静态的接口数据(Json、XML 等格式数据)、页面重定向等等。 下面,我们通过Apinto Dashboard配置了一个重定向到【Apinto 官网 www.apinto.com 】的静态资源路由示例

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册