codefollower

Results 225 comments of codefollower

**技术分类**: 数据库-存储引擎 **技术思想**: 相比于行存储,几乎无读写开销的以Page为粒度的混合行列存储 **实现状态**: 已经实现 **细节描述**: 不管是传统的像 MySQL 还是新型的像 Lealone 这样的关系数据库,都会优先考虑使用按行顺序存储的底层存储模型,行存储实现简单,如果字段数不多,每次查询都会查大多数字段时就不存在什么问题,如果有上百个字段,当执行少量字段查询或者进行max(f1)、avg(f2)这样的聚合运算时就会加载其他大量没用到的字段,不但浪费内存,性能也慢。 显然,当加载少量字段时,列存储就有明显优势了,只要按需读取相关列即可。 过去,一些存储引擎会把一些列组合成一个分组,比如 HBase 就有 Column Family 的概念,每个分组会存入不同的文件中,这种方案是以牺牲写性能为代价的,一行记录分开写入不同文件会影响性能。 如果不引入列分组的概念,又不写入多个文件是不是更好呢?考虑到关系数据库的行式存储引擎都会使用B-Tree(或它的变体)来组织数据,B-Tree 通常都由一个个的 Page 组成一棵有层次的树,Page 又细分成 Node Page 和 Leaf Page,Node Page...

**技术分类**: Java语言-接口默认方法 **技术思想**: 使用接口默认方法和简单的适配器模式让实现类模拟出多重继承的效果 **实现状态**: 已经实现 **细节描述**: 虽然多重继承有很多问题,但是在某些场景下使用多重继承能让代码更简洁,Java 语言不直接支持多重继承,过去只能同时实现不同接口来达到类似多重继承的效果,但是这种方式常常产生冗余的代码或设计出更多的类来满足,继承能复用代码减少冗余。 从 Java 8 开始引入了接口默认方法,这能让多重继承的模拟变得更优雅。 最近就碰到了一个适合使用多重继承的场景,Lealone 数据库在网络层设计了 **NetServer** 和 **NetClient** 两个接口,提供了一种插件机制,让不同的网络框架可以集成进来,比如6个实现类 VertxNetServer/VertxNetClient、NettyNetServer/NettyNetClient、MinaNetServer/MinaNetClient 分别用于集成 Vertx、Netty、Mina,另外,基于 NIO 还实现了内置缺省的 NioNetServer/NioNetClient。 因为所有的 NetServer/NetClient 实现类都有一些共同的行为,比如 start、stop、连接管理等等,所以适合把这些行为抽取出来放入基类中,所有的实现类直接从基类 NetServerBase/NetClientBase...

**技术分类**: 数据库-事务引擎 **技术思想**: 列锁 **实现状态**: 已经实现 **细节描述**: 如果以Page为粒度的混合行列存储能实现,是否可以在事务引擎中引入更加细粒度的**列锁**? 最常见的是行锁,Lealone 数据库也支持行锁,但是有时候两个事务根据同一个记录 ID 修改这条记录的不同字段由于行锁的存在是不被允许的,如果有了列锁,因为修改的是不同列,所以这两个事务不会发生冲突,自然就允许同时执行了。 更多细节还在思考中…… **2018/10/15 更新**: 已经实现,例子见: [UpdateTest](https://github.com/lealone/Lealone/blob/master/lealone-test/src/test/java/org/lealone/test/sql/dml/UpdateTest.java) 代码中的 testColumnLock 测试了 4 个事务对同一行进行不同字段的更新,只有第三个事务失败了。

**技术分类**: 数据库-存储引擎 **技术思想**: 异步并行化的无锁 BTree (核心是把 Page 的更新操作绑定到固定线程) **实现状态**: 已经实现 **细节描述**: 要在 B-Tree(或它的变体) 上实现高性能的无锁操作是一件很复杂的事情,考虑到 B-Tree 由一个个的 Page 组成,如果针对某个 Page 的更新操作都固定分配给唯一的线程执行,那么在这个 Page 上就不存在并发冲突了,考虑到实际场景中,一个 Page 的记录数多在 1000 行以下,所以只要 Page 之间的热点是均衡的,那么就能达到很好的并行效果,如果热点都落在同一个 Page 上,那么由一个线程处理这...

**技术分类**: 数据库-调度器 **技术思想**: 减少服务线程种类,实现统一的异步任务调度器 **实现状态**: 已经实现 **细节描述**: Lealone 数据库从2015年开始就引入了异步化思想,把原来的每连接每线程模型变成了多线程异步化模型,每个连接不再绑定到固定的线程,线程与连接分离,事务也与线程分离。 对于事务的处理流程也已经完全异步化,整个流程按运行阶段被打散成不同的子任务,每个子任务会放到不同的线程中运行,比如当客户端创建连接后发来一条 SQL 时,后端处理流程的第一个阶段会由 **NetServer** 的事件循环服务线程处理,它会读取字节流,得到一个完整的协议包后,进入流程的第二个阶段。 第二阶段由**命令处理器**负责,它会解析协议包,得到 SQL 字符串后就进入第三个阶段了。 中间还有很多个阶段,直到事务相关的 redo 日志由**日志同步线程**安全写入硬盘后才让 NetServer 把响应结果发给客户端,此时整个流程才算结束。 这种把一个大任务(整个处理流程)按阶段分成不同子任务然后由不同线程(或线程组)处理子任务的思想其实就是 [SEDA](https://en.wikipedia.org/wiki/Staged_event-driven_architecture)。这种架构当然也有缺点,每个阶段的子任务会被放入放出到不同的队列,然后还得适当的调用线程唤醒操作,这些都是开销,而且当 cpu 核数小于流程的阶段数的话也会带来不少上下文切换开销。 既然子任务都异步化了,为何不把它们一视同仁呢,为何不用一个或少量的线程组就能处理完呢? 这其中的原因多半还是怕某个子任务是计算型的,只要运行这种任务,线程很快就会占满,所以在没有抢占调度的情况下,使用一个固定的线程组并不合适。 所以,问题的核心还是如何包装子任务,让这些已经异步化的子任务能在某些合适的检查点让出线程,或者当来了优先级更高的任务后低优先级的也要让出线程,只有这样才能放心使用统一的线程组调度这些任务。 **2019/1/23...

第一个问题,因为事务ID是递增的,所以当客户端发来一个读或写请求时,都会开启一个新事务,并且事务ID总是大于以前的ID,并且当前事务ID列表里只会存放从系统启动以后新创建的事务,上次由于崩溃而写入硬盘的脏数据中携带的事务ID是不会大于当前事务ID列表中的ID的。事务ID是long类型,目前没有复用之前的老ID,也就是一直单调递增的。(当然,最好还是禁用底层存储的自动保存功能) 第二个问题,对于查询,如果是列存储模式,就是查哪列就从硬盘读哪列。但是内存每行对应一个object的对象数组,这样才能按列的index做正确的填充,目前没用map,而是用数组来表示内存中的一行记录。 null的列写到硬盘会用一个字节表示。 事务是一样的,并且还支持列锁,多个事务更新同一行的不同列不会冲突。这在MySQL是做不到的。

要让每个事务有一个唯一的标识符不难,比如最简单的uuid,所有当前事务id肯定跟之前的id不一样,读到上次不小心写入的脏数据,只要拿出id跟所有当前事务id比一下就知道了,不在当前事务的id列表中,必然断定是脏的。

@KangZhiDong 我在GitHub上面没提到BATS引擎吧,Lealone好久没更新了,BATS引擎是内部项目,暂时没有开源的打算。 不过BATS引擎支持的SQL语句类型肯定是目前已经开源的Lealone的超集的。

collection类型并不是NoSQL特有的,这只是Cassandra自己的特性,SQL不是也没有。

八九年前跟现在哪有什么可比性,那时的阿里又没上市,淘宝网才刚起来没几年,整个中间件团队P7的人数都是屈指可数的。P6能有15万年薪就不错了,还不算被扣的个人所得税和五险一金。现在P6能拿25万以上我是相信的。我算幸运的了,一些名校研究生毕业的同组同事进来工作两年才到P6。