Giter VIP home page Giter VIP logo

my-blog's People

Contributors

codefollower avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

my-blog's Issues

再见 2017,你好 2018

退出微博后,好久没自言自语写点东西了,
因为2017年过得有点糟,所以还是忍不住写篇流水账纪念一下它。

生活

2017年,也是我的本命年,都说本命年犯太岁要穿红裤衩才能趋吉避凶、消灾免祸,我倒是忘了,也没人提醒我,果真过完春节后就开始突如其来的腰腿疼痛坐立不安,连走路都怕,右脚放射疼是很恐怖的,冷不丁的来一下。

我不迷信,拿本命年说事自然是为去年加班太猛找借口,36岁毕竟已是中年,若是一年365天平均每天坐在电脑前10小时以上,身体总要出点毛病的。

就医的过程倒也奇葩,从二月到八月中旬,换了三家医院,从西医到中医,也看不出个所以然来,
后来药也不吃了,医生也不看了,也不知是什么原因触发想去骑自行车,第一次从市中心广场延中山路骑完西二环路,当然得强忍着疼痛。第二天接着骑时居然腰腿不痛了,很神奇,为了巩固效果,连续在桂林市区附近骑了十几天小黄车,然后整个九月份在十几个省会城市继续骑行,边逛边吃边锻炼,到现在都没有再痛过。

有些医生建议我多走路做牵引,还有些让我躺床上屈膝抱腿前后运动,我都做了,都没用。还有医生建议我手术,还好我没听。没有一个医生告诉我骑自行车对我有用。中秋节时我花了三千多买了辆山地自行车,心情好时就去桂林周边的县转转,一边锻炼一边欣赏风景,当然还有自虐,比如上周末走大境瑶族乡到洞井瑶族乡那段路,想哭的心都有……

不管怎样,现在骑行已经变成一种生活习惯了,保守估计今年已经骑行了3500多公里,为了身体健康还是得养成运动的习惯,骑行比在学校操场跑圈有趣一点,就是多了一点风险,我累计骑行过20小时以上的夜路,还好都没碰到什么事故。以后能不走夜路就尽量不走,要走也要提前给电筒充足电量。

工作

因为身体的原因,今年的所有工作计划基本上没有什么进展,上半年只有一个简单的二期项目,项目款都已经顺利拿到。下半年在我的建议下,合伙人另开了家公司,再加上我也不想接项目了,所以我司在下半年基本上没什么事做。

最近两个月因为身体好转了,又开始出差了,先是去了广州半个月,合作伙伴拿到电信的新大数据项目,我帮他们的.net团队转型到java/hadoop/spark平台,还好这项目没太多技术难度,只是团队没什么技术积累,需要一个人带,把架构、代码框架整好,写好一些模板样例,让他们填业务逻辑即可,有C#基础转java还是可以边做边学的,只是苦了我啦,三四个人一天到晚问一些非常初级的问题,为了不影响项目进度,也只能有问必答了。还好半个月就已经带得差不多了,刚好合伙人这边的移动公司的新项目又开始了,又跑到另一城市出差。

做项目确实累,从普通的web项目到大数据项目已经做腻了,若不是为了救火,已经不想再做。
我已经跟所有人讲了,从2018年开始不要再找我做项目,除非给的钱特别多。:)

投资

投资给家里种的1000棵砂糖橘今年刚结果,差不多5年了,第一次种没有经验,再加上土地贫瘠,多花了一年半时间才有收成,还好今年的成本总算是收回来了,还小赚了一点点。还是挺欣慰的,不容易,通过投资帮助一家人自力更生比直接给钱还难,但是又不得不这么做。

今年开始关注美股了,主要是之前在阿里工作时虽然辞职早了两年,但还是有400多股挂在那,时不时就得去看一下,这几年都没卖,发现涨了挺多,关注的几支股票都在涨,可惜都没买。美股做中长期投资还是可以的,正好平时也喜欢看一些科技公司和业界的相关动向,还是有一些自己的判断力的。

开公司算是最大的投资吧,除此之外,有闲钱还是可以考虑其他方向的小投资,20万以内的,有好的方向就投点,虽然有风险,就算亏也不会亏太多,当然,银行里也要适当放一点,用于应急和日常生活中的各种开支。

中年危机

不得不承认,我也中年了,还好30岁时就已经从大城市回到家乡回到桂林这样的三线城市,5年下来已经习惯了,反而不喜欢往大城市跑了。在桂林这样的城市生活工作,压力都不大,所以我基本上没有中年的危机感,只是随着年纪越来越大,身边熟悉的人慢慢离开了,今年我外公外婆都相继离世。

只要人的物质欲望少点,其实也没有所谓的危机,就像我至今都没买车买房,在物质上没什么攀比之心,两三万块钱生活一年就过得挺不错了,挣这点生活费还是很容易的,开支小,就不用整天被钱驱使折磨。

微博

我知道会有一些网友想念微博上的那个“我”,但那不是真实的“我”,他很高调很狂傲,挺适合给这个无聊的技术圈带来一些乐趣的,但现实的我已经对微博无爱了,退出微博前以为少了微博没法活,这近两个月的时间才发现微博对于生活真不是必需的,不玩微博反而多了时间思考别的事。

2018

马上就2018了,对于2018没太多奢求,首先就是希望自己和家人朋友都身体健康,然后工作上专心做好一件事,不再接任何项目了,把未做完的数据库产品做完,这可是我多年的心血,就算最后赚不了大钱,也必须把这产品做完,要有始有终。

想通过自学成为一个有良好计算机专业素养的Java程序员要读哪些书?

  1. 前提条件:
    需要有高中毕业的水准
    (或者同等学历的也行,比如中专,我本人就是读中专然后再读自考大专的)。

  2. 学习方式:
    完全可以自学,最好自己有电脑。

  3. 偏重方向:
    偏重软件,而不是计算机硬件。

  4. 英语要求:
    掌握3000个左右的常用单词,能读就差不多了,
    如果不想看英文可以找中文翻译版的,我个人偏向看中文,对于非常好的书我通常中英文都买来看。

    以下列出的书是按照循序渐进的方式介绍的,
    都是我自学过程中学过的并且觉得不错的书,
    计算机基础类的书籍与Java相关书籍的比例是5:2,所以非常强调基础,
    核心书籍将近35本,预计需要花两年半时间左右,
    别觉得书很少,能平均一个月完成一本算挺不错了,说明你在计算机方面有一些天赋。:)

    另外,扩展阅读也有35本,这些可以选读。

    (注:这是我12年来的自学生涯中特意挑选出来的书,如果当初就有人告诉我该这么自学我肯定会少走无数的弯路)

目录

  1. 计算机基础类的书籍
  2. Java相关书籍
  3. 扩展你的眼界

1. 计算机基础类的书籍

1.1 与计算机相关的数学

主要有: 微积分、线性代数、概率论与数理统计、离散数学

1-1-1
1-1-2
1-1-3
1-1-4
相对来说离散数学最有用,其他三门在从事数值计算、计算机图形学、算法复杂度分析等领域会用得多一些,
国内的大学很多都是使用高等教育出版社同济大学写的书,我也读过,
不过,我还是推荐顺便读一读国外出的这4本,学这4门课对自己的要求不必像数学系的学生那么苛刻,能达到70分的水准就够用了。

1.2 计算机科学导论

1-2-1

顾名思义,这是计算机专业的学生第一本要读的专业书,
这本书对整个计算机科学的各个领域做了一个概述,点到为止,
包括最基本的十进制、二进制、十六进制;数据的存储、运算;
计算机组成、网络、操作系统、算法与数据结构、程序设计语言、数据库、人工智能等等。

读完这本书,就知道整个计算机学科都有哪些东西了,这本书适合泛读,若碰到不理解的,可以直接跳过。

1.3 C程序设计语言

1-3-1
程序设计的入门课,"Hello,World"就出自这里,虽然只有160多页,却是永恒的经典。
此书要精读,并且需要在电脑上跑通书上的代码例子,习题也要做一做。
1-3-2
另外,推荐再读『程序设计抽象**-C语言描述』,这本书即是C语言的应用实践课,又是进一步学习数据结构和算法的入门读物,
书里用了大量的例子介绍了递归、堆栈、链表、树、图等等非常常用的知识,并且还引申到Java的一些基本概念。

1.4 理解计算机系统

1-4-1
这本书主要是介绍计算机整体的软、硬件系统的,包括处理器、存储器层次结构、虚拟存储器、链接、IO、网络等等,
读完它你大概知道你所写的程序是怎么通过计算机的硬件和底层软件系统运行的,
里面时不时提到C语言,所以先学C语言再学这本书比较合理(或者穿插着学也可以)。
1-1-4
另外,可选的,推荐再读『计算要组成及汇编语言原理』,书不厚,240多页,最主要是以Java虚拟机为例来讲的。
我在读自考大专时是用这本: IBM-PC宏汇编语言程序设计

1.5 算法导论

1-5-1
学算法的都知道这本书吧,非常有名,老实说这书有点难度,需要自己慢慢啃,
去大公司面试时特别用得着,快速排序、二叉树、红黑树、动态规划、最短路径这类算法会常拿出来问的。

1.6 操作系统

1-6-1
1-6-2
进程、线程、CPU调度、同步、死锁、内存管理、虚拟内存、文件系统、I/O系统这些最基本的概念和原理必须掌握和理解透,
对于以后的工作是相当有用的。

1.7 计算机网络

http://book.douban.com/subject/1179807/
这本书一时没找到,我就不拍照片了,只要大概了解一下即可,除非打算将来做网络管理员之类的工作。

1.8 程序语言理论与编译原理

1-8-1
1-8-2
1-8-3
1-8-4
1-8-5
1-8-6
1-8-7
1-8-8
这是我目前工作和研究的领域。

这类书比较偏理论,会有点难读,不过我还是强烈建议你硬着头皮认真读一读,
只要你能理解透书中的内容,以后你再学习新的程序语言都是件轻而易举的事,
并且也是从事数据库SQL引擎研究的前提。

先读『自动机理论、语言和计算导论』,
里面告诉你什么是自动机、什么是正则语言、什么是上下文无关语言,还有图灵机。
清华大学的『形式语言与自动机理论』也可以同时看。

接着就可以看『编译原理』了,此书也叫“龙书”,当你想从事编译器开发时这本书是必看的,
『编译原理』出了两个版本了,如果有钱的话我还是建议你两个版本都买,
『编译原理』和『自动机理论、语言和计算导论』的内容有一些重叠,
有些人一上来就直接看『编译原理』而不看自动机和形式语言,这会有点难,
『自动机理论、语言和计算导论』算是『编译原理』的前导课程。

最后可以再读读『程序设计语言原理』和『程序设计语言实践之路』
这两本书其实不偏重理论的,有了前面的基础反而更简单,算是各类程序语言的综述,
各种泛式的语言都有涉及,包括: 过程式语言、面向对象、函数式语言,甚至基于逻辑的程序语言都讲了(比如Prolog)。

1.9 数据库

1-9-1
1-9-2
1-9-3
1-9-4

这也是我现在的工作和研究领域。

要说数据库和程序语言是整个计算机学科应用最广泛的领域应该没有人会反对,
关系数据库管理系统(RDBMS)在过去的几十年直到现在都占着绝对的**地位,
随着互联网、社交网络的发展以及数据量的增长,图数据库、NoSQL都在蓬勃发展,
所以可以预计未来5年内数据库会是一个非常活跃的领域。

数据库的理论书籍不需要读得太多,
只要读完上面的『数据库系统基础教程』『数据库系统实现』基本上够用了,
当然,你需要慢慢的品尝、要精读,至少要读两遍以上。

『事务处理』这本书如果你将来不从事数据库事务领域的研究不需要读,
前面两本书中也会有好几个章节讲事务相关的知识。

至此,如果能学完、学好上面9小节中列出的书籍和相关知识,
那么恭喜你,你的计算机基础已经打得非常牢固了。

2. Java相关书籍

2.1 入门

2-1-1
2-1-2
我用的版本比较老, 『Java核心技术』最新已出到第9版了,只有经得起时间考验的书才值得一版再版,
每一个Java程序员都应该持有一本 『Java核心技术』 。
无需多说,除了AWT、Swing、Applet、RMI、CORBA、XML、JNI用得相对较少或过时的技术章节之外,
其他的你应该而且必须好好细读并且一边读一边在电脑上面操作练习。

另外,有关线程、IO、JDBC、网络、安全相关的章节在下面都会有更专业的书来介绍,
这类书归入进阶类。

2.2 Java 线程

2-2-1
这本书讲了有关线程的各种API,还有一些基本概念:死锁、竞态条件、锁、条件变量、屏障等等,
如果你想从事一些类似Tomcat这种Server的开发,理解线程相关的东西是非常必要的。

2.3 Java IO/NIO

http://book.douban.com/subject/1433583/
我是在原来的公司内部看的英文电子版,跟『Java线程』 的风格类似,完整的介绍相关API和基本概念。
NIO的知识对于实现像Netty这样的高性能网络框架必不可少,同样像Tomcat、Jetty也太量运用了NIO。

2.4 Java 网络编程

2-4-1
又是一本经典好书,如果你想写出好的网络程序,必须读读这本书,
InetAddress、Socket要是你连这些都还不懂,赶紧买这本书来看。

2.5 Java 安全

2-5-1
一本常被“无视”的好书,在国内没多少人听说过...此书作者跟『Java 线程』 的作者是同一个人,
书里介绍了Java的整个安全模型,沙箱、安全管理器、密匙、证书、消息摘要、数字签名、SSL等等,
这本书并不讲高深的理论,非常实用,
在Tomcat、Jetty、Netty、Cassandra、H2这类开源项目需要涉及安全的地方都会用到这书里提到的一些技术。

2.6 Java JDBC

http://book.douban.com/subject/1886537/

我也是看的电子版,不过没读完,因为之前看过JDBC规范了,所以就没什么必要再细读它,
虽然类似Hibernate和iBatis这类ORM和SQLMapping框架用得比较多,
不过我还是推荐你先把JDBC掌握了,这样不管以后你去看MySQL的JDBC驱动还是iBatis的源代码都会非常有帮助。

2.7 Effective Java

2-7-1
2002年度Jolt大奖,慢慢品味吧,我就不啰嗦了。

2.8 Java与模式

我的书没找到,只给个链接吧,

http://book.douban.com/subject/1214074/

这本书其实不合我的口味,太厚太啰嗦,我并不喜欢里面的故事,比如击鼓传花什么的,
如果把此书浓缩到300页左右会更理想。

2.9 Servlet与JSP

2-9-1
如果你要做Java web开发,了解Servlet与JSP的基础知识是必须的,如果你要研究Tomcat,也必须精通Servlet与JSP的相关规范。

2.10 J2EE核心模式

2-10-1
J2EE已变成了JavaEE,Spring把J2EE的水搅混了,以致于Java企业级开发包括国内某大型互联网公司的内部都是Spring的天下,
不过,J2EE的一些设计模式你还是可以了解一下的。

2.11 Java虚拟机

2-11-1
2-11-2
国内有关Java虚拟机的书非常少,
『深入理解Java虚拟机』 是国产的,算是国产书里面相对还可以的技术书籍,内容能跟上时代,
注重实用性,当然,还不够深度,更侧重概念的理解而不是如何实现,
另一本『深入Java虚拟机』 是国外的,可能很难买得到了,内容有点陈旧,
不过,如果你能买得到我还是推荐你读一读,还是有实用价值的,
比如我在2007年研究Java语言编译器(javac)时就从这书上学到了非常多的有用知识,
特别是从第5章开始以后的章节都写得不错。

2.12 Java并发编程

2-12-1
把这本书放到最后,主要还是因为这本书还是有点难度的,并发问题都是高级主题,
比如书中举了一些Servlet的例子,要是你没学过Servlet你就会不太明白例子的用意是什么,
当然还有线程的东西你也需要知道,还有前面Java虚拟机的书里也会提到Java内存模型,
这对于你理解Java多线程并发运行时会有哪些问题也是很有帮助的。

2.13 XXX in Action

有大量以"XXX in Action"命名的书,比如Struts in Action,多是介绍具体框架的,
这类书实战性比较强,因为框架更新换代也很快,过时也比较快,所以你可以从图书馆里借阅,
或者让公司出钱买也行,这样你能省点费用。

2.14 直接去研究Java开源项目的代码吧

看书只是为了打基础,并且是件相对容易的事,想要与他人不同,
你应该去研究你感兴趣的Java开源项目的代码,
看源代码是件需要时间和考验耐性的事,如何看?这是门学问,是另一个重大的话题,我就不在这里多谈了。:)
顺便卖个广告,你可以关注我的GitHub主页 里面有大量开源项目的研究。

至此,有关Java你需要掌握的最核心的东西都列在上面了,行动起来吧,不能只看书,实践也很重要。

3. 扩展你的眼界

虽然上面谈到的计算机基础类的书籍与Java相关书籍已够用了,
不过,我想你不会满足于此吧,多读点书没坏处,技多不压身,
最不济的情况下学点别的至少也能吹吹牛不是。:)

以下是我觉得可以读一读的书,至少已被我过滤了一回,你不用担心它们是垃圾书籍(我确实买了很多垃圾书籍),
当然不需要每本都看,比如像TCP/IP这种,如果你不做OS、不做网络层的开发不读也可以,比如TCP/IP三卷本我多半是买来收藏的。
这些书我就不一一介绍了,全列在这了。
3-1-1
3-1-1

聊聊过去的技术历程

这篇文章的写作背景来自知乎的这个问答
zhh-2015在分布式系统和数据库领域研究水平和工程能力怎样?

快奔四的老码农了,正好在休养期间,写篇长文如实回顾一下,年轻人看了也许会有点帮助吧。
工作、考研、成长、晋升、技术研究、创业、职业病都涉及了。

从2006年讲起,分三个阶段:

  1. 2006年-2010年 独立研究阶段
  2. 2010年5月-2012年7月 在淘宝工作的阶段
  3. 2012年8月至今 独立研究与创业阶段

1. 2006年-2010年 独立研究阶段

2006年以前做过4年的电信移动行业的应用项目,因为厌倦了做业务系统,想寻找方向,并且还攒了点积蓄,所以在2006年年初选择了辞职,走上了考研之路,复习了8个月后又发现读研不是自己想要的,刚好又迷上了编译器和程序语言理论,随后放弃考研,宅在家不工作,走上了兴趣驱动的自学之路,一直持续到2010年5月初。

这4年沉寂的时间阅读了大量技术书籍,下面这篇博文中提到的书名大多数都是在这段时间涉猎的:
想通过自学成为一个有良好计算机专业素养的Java程序员要读哪些书?

书本上的知识自然是满足不了我对技术的饥渴的,因为最开始是痴迷编译器,加上2006年当时的SUN公司把Java语言编译器(Javac)和HotSpot VM都开源了,所以在补完了基本的理论基础后就很兴奋地去研究Javac的源代码了。

2007/2008年Ruby On Rails大火,Java社区感受到了前所未有的恐慌,当年的JavaEye论坛上一大片Java将死的言论,作为Java铁杆粉丝我自然是不服的,为了探索Java全栈web框架的新方向,进而转去研究Tomcat、iBatis甚至包括MySQL/PostgreSQL的JDBC Driver这类开源项目的代码。

经过几年书本和代码的狂轰乱炸,很自然的产生了把编译器用于应用服务器的想法,第一次品尝了我也能做技术创新的甜头,造了第一个纯技术的属于自己的全栈web框架,直到2009年的11月份,在JavaEye论坛首次发布了第一个原型版本并讲了一下心路历程,获得了不小的轰动,见下文:
Without SSH/JSP/Servlet,不走寻常路,Java可以更酷

也是因为在JavaEye论坛上发布的这一篇博文,开始在网上有了点小名气,并且被当时的淘宝员工看到了,问我想不想去淘宝工作,可以帮我内推。2010年经过好几轮面试,在5月初,我正式入职淘宝中间件团队,结束了4年多独自一人宅在家潜心研究技术的生活。
这期间深入研究过的开源项目,包括Javac、Tomcat、iBatis、MySQL/PostgreSQL JDBC Driver、Volocity等等,我都或多或少的加了代码注释、文档以及自己写的测试用例,5年前我把它们都放到GitHub了,一方面怕丢失,另一方面对于有兴趣的人也可以看看。以下是连接:
Open-Source-Research
Tomcat-Research

我造的全栈web框架叫Douyu,名字来源于斗鱼,这是一种小时候很喜欢的生命力很强悍颜色又很艳丽的鱼类,可以从下面这个连接中找到这个框架的源码(里面对Java语言编译器做了定制):
Douyu

2. 2010年5月-2012年7月 在淘宝工作的阶段

2.1 TDDL小组

淘宝中间件团队当时的负责人是华黎,团队以Java开发语言为主,中间件团队有三个最核心的小组,刚好对应三架马车,分别是:
TDDL(Taobao Distributed Data Layer): 俗称"头都大了",TDDL是对MySQL进行读写分离和分库分表的中间件
HSF(High Speed Framework): 被戏称为"好舒服"框架,其实是一个RPC的服务框架
Notify: 消息中间件

我被内推到中间件团队在面试时是不知道具体岗位的,入职后分到了TDDL小组,可能是因为我比较了解iBatis和MySQL JDBC Driver的代码吧,加上我还懂编译器的知识,TDDL也有SQL Parser这些东西,所以综合来看,进TDDL小组比较合适。

进TDDL小组时正好赶上下一个大版本的规划,当时加上Team Leader(TL),有4个核心开发,然后还有一个新人,总共5个人。在没加班的情况下,经过将近4个月的开发,顺利发布。这个大版本如今看来是个里程碑,当时所用的架构和设计至今都没有什么大的变化。

开发完TDDL这个大版本后,团队有个同事离职了,他负责的一个项目没什么进展,然后我跟TDDL小组的一个同事又接管了这个项目,这个项目叫DB-Proxy,想用来解决MySQL的连接数受限问题。我们俩从头开始,用Google的Protocol Buffer设计了新的协议,实现了一个遵循JDBC规范的客户端,还有一个Proxy Server。用了两个月就做完了,不过后来又不用了,可能是因为在MySQL之前挂个Proxy,多了一个中转站,损失了10%-15%性能,所以觉得划不来。淘宝的应用基本上都是用Java开发的,并且并发量很大,对于中间件的要求很高,在网络中多一个中转站就会损失一些性能,为了追求极致的性能,TDDL才被设计成一种部署在客户端的中间件,这样就避免了不必要的网络开销。

另外值得一提的是,TDDL这个产品几年前已经在阿里云上面提供对外服务了,公有云的版本叫DRDS,这个版本因为考虑到外部客户的应用不一定是用Java开发的,如果客户用的是Node.js,那就不能在用Node.js开发的应用中直接使用TDDL的接口了,所以DRDS多了一种可选方案,为了支持多语言客户端,引入TDDL-Server,这跟DB-Proxy的架构差不多,但是要接受一点性能损失。

TDDL之前有开源过一小部分代码,后来不知为何又不开源了,可以从以下链接中找到开源的部分,还能从代码中找到@author yangzhu,这是我在淘宝的花名,这是阿里巴巴的企业文化之一。
TDDL

DRDS在阿里云上面的介绍可以从下面的链接中找到:
DRDS

2.2 Land小组

在TDDL小组工作了半年后,年底我晋升了一级,工资多加了几千,还配了点期权。

前面提过我进中间件团队前对Tomcat的源代码有比较深的研究,又自己造了个叫Douyu的web框架,我进淘宝时是打算看看有没有机会推广我的web框架,再加上TDDL小组的工作已经告一段落了,听说有个小组正在开发一个新的名叫Land的应用服务器,已经有一年了,这个小组比较边缘,刚好也归属在中间件团队,我很顺利地转过去了。

Land小组在我加入之前有三个人,过去才发现Land是个死气沉沉的项目,只能部署简单的后台Spring应用,连基本的web项目都不能部署,也不是什么Servlet容器。当时淘宝的应用服务器还是古老的JBoss(内置Tomcat 6),JBoss是一个完整的遵循J2EE规范的应用服务器,但是淘宝的应用绝大多数都是用Spring开发,所以J2EE中的功能大多数都用不到,只要使用Tomcat/Jetty这类Servlet容器就满足需求了。

分析了当时业务系统的需求后,我们觉得重新开发一个新的应用服务器是没什么必要的,如果想使用Servlet 3.0的新功能,还不如用Tomcat 7替换掉JBoss。

在Land小组期间我们做了一个叫依赖中心的小项目,经过三个月的挣扎后,Land项目宣告失败,Land小组被合并到HSF小组,Land小组的TL转岗了,我们三个人被归到HSF小组。

2.3 HSF小组

来到HSF小组后,因为当时的HSF经过两代人的努力已经相对稳定了,没有什么大的需求,只有一两个人在维护,做些小特性修点bug。这期间,因为集团层面想把一些老旧的基础设施做一些升级,所以JBoss也是要拿来开刀的,要用Tomcat 7替换掉JBoss。然后我就接手了Tomcat 7的工作,一方面是做Tomcat 7与HSF的集成,另一方面得找上百个应用放到Tomcat 7上面跑,看看兼容性如何。

另外我们小组当时还负责Webx框架的维护,Webx是一个类似Spring MVC的框架,是阿里巴巴的创始人之一周悦虹开发的,我去淘宝面试时最后一轮的技术面就是菲青(早期JBoss核心之一)跟他一起的。Webx也要依赖Spring,并且在阿里系的web应用中几乎是处于**地位的。周悦虹大多数时间都在国外,Webx的代码基本上还是他一个人在弄,还好Webx当时已经相当成熟了,我对Webx的贡献不多,只贡献了一个特性,加快Webx的启动速度,我们组还有一个同事大多数时间都在负责Webx的答疑。

虽然我一直想把我的开源项目Douyu也用上,但是Douyu完全不走Servlet那一套技术栈,而Webx是难以撼动的,所以Douyu的后续开发就断了,光有好的Idea不行,得有场景去用去持续开发才可能成功,PlayFramework 跟Douyu的思路类似,还是同一时期出来的,它现在还是有挺多人用的,虽然Douyu失败了,但至少证明我的技术眼光还是不错的。

Tomcat这边几乎都在测试应用的兼容性,应用方也没有动力升级到Tomcat 7,太难推动了,直到毕玄跟多隆那边在做T4容器(虚拟化相关),才把JBoss替换成Tomcat 7这件事重视起来。期间有一件事能看出多隆还是很厉害的,好像是用户登录系统后发生了窜号问题,多隆比我还先发现是因为Tomcat中某处与Cookie相关的代码有bug。一直以为他是C/C++领域的大牛,没想到Java这边排查问题也这么快。
当然了,我也不弱啦,在Tomcat这边有件趣事也能证明我的实力。

事情是这样的,因为折腾Tomcat 7也有半年了,但是应用又不想升,所以当时我就想找找JBoss的安全漏洞吧,还跟HSF小组的TL说,要是有安全漏洞,JBoss又没有补丁,那升级到Tomcat 7就好办了。

因为当时淘宝用的是Apache + JBoss(内置Tomcat 6)的组合,我不擅长C/C++,对Apache的代码也没什么研究,我当时想,要如何通过http client发送一个数据包就能无声无息地通过Apache再对JBoss发起攻击,不但能拿到后端的一些敏感信息还能让JBoss所在的服务器CPU陷入循环达到类似DoS 的效果。Apache和JBoss之间走的是AJP协议,而http client跟Apache之间又是http协议,所以只能通过发送表面上正常的http协议包通过Apache后再用AJP协议转给JBoss。只要AJP协议有漏洞,那么就能通过精心伪造的数据包发起对JBoss的攻击,按这个思路,接下来两天(还是几天我忘了)把重心放到JBoss端的AJP协议实现上,没想到轻松地搞了个大漏洞,因为JBoss与AJP这一块本身就是Tomcat 6的代码,所以自然Tomcat也中枪了,而且是全部系列版本。只可惜的是,哪怕我这么努力了,在我辞职之前也没有看到应用从JBoss升级到Tomcat 7。

以下是寻找这个安全漏洞的最好证明,Reported: 2011-08-20 07:54 UTC by zhh
2011年就是我在HSF小组的那一年。
ajp CPing/Forward-Request packet forgery, is a design decision? or a security vulnerability?
CVE - CVE-2011-3190

2.4 HBase、TLog

从Land小组到HSF小组,直到2011年11月份,差不多整个2011年都像是打杂的。

从11月份开始,我们都在找活干找创新点,从HSF框架的调用日志开始思考,毕竟HSF是个RPC框架,如果能把每个API的调用通过抓取日志后做一些存储分析应该是件挺好玩的事,但是API调用日志的量太大了,全网每天都是千亿级别,肯定不适合存到MySQL。刚好当时NoSQL运动正是热火朝天的时候,加上Facebook成功把HBase应用到了自己的Message系统中,已经有人先吃了螃蟹,HSF框架的创始人毕玄虽然不在HSF小组了,但是他把HBase带到了阿里,并且历史订单查询库已经用上了HBase,数据平台部门还有个Hadoop/HBase团队,所以我们也要尝试用HBase来存HSF的调用日志。

当时HBase团队在维护HBase 0.90/0.92这两个版本,社区正在开发的是0.94,经过三周的调研,发现HBase 0.90/0.92无法满足我们的需求,而正在开发的0.94可以满足,然后我们不等发布就大胆使用还处在trunk中的代码了,当时工作之余我都在读0.94的代码,HBase出任何问题都算是我的。我们基于HBase构建了一个TLog系统,包括采集存储分析,经过三个月的努力成功上线了一个HBase 0.94的集群,TLog是这个集群的第一个应用。

因为开始做HBase和TLog已经是快年底的事了,所以2011年实际上没做出什么成绩,晋升自然也无望。阿里就是这样的,成果导向,技术再好,没做出成绩也没机会晋升。所以,要么跟对团队,要么自己跟团队努力寻找新方向。

TLog这个系统还包含了一个叫EagleEye 的子系统,经过小组其他人的努力,现在也在阿里云上面作为HSF一个功能模块提供对外的服务了,见下面的链接:
HSF

题外话: 我辞职后,某一年在网上听说HSF的小伙伴们给TLog这个系统引入了Storm,用于实时的流计算,然后似乎不太喜欢Storm所用的Clojure语言,不好定制Storm,最后用Java重写了一遍,弄出了一个JStorm。

2.5 辞职

2012年上半年,跟HBase团队一起推广普及HBase,从HBase 0.94集群上线以来,除了TLog之外,陆续有10来个新应用部署过来了,然后我们想扩大应用范围,也有业务团队的系统想接入,剩下的工作大多都是开会定日志格式之类的扯皮事情了。我对这些事情不是很感兴趣,辞职前的最后一两个月有点消极了,感觉在中间件团队没太多挑战了,去其他两个团队聊过也提不起兴趣,加上人在杭州家在桂林,30岁了,总有种漂泊的感觉,就想辞职回去了,于是在2012年7月底正式辞职。

中间件团队还是非常不错的,我印象中从没加过班,因为房子租在古荡湾新村,离公司很近,下班回去也无聊,所以常常晚上在公司呆到11点才回去,公司还有免费的夜宵吃,周末也常常在公司,同样有免费的盒饭。

在这里要很感谢我们的老大华黎,是他当初抗住HR的压力让我这种奇葩进了中间件团队,老大人很好,没有架子。还得感谢在JavaEye上发现我,并推荐我去淘宝工作的前同事伯岩,让我有了一段值得一提的工作体验。最后还得感谢我的几任TL,我确实不是那种很温顺的人,过去的一些小的不愉快都请忘记吧,对于沈询我特别抱歉,不应该在辞职后因为HBase的问题在微博上闹翻。还有各小组的同事们,都是我工作中遇到过的最和谐的小伙伴。

3. 2012年8月至今 独立研究与创业阶段

在淘宝工作两年半不到,攒了25万块钱,回到桂林后也不想工作,还是想像以前一样按自己的兴趣做点技术研究。因为HBase在阿里的应用很成功,大数据和NoSQL正是大热门,有阿里背书,HBase在国内就慢慢普及了。考虑到HBase的API相比SQL还是太难用了,当时我就产生了为HBase套一个SQL引擎的想法,因为我想把这样的SQL引擎内嵌到HBase中,而HBase又是用Java开发的,我又不想从头写一个SQL引擎,所以就找到了H2数据库。

从阅读H2数据库的源代码到把它改成一个分布式SQL引擎放到HBase上面跑花了半年时间,就这样做出了属于自己的第二个开源项目,给她起了个名字叫Lealone,灵感来自于在淘宝中间件团队工作时办公桌上那些叫绿萝的室内植物,一直想做个项目以它命名。

基于H2和HBase做出的第一代Lealone我不满意,然后2013年转去研究Cassandra了,参考Cassandra的架构对Lealone做了大量改造,第二代Lealone就是这么产生的。不过最后还是遇到了瓶颈,一时找不到好的突破点,于是在2014年转去研究HotSpot VM的代码了,跟数据库无关,只想从HotSpot VM层面做一些代码分析的工作,因为我读过很多Java开源项目的代码,总想找到一种能快速读懂大中型开源项目代码的有效途径,差不多整个2014年都是在苦哈哈的研究HotSpot VM的代码,因为实在是太难读了,一边读一边试着改,验证一些想法,虽然很苦,但这一年也没拿到什么创新成果。

2015年又回到数据库领域,继续探索下一代分布式关系数据库应该是什么样子,逃出Cassandra的固有思维,花了一年第三代Lealone就出来了,逐渐变成自己想要的产品,所以就决定在2016年创业了。
Lealone的详细演变过程可以看这篇文章:
Lealone过去现在将来
H2数据库的源代码研究在这里:
H2-Research
Cassandra这个NoSQL的源代码研究在这里:
Cassandra-Research
下面这个是HotSpot VM的源代码研究:
OpenJDK-Research

2016年创业第一年还是比较顺的,一边做数据库产品,一边做电信移动行业的大数据项目,因为不找风投,所以做点高质量的应用项目还是可以的,能赚点钱,也能积累客户,对将来推广产品有帮助。

不幸的是第一年工作太拼了,加上年纪大了,又长年坐在电脑前工作10几个小时,到了2017年年初,身体的毛病就出来了,颈椎和腰间盘都不好,特别是腰间盘,2017/2018年各有一半时间长期受腰间盘问题困扰,都没办法舒服坐在电脑前工作,基本上都处在休养状态,直到发这篇文章的现在都没法像以前一样高强度工作了,数据库产品开发进度缓慢。

现在只要身体好转了就会写写代码,没钱用了,就接点有价值的项目做做,有时也义务或有偿地做做技术顾问。

Lealone这个数据库会一直做下去的,因为除了她,也想不到什么更有挑战更感兴趣的技术方向了。
不是为了钱,只是不想生活太无聊。

就写到这吧,以下是Lealone的项目主页,90%的代码都开源了。
Lealone
以下是近期实现的或正在实现或构思的想法:
日常各类技术想法分享
统一批处理、流计算与 OLTP 数据库

1

1

建议搭个博客

大佬为何不做个以Github Pages的博客?Jekyll或者Hexo主题的都可以呀。

统一批处理、流计算与 OLTP 数据库

最近在研究 Flink 开源项目,这是个好东西,准备进一步阅读源代码,看看能带来多少灵感。

也许在不远的将来,数据处理这个大领域真能迎来大一统。

先发个帖占个坑,想说什么时再来补充。😜

Kotlin 吐槽大会

因为不满意 JavaScript 以及相关工具链,所以想换个 JVM 系的语言写前端,
选来选去选了 Kotlin,最初期望很大,试用了几个月后挺失望的,憋得难受,不吐不快。

一开始想用 Kotlin 写个能在前端跑的 html parser,因为已经有一个用 Java 开发的 jsoup了,所以就不想从头造了,反正 Java 代码能转成 Kotlin 代码,正好可以从一个现有成熟 Java 项目的角度来审视 Kotlin 。

每个回贴说一个槽点。

我在<<高级语言虚拟机:HLLVM>>群组中发的技术贴

re 关系模型的实质

有人提起王同学这篇老文<<关系模型的实质>>跟原来的版本相比已经删掉很多内容了。
文章随便看看就好了,这不是他的研究方向。有很多值得提的,随便捡几个:

  1. 关系数据模型不是数据结构,而是数据结构 + 数据操作 + 数据上的约束
    想通过C语言或Java语言来描述约束不是不可以,但没有SQL简洁,
    比如可以用Java在类的字段上加注解,然后标明它是否可以为null,长度是多少之类的。
    用关系代数或SQL来描述数据操作而不用程序语言就是为了简单,
    让数据库实现者优化起来更容易。
    现在很多数据库也都支持直接用Java和JavaScript来写存储过程和自定义函数的。

  2. 变长记录这种问题取决于具体的数据库实现,但凡先进点的数据库都是支持变长记录的,而且在修改后也可以很容易的做到不会产生Page Overflow,
    我在Lealone中用了Copy On Write B-Tree就轻松搞定了这类问题。
    我以前提过不同数据模型的关系:
    5种主流的数据模型,从复杂到简单,从松散到规整,排列成这样: 图 -> 文档 -> 列族(或聚簇) -> 关系 -> 键值。键值模型如果在乎值的内容,切开来就变成关系模型中的域(字段),把多个域分组为一个族,关系模型就变成列族(或聚簇) ,允许任意层次的聚簇就变成树状的多文档嵌套,文档之间有联系就变成图。
    看似复杂,其实只不过是一个数据编解码的过程。

  3. NoSQL的出现不是因为SQL表达能力太弱,而是因为它过于强大了,
    有些功能在分布式场景下不好实现,比如join,
    只能通过大量冗余把相关的数据聚在一起,这样在更新时不用考虑分布式事务,
    查询时不需要跨多个节点传输数据。
    MongoDB和Cassandra、HBase这类数据库都是不能很好的支持join的,
    都是通过冗余来避免上面所说的两个问题。
    用JSON来描述CRUD其实是没有SQL简洁的,MongoDB的官方文档上有对比,
    JSON的出现更多的是满足那些不想学SQL的码农的需求。

  4. HBase、Cassandra都不是列存储,前者其实是列族存储,后者是行存储,
    NoSQL不会朝简单的RPC方向发展的,而是SQL化,比如Cassandra的CQL就是类SQL的语言,HBase已经有了挺多的SQL引擎,比如Apache Phoenix
    RDBMS和NoSQL都会朝NewSQL的方向走。

事务隔离级别: REPEATABLE_READ 的瑕疵

REPEATABLE_READ 这个事务隔离级别的含义是顾名思义的,简单说就是在同一个事务中对同一条记录的两次读取应该都能读到相同的记录,哪怕在这两次读取记录的过程中另一个事务更新了这条记录也不会受影响。

以 MySQL 5.7 举例说明,MySQL 5.7 的默认事务隔离级别就是 REPEATABLE_READ

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set (0.00 sec)

1. 通过打开两个 MySQL Monitor 来演示正常情况下的 REPEATABLE_READ

先在 MySQL Monitor A 中创建一张测试表并增加一条测试记录:

mysql> create table tx_isolation_test (pk int primary key, count int);
Query OK, 0 rows affected (0.17 sec)

mysql> insert into tx_isolation_test(pk, count) values(10, 100);
Query OK, 1 row affected (0.00 sec)

然后紧接着在 MySQL Monitor A 中启动事务 T1,先执行下面的 SQL 读取记录的 count 字段的当前值:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|   100 |
+-------+
1 row in set (0.00 sec)

从查询结果可以看出 count 字段的当前值是 100。

接着在 MySQL Monitor B 窗口中启动另一个事务 T2,执行下面的 SQL 更新 count 字段的值:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update tx_isolation_test set count=count-10 where pk=10;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|    90 |
+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

从上面的结果可以看出,当事务 T2 提交后,count 字段的值已经是 90 了。

最后切换到 MySQL Monitor A 窗口,再查一下 count 字段的值是多少:

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|   100 |
+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

从结果看出,count 字段的值还是 100,说明在事务 T1 中执行的两次读取操作不受事务 T2 的影响,这满足了 REPEATABLE_READ 事务隔离级别的语义。

2. 再来看看特殊情况下 REPEATABLE_READ 有什么瑕疵

先在 MySQL Monitor A 窗口中启动事务 T3,执行下面的 SQL 看看 count 字段的当前值:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|    90 |
+-------+
1 row in set (0.00 sec)

结果表明前面的事务 T2 确实成功提交了,count 字段的值已经是 90 了,

然后再切换到 MySQL Monitor B 窗口启动事务 T4,执行跟事务 T2 一样的操作:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update tx_isolation_test set count=count-10 where pk=10;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|    80 |
+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

当事务 T4 提交后,count 字段的值变成 80 了。

然后再切回 MySQL Monitor A 窗口,在事务 T3 中看看 count 字段的值是多少:

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|    90 |
+-------+
1 row in set (0.00 sec)

因为事务 T3 要遵循 REPEATABLE_READ 的语义,所以此时 count 字段的值显然是 90,
接下来,试着在事务 T3 中对 count 字段减 20,第一直觉是 count 字段会变成 70 吧(90-20=70)……

mysql> update tx_isolation_test set count=count-20 where pk=10;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

直觉也许不对呢,查查看:

mysql> select count from tx_isolation_test where pk=10;
+-------+
| count |
+-------+
|    60 |
+-------+
1 row in set (0.00 sec)

怎么会是 60?为什么不是 70 ?
如果是 70 会出什么问题?

如果执行完事务 T3 再执行 T4,在事务 T4 中把 count 字段减去 10,而 T3 把 count 字段减去 20,最后结果确实应该是 60。

但是这里事务 T3 和 T4 并不是串行执行的,而是在事务 T3 执行的过程中执行了 T4,并且是在 REPEATABLE_READ 这样的事务隔离级别下执行的,如果在 T3 中完全遵循 REPEATABLE_READ 的语义,那么 count 字段的值应该是 70 才对,但是这样会导致更新丢失的问题,事务 T4 的更新被丢弃了。

所以,宁愿不产生更新丢失的问题也要违反 REPEATABLE_READ 的语义。

在事务的 ACID 语义中,I 就是代表事务的隔离级别,而 REPEATABLE_READ 只是其中的一种隔离级别,
更新丢失违反了 ACID 中的 D,在 I 的语义无法满足并且又不会产生致命影响时优先满足 ACD。

如果想严格遵循 REPEATABLE_READ 的语义,又不会产生更新丢失的问题,那么可以在事务 T3 的查询语句中加上 for update,这样会锁住记录,直到事务 T3 提交后,事务 T4 才能继续更新。

数据库领域总会碰到这样的不完美,遇到特殊场景时就会发明一些新的 SQL 语法或配置参数去弥补。

再见 2018,你好 2019

又到了每年的最后一天,以前很少写年度总结,去年写了篇流水账 再见 2017,你好 2018
再次读起来却也有点意思,以后每年年末尽可能都写一写,标题的风格都想好了: 再见 xxxx,你好 yyyy

去年许下的愿望是: 不再接任何项目、身体健康、把数据库产品做完,惭愧的是只有第一个愿望达成了。

情感

今年前3个月谈了一次短暂的痛苦的恋爱,现在想来也算不上什么恋爱,毕竟是痛苦更多。
分分合合好几次,两个人若不合适,如果分了一次,就可能有第二次第三次,所以早分早好,不必迁就。

勉强在一起,不但影响心情还影响工作,最后可能还变成路人。

这段短命的感情让我总结出了两条经验:

1: 如果跟某位异性朋友认识了很多年,虽然彼此都单身,如果过去那么多年也没走在一起,那就别抱幻想;
2: 如果对方过去有一段很深刻的感情,如果你跟TA之间的情感远没有前段那么浓烈,能放弃就放弃。

生活

3月份分手后,4月份在调节心情,5月份进入工作狂模式,6月18号早上起来又出现右脚背放射痛了,又像去年一样。下半年就跟个废人差不多了,无法正常工作,一直到12月1号都没什么好转,从12月2号开始决定彻底放弃工作,电脑都很少开了,每天要么躺在床上,要么背靠床头,看看书看看电视剧和电影。

不碰电脑了,这一个月反而好转了不少,去年也是痛了大半年后离开电脑骑了一个多月的自行车才好,今年发现骑自行车没用了,所以才试试别的办法,那就干脆卧床休息吧。吃药也没啥用,更不想去动手术,因为并没有严重到走不了路,就是无法长时间坐在电脑前,连续坐一小时都不行。

总之,2017/2018年生活质量都挺差的,也算是中年程序员的职业病了,都是自己作的。以后为了身体健康,再也不敢高强度的工作了,还是要劳逸结合。

工作

整个2018年的有效工作时间可能累计都不到3个月,不过还是做了不少事。

5月份时阅读了Ebean这个ORM框架的一些文档和代码,获得了一些灵感,也为我的Lealone数据库造了一个小巧的专用的ORM/DSL框架,还学了不少前端技术,把这个ORM/DSL框架移植到了前端,目前都还没有做完整,只是把常用的功能做了。

下半年有一个多月身体刚有一些好转,然后又拼命工作,把Lealone-Plugins项目更新了不少内容,包括支持第三方的网络应用框架、存储引擎,也开始支持MySQL协议和基本的SQL语法了。Lealone-Plugins项目下面的子模块也都没有达到生产级别,只是把基本的框架和最基础的功能做了,后续如果有人有兴趣可以接着完善。

Lealone数据库的代码也做了不少重构,技术想法分享这篇文章的前5个构想也基本都做完了,第6个是目前正在做的。

明年的工作任务更多的是增强稳定性了,功能已经做了很多了,做稳定后再发布。

家庭

今年除了我的健康问题之外,家人都平平安安,这点倒让我挺满意。有一个堂弟12月1号也结婚了,我还回去吃了喜酒,去年他还老说找不到老婆,没想到这么快就结婚了。就差我这个老大哥拖后腿了。

投资

今年美股太不稳定了,手里那点阿里的股票也跌掉40%了,不过一直没卖,就放在那吧,其他美股也没买。

原本想在乡下再租10几亩地种果树的,结果人家想再等等行情,又没下文了。

今年我拒绝了所有的项目邀约,所以项目都放到我的合伙人新开的公司做了,因为我的身体原因加上也没精力和时间,所以帮不了什么忙,他那边虽然有好几个项目,但都做得马马虎虎,几个潜在的大数据项目也没拿下来。

2019

到了我这年纪,物质追求太低了,所以还是老样子,希望家人朋友包括我自己都身体健康,能在2019年顺利发布产品就是我的最大愿望了,有余力也会管管项目带带技术团队,然后多写点技术文章。

再见 2020,你好 2021

这是第四年在 GitHub 写年度总结了,每年固定在12月31号这一天专门准备几个小时用于写总结。

前言

2020年很不平常,因为疫情影响了很多人,也是我20年来,春节期间待在乡下最长的一段时间了,足足待了两个月。一大家人住进了新起的房子里,本来是我家请客的,因为疫情也取消了,买了四桌的菜,6个大人加3个小孩吃了半个月,难得有机会跟家人相处这么久,也胖了10斤。

二月和三月,网上一片悲观的情绪,我倒是挺乐观的,因为看到乡下的生活水平在不断改善,不能因为短期的困难就否定一切。心态积极向上一点,别那么悲观,也许就会有好运呢,2020年对于我个人而言倒是挺顺利的。

工作

因为前两个月在乡下闲着,今年工作从四月份才算正式开始。先是主动放弃了一个准备了半个多月的应用项目,虽然项目金额是做过的应用项目中最高的,但是这其中的关系太复杂,利益分配谈不拢,加上项目时间太短,不得不放弃了。

4-6月份还是专注于 Lealone 这个产品的完善。7月初合伙人在南宁接到一个项目,想让我过去一起做做,因为去年把积蓄花得只剩两万块的生活费了,为了赚点零花钱就答应做项目的技术顾问,做之前说好了每天花两三小时。不过,最后发现还是大意了,签合同前没有看到项目的代码,拿到代码后才发现是个巨坑,系统跑起来慢,没有多少有用的文档,连个完整的建表脚本都没有,得边读代码边点系统界面去了解业务流程,加班加点做了两个月才做完一块业务。为了不拖累自己的产品进度,不得不退出这个项目了,直到最近才听说这个项目快结束了。

9月中旬又开始回归到 Lealone 的开发工作上,主要聚焦在数据库和微服务一体化方向,对于改进应用开发效率有很大帮助。目前已经把一个老项目的 web 层和服务层用 Lealone 改造完了,最近一周还基于 Vue 和 Lealone 做了一个样板应用 PetStore ,后续还有一连串的应用项目准备用 Lealone 开发或改造。总之,Lealone 的商业应用从今年12月份正式开始了。

值得一提的是,今年下半年还开启了另一个产品方向:做一个在线开源代码学习网站。目前第一批开源项目有这6个:tomcat/spring、netty/vert.x、mybatis/h2,这些开源项目的代码在过去十年或多或少都看过,这一次阅读整理更有针对性了。网站目前还没上线,明年会花很多时间专注在这个网站的研发和运营上,这个网站的后端技术栈也一定会用 Lealone 去做。

家庭与生活

今年工作比较繁忙,除了下班后时不时散步3-5公里基本没做什么锻炼了,不过身体倒是没出什么毛病,看来半躺在床上工作还是很有效果的,腰椎和颈椎不像以前那样老出毛病了,坐在电脑桌前反而容易出问题。

家人今年也很平静,没什么要我操心的事了,看来去年把房子起好,支持兄弟姐妹把房子买了,确实了却了人生中的一件大事,安安静静过日子了。

有两个亲戚又因为癌症离开了,在乡下,50岁以后的人得癌症算是很常见了。没有什么好办法,上一辈的人就是这么苦过来的,平时也没想着去体检,小病小痛能忍也不会上医院,真检查出是晚期就听天由命了。老年人有些顽疾已经无法根治,如今只能尽量对父母好一点了。

投资

这一年从3月19号正式炒股了,只玩港股和美股。运气真的太好了,长中短期都炒过,绝大多数都赚了,可惜只能看着数字变动,取不出来不能用。最重量级的决策就是把持有了6年的阿里原始股都卖完了,全部重仓到蔚来汽车这只大牛股身上,虽然最高涨了3倍,不过都没卖,希望2021年蔚来汽车能再涨两倍吧,哈哈哈。

炒股现在已经是我最重要的一个副业了,随着帐面数字越涨越高,就算是副业也得重视了,至少不能只靠运气,得去了解人家公司的各种信息,还有相关行业的资讯也得保持关注,尽量把握大方向和行业发展趋势。

情感

这一块今年绝对是压轴戏了,总算认认真真谈恋爱了,第9个月了,目前还在热恋中。

女朋友比我小10+岁,目前是在读的医学硕士研究生。医学也是我很向往的一个专业领域,可惜因为这门学科不像计算机那样适合自学,哪怕早几年都买了一批医学教科书了,依然没有入门。所以看着女朋友在研究方向上有困惑也无能为力,等我不那么忙了,也许还可以把房间角落里堆成灰的课本拿出来读读吧。

情感这一块其实有很多想说的,但是我女朋友平时也看我微博,这篇年度总结也一定看得到,所以就“不敢多说了”,求生欲满分,哈哈哈。

2021

2021年只有一个目标: 赚钱、赚钱、赚更多钱。

有钱了才能跟女朋友到处玩,随便吃随便买,有钱会更快乐。今年我真的没钱,我好多吃的用的穿的东西都是女朋友给我买的,感觉就像被富婆包养了一样,哈哈哈,明年不能再像这样了,虽然感觉很幸福,但是赚不了大钱真的对不起我这身本领嘛。人还是要实际一点,不能为了追求技术而不食人间烟火,把自己的技能转换成钱,至少不是件坏事,何乐而不为呢。

最后,希望家人在新的一年平平安安、健健康康。

祝所有的同事、朋友、同学、还有各位网友都能过上自己想要的生活!

再见 2021,你好 2022

2021年的关键词: 缺钱

缺钱

这是从2002年正式工作以来最缺钱混得最惨的一年了,仅次于2001年。
去年帮朋友的忙参与了一个外包项目,加班做了两三个月,只赚了点辛苦费,还影响了数据库产品的研发进度,所以今年决定什么外包项目都不接,什么忙都不帮了,专心搞产品研发,工作收入为零。

原以为股票的钱在上半年能转点回来,结果一直到12月1号才转回来一笔钱,虽然晚了点,也是雪中送炭了,否则年都过不下去了。

前11个月一直很缺钱,所以从支付宝里借了3万块钱,用了10年的笔记本电脑老化了,用借来的钱花了4000多换了一台新的,还有一万给了家人。因为缺钱,测试用的电脑也买不起,所以严重影响了今年的研发计划,网站也没有钱上线运营。

工作

2021年在 github 上面提交了716次代码或文档,其中 Lealone 提交了418次,发了5个 RC 版。 ​​​​

大的功能主要有4块:

  1. 重新实现前端的 RPC 框架 Lealone-JS,调用后端服务更方便了,还尝试定制了一下 vue 框架,在前端调用服务更加动态化、智能化,这些功能都是实验性的;

  2. 数据库复制协议迭代到了一个新的阶段,改用轻量级锁和客户端冲突检测技术来实现,绝大多数操作都能在一个 RTT 内完成,延迟进一步降低;

  3. 引入新的可暂停的渐进式 SQL 引擎,用一套代码把 OLTP 和 OLAP 优雅地整合在一起了,中间也做了不少失败的尝试,包括重新在 apache calcite 和 apache drill 上面改出一个简化版,连 presto 都拿来改过,事实证明强行把现有的 OLAP 引擎跟 OLTP 引擎的代码整合在一起不是个好选择,代码会变得很复杂,难以维护;

  4. 新建了 Lealone-xSQL 项目,正式兼容 MySQL 和 PostgreSQL 的协议和 SQL 语法,现在可以用 MySQL 和 PostgreSQL 的客户端访问 Lealone 数据库了。

因为缺钱缺人,今年比较遗憾的是 Lealone 这个数据库产品还没有做到 GA 级别,开源代码学习网站严重依赖 Lealone,所以也无法上线运营。

现在股票赚的钱可以持续转一些回来了,也开始花钱买一些测试用的电脑,性能压测已经持续做了一段时间,明年得抓紧时间把 Lealone 做稳定。

明年要开始花一部分时间研究 GraalVM 的代码了,涉及多语言架构和代码分析相关。要借助 GraalVM 在 Lealone 中跑 js、go 等多种语言,还要借助 GraalVM 做深层次的代码分析,最终是要在多年后能实现半自动化编程的长远目标。

2022年还会成立一家新公司专门做开源软件的周边产品,开源代码学习网站也会放到新公司做,新公司会在合适的时机去找风投,现在的公司专注于数据库和微服务框架。

投资

受国内大环境影响,今年的股票投资收益远没有去年好。港股买了5只,美股买了8只。

港股玩得比较小,基本上都是玩短线,虽然累计收益有30+%,也只不过赚了三四万港币而已。

无论港股还是美股,今年还是适合玩短线,我今年的短线交易都赚钱。

长线就比较吃亏,美股的重头戏主要放在蔚来汽车、B 站、confluent 这三只股票。

1-2月份时蔚来的投资收益最高达到4倍多,但是没有套现,放了半年不管不顾,直到7月份才清仓,回调了30%,少赚了100多万。若是不清仓按现价算回调超过50%。

B 站虽然上半年赚了10多万,下半年重仓200多万时受监管影响损失惨重,所以今年在 B 站上的投资亏损50万。还好及时止损,放到现在亏损将近60%。

confluent 的投资比较成功,一开始投几十万玩短线也小赚了一点,后续重仓160万最高收益达到60+%,把 B 站上的亏损补回来了,目前已经卖掉差不多一半了。

总之,今年的股票投资没赚多少,比较深刻的教训有两条:

  1. 还是得花时间关注股票相关资讯,不管不顾容易亏钱或错失良机;
  2. 处在灰色地带容易被政策影响的股票要格外小心,一旦监管这把刀砍下来时要及时止损。

家人

今年家人还算平安,唯一一件不那么顺的事也是跟大环境有关,家人两年前买了恒大的精装修房子,原计划12月交房,结果因为恒大今年出了一堆事,停工了一段时间,还好年底又陆续开工了。股票的钱转回来后,我马上给家人转了几万,也是希望不要被恒大的事影响,能在春节后顺利搬到新房即可。

女朋友

跟女朋友交往两年了,今年也因为钱的问题偶尔跟我抱怨过,国庆假期也没有钱去玩,好在女朋友还是能与我共度这段穷日子,我也常跟她说这是考验,要先苦后甜,能过苦日子以后的好日子才更长久。

虽然没钱,但我依然乐观,并没有焦虑,困难只是暂时的。因为我知道2021年跟2001年的境况是完全不同的,2001年我刚毕业,没钱还找不到工作,那时能力也不太行。20年后的我,虽然暂时没钱,并不是我找不到工作,而是为了专心做好产品不想花时间去做不相关的事,只是股票的钱转回来晚了一些。

我给女朋友说了很多原因后,她还是能理解的,所以并没有离我而去。
谈恋爱还是要多沟通,只要双方都是讲道理的人就能和谐相处。

2022

2022年应该不会像2021年那么惨了,我把股票投资当成一个收入来源了,目前创业要靠股票投资赚来的钱去支撑。

公司要争取在2022年通过数据库产品和网站运营赚一点钱。

希望2022年一切顺利,祝所有人都顺顺利利!

可以开始初步像使用MySQL一样使用Lealone了

因为很多年以前已经做过MySQL的商业级别的分库分表中间件了,所以后来做新的NewSQL数据库时就想换一种玩法。

加上不喜欢用C++写代码更偏爱java,同时又不想从零开始一行行堆代码,最后选择了H2数据库作为研究和实验的对象。

Lealone就是这六年时间内从H2陆陆续续演化来的,现在两者的差异远大于共性,就剩下SQL语法比较像了。

目前单机关系数据库MySQL才是主流,用户已经养成难以改变的使用习惯了,为了兼容MySQL至少得实现相同的client到server的网络协议,然后兼容尽可能多的SQL语法,不基于MySQL的代码是很难很难做到100%兼容的。

就算兼容绝大多数常用的功能也是个体力活,而且使用落后的MySQL协议也有很多限制,比如不能在一个TCP连接中管理多个session,客户端就必须延用古老的连接池技术,原有的MySQL驱动也还是用传统的同步模型。更别说做一个smart client了。

虽然兼容MySQL有种种缺点,但考虑到用户习惯,同时从推广角度想一想,还是可以做一做的。

Lealone通过插件的方式兼容MySQL,目前实现了基本的协议,协议的实现代码是从cobar项目剥离改造来的,SQL parser是手写的,因为Lealone的语法跟MySQL挺像的,所以后续只需要把差异性补上就可以了。
这里是具体代码

PostgreSQL的协议从H2那里很早就有初步的支持了,后续有强烈的需求再逐步完善,包括语法支持。

一步一步教你如何创建第一个Vert.x Web应用

Vert.x是一个支持多语言的运行在JVM之上的开发平台,可以用于构建各种应用,包括web应用,这篇文章教你如何使用Maven和Vert.x创建第一个web应用。

0. 需要JDK 8

http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

1. 安装Maven

如果已经安装了,可以跳过这一步

下载Maven: http://archive.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip
下文我都以Windows 7为例,如果使用Linux,需要修改一下相应的目录名,
把apache-maven-3.3.9-bin.zip解压到D:\apache-maven-3.3.9目录(也可以选择其他目录),
然后把D:\apache-maven-3.3.9\bin加入Path环境变量,
打开一个命令行窗口,输入mvn -version,能看到类似下面的信息:
Apache Maven 3.3.9

2. 创建一个空的Maven项目

mvn archetype:generate -DgroupId=my.test -DartifactId=vertx_app -Dversion=1.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

如果在D:\目录下运行上面的命令,会生成一个D:\vertx_app目录,
打开D:\vertx_app\pom.xml文件,用下面的内容替换掉:
(用Vert.x开发最简单的Web应用只需要依赖vertx-web,测试Vert.x应用需要用到vertx-unit)

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>my.test</groupId>
  <artifactId>vertx_app</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>vertx_app</name>
  <url>http://maven.apache.org</url>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-web</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-unit</artifactId>
            <version>3.2.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <manifestEntries>
                                        <Main-Class>io.vertx.core.Starter</Main-Class>
                                        <Main-Verticle>my.test.HelloWorldVerticle</Main-Verticle>
                                    </manifestEntries>
                                </transformer>
                            </transformers>
                            <artifactSet />
                            <outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

3. Hello World

文件位置: vertx_app\src\main\java\my\test\HelloWorldVerticle.java

package my.test;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;

public class HelloWorldVerticle extends AbstractVerticle {
    @Override
    public void start() {
        HttpServer server = vertx.createHttpServer();
        Router router = Router.router(vertx);

        // 处理http://localhost:8080/
        Route route = router.route("/");
        route.handler(routingContext -> {
            routingContext.response().end("Hello World!");
        });

        // 处理http://localhost:8080/date
        route = router.route("/date");
        route.handler(routingContext -> {
            HttpServerResponse response = routingContext.response();
            response.putHeader("content-type", "text/plain");
            response.end("date: " + new java.util.Date());
        });

        server.requestHandler(router::accept).listen(8080);
    }
}

4. 测试类

文件位置:vertx_app\src\test\java\my\HelloWorldVerticleTest.java

package my.test;

import io.vertx.core.Vertx;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(VertxUnitRunner.class)
public class HelloWorldVerticleTest {

    private Vertx vertx;

    @Before
    public void setUp(TestContext context) {
        vertx = Vertx.vertx();
        vertx.deployVerticle(HelloWorldVerticle.class.getName(), context.asyncAssertSuccess());
    }

    @After
    public void tearDown(TestContext context) {
        vertx.close(context.asyncAssertSuccess());
    }

    @Test
    public void testHelloWorldVerticle(TestContext context) {
        final Async async = context.async();

        vertx.createHttpClient().getNow(8080, "localhost", "/", response -> {
            response.handler(body -> {
                context.assertTrue(body.toString().contains("Hello"));
                async.complete();
            });
        });

        vertx.createHttpClient().getNow(8080, "localhost", "/date", response -> {
            response.handler(body -> {
                context.assertTrue(body.toString().contains("date"));
                async.complete();
            });
        });
    }
}

5. 使用Maven测试

mvn test

6. 使用Maven打包

mvn clean package -Dmaven.test.skip=true

7. 运行

java -jar target/vertx_app-1.0-SNAPSHOT-fat.jar
如果要停止请按ctrl + c

8. 在浏览器中查看结果

http://localhost:8080/
http://localhost:8080/date

9. 参考文档:

Vert.x-Web Manual
Introduction to Vert.x

开发Lealone数据库的过程中看过的书和论文以及深入研究过代码的开源项目

数据库的书以下面两本为主:

数据库系统基础教程
数据库系统实现

其他的参考书:

数据库系统概念
事务处理:概念与技术
数据库与事务处理
SQL与关系数据库理论
PostgreSQL 数据库内核分析
MySQL技术内幕 InnoDB存储引擎
MySQL内核:InnoDB存储引擎 卷1
MySQL核心内幕

RDBMS和NoSQL开源项目

H2关系数据库源代码研究
Cassandra源代码研究
【HBase 0.94源代码研究 (已删除)】
WiredTiger存储引擎源代码研究】TODO

数据库相关的论文

电脑上能找到的,有下面这些: (TODO: 链接地址没有)

│  A Distributed Index for Features in Sensor Networks-eScholarship UC item 10k610k2
│  A Lock-Free B+tree
│  A Relational Model of Data for Large Shared Data Banks-E.F.Codd
│  amazon-dynamo-sosp2007
│  An Architecture for Fast and General Data Processing
│  Architecture of a Database System-fntdb07
│  BloomFilterSurvey
│  Cache-Oblivious B-Trees
│  calvin-sigmod12
│  cassandra-dht
│  CIF Column-Oriented Storage Techniques for MapReduce
│  Citrusleaf- A Real-Time NoSQL DB which Preserves ACID vldb2011
│  Dremel_melnik_VLDB10
│  Fractal Tree Indexes Theory and Practice-20130514
│  Fractal-tree indexes-BenderFaFi07
│  G-Store-HBase-socc10-das
│  google f1 38125
│  Google Spanner (中文版)
│  google系列论文
│  gray-lock-granularity
│  HBase二级索引
│  High Performance Dynamic Lock-Free Hash Tables-spaa-2002
│  HyperDex-2013-02-25
│  HyperDex
│  Immutability Changes Everything-CIDR15_Paper16
│  Integrating Compression and Execution in Column-Oriented Database Systems-abadisigmod06
│  lamport-Time Clocks and the Ordering of Events in a Distributed System
│  LampsonSturgis_Crash recovery_later
│  lf-linked-list-full
│  lfbtree-full
│  Linearizability-p463-herlihy
│  LIRS-TR-02-6
│  LSM-Tree
│  lsm-vs-fractal
│  michael-cahill-2009-thesis-url.txt
│  michael-cahill-2009-thesis
│  MIT-LCS-TR-314-url.txt
│  MIT-LCS-TR-314
│  nsdi_spark
│  omid-sosp11-display-poster12
│  omid-sosp11-final12
│  p1724_tilmannrabl_vldb2012
│  paxos-simple
│  Percolator
│  Probabilistically Bounded Staleness-pbs-vldb2012
│  Query Execution in Column-Oriented Database Systems
│  raft
│  ramp-sigmod2014
│  skip-graphs-journal
│  spanner-osdi2012
│  spanner-专利US5247664
│  Speedy Transactions in Multicore In-Memory Databases
│  Tokutek-White-Paper
│  warp
│
└─Concurrency Control and Recovery in Database Systems
        appendix
        biblio
        chapter1
        chapter2
        chapter3
        chapter4
        chapter5
        chapter6
        chapter7
        chapter8
        contents
        glossary
        index
        preface

谈谈阿里巴巴的PolarDB

产生 PolarDB 这类产品的想法还是很容易理解的,但凡做RDBMS的人都知道有redoLog和b-tree这两个东西,如果我们把redoLog中的一个block和b-tree中一个leaf page做一层抽象,变成虚拟的block和leaf page,它们即可以代表本地的也可以代表网络上另一台服务器文件系统中的一块数据。

经过这一层抽象后,整个数据库就像是跑在没有硬盘的服务器上一样,那些虚拟的redoLog block和b-tree leaf page就变成一个指针了,此时的服务器就能很好的充当一个计算节点的角色了。

所以 PolarDB 在**上并不让人吃惊,关键还是在工程实现的细节上,并且提前尝试RDMA这种远未普及的新技术,让访问本地文件系统跟访问远程存储节点上的数据在延迟上不相上下。

当然其中还有一个Parallel-Raft,使得从计算节点到多个存储节点之间的数据同步是并行的,而不是 计算节点 -> leader存储节点 -> follower存储节点 这样的串行写入流程。

PolarDB 这样的架构,除了计算节点很容易扩容之外,配上RDMA和Parallel-Raft,理论上的写入性能会跟单机版的MySQL接近(jdbc client->MySQL约等于jdbc client->PolarDB 计算节点+RDMA+存储节点)。

我司的数据库产品运行在replica set模式时,是在jdbc client端发起并行复制的(配合服务端完成整个复制协议),所以在写入性能上跟PolarDB接近。

PolarDB 的这套技术很容易迁移到其他RDBMS中,拿我司的数据库来说,b-tree的leaf page分成了local leaf page和remote leaf page两种,只要把PolarDB如何操作RDMA那一整套东西用到remote leaf page中就行了,如果想用PolarDB的Parallel-Raft,就在jdbc client禁用并行复制。

TiDB/TiKV 就不好弄了,要想使用PolarDB这种架构,必须把TiDB/TiKV合并,然后进一步修改RocksDB,并且TiKV中的Raft实现也可能需要大改或直接废掉。

补充:

PolarDB这种架构依然是传统replica set的范畴,如果将来想扩展到sharding模式,还需要从InnoDB这一层一直往上改到MySQL Server层,sharding模式下频繁出现的分布式事务问题依然很刺手,特别是表和索引的数据块在不同的存储节点上时(比如要支持全局索引),如何保证执行update/insert/delete时不同的存储节点中的表和索引数据满足ACID。

再补充:

睡不着,刚想了一下,PolarDB可以学我司的Lealone数据库使用leaf page sharding算法,只要leaf page发生切割时让其中的一个子page移到不同的存储节点即可。

这样的话在做分布式聚合时都不用做sql重写了,比如把求平均数重写为count和sum。因此就不用改sql引擎层的代码了。扫描到b-tree的page时哪怕它们在不同节点,也没关系,就像挂载本地硬盘一样。

Lealone因为缺乏RDMA的支持,就必须得重写sql,否则遇到remote leaf page时直接加载远程page是很低效的。

不过PolarDB只允许一个可写的计算节点,这在一定程度上损失了leaf page sharding算法的好处,Lealone可以并行读写。

总之,PolarDB对RDMA的大胆尝试还是值得称赞的,如果我司的Lealone数据库也配上RDMA,那就是个完美的数据库了,合适的时机我再考虑集成RDMA的方案。

再见 2019,你好 2020

已经连续三年在 GitHub 写年度总结了,就连标题的风格都一样。
2019年对于我很重要,也很顺利,甚至还超出预期。

前言

10年前的2009年是我至今技术生涯第一个超重量级的转折点,2006年辞掉工作,靠着前面4年工作得来的积蓄宅在出租房里,全身心研究自己喜欢的技术,花了4年时间才有了一点小成果,直到这篇文章发出来后才被注意到,也因此被推荐去了阿里巴巴工作。

10年后的2019年,也是类似的经历,经过多年的技术研究与实践,7年前心心念念的数据库也做出来了,而且还全部开源。等到2029年回头再看时,也许2019年会是我技术生涯的第二个转折点

工作

本来今年的工作计划重点是完善 Lealone 数据库,但是在年初的时候突发灵感想去做一个大一统的 SQL 引擎,用来统一解决 OLTP、OLAP、批处理、流程处这四个场景下的计算问题,所以2019年的前面8个月都转去做 Bats 这个新的 SQL 引擎项目了。

为了做 Bats 这个项目前期做了很多技术研究,最初以 Apache 的 Hive、Spark、Flink、Tez、DrillCalciteApex 作为研究对象,最后考虑到 Drill 和 Apex 都使用 Calcite 作为 SQL 引擎并且有一些自己的扩展,刚好这三者能满足 OLAP、批处理、流程处这三种场景下的一些需求,所以工作重心就围绕 Drill、Calcite、Apex 的源代码展开了,花了8个月研究三者的代码,并做了很多代码整合与重构的工作。

Drill、Calcite 的设计比较复杂,考虑到 Bats 这个项目要想达到的目标可能不是短期就能实现的,所以优先级降低了,毕竟这只是计划外的项目。从9月份开始就重新把精力放在 Lealone 这个产品上了,先把 OLTP 领域的东西做好,后续再继续投入到 Bats 这个项目。长远看,我就是想用 Lealone + Bats 这个组合来完成我的另一个宏伟目标: 统一数据库和大数据

Lealone 今年的工作绝大部分就是完善这里提到的几个技术想法,然后还把之前未开源的几个超重量级的技术创新也开源了,包括新的分布式事务模型、新的副本强一致性协议等等。

生活

今年生活过得很充实,虽然骑自行车的次数极少了,也没再去跑步,只是常常去散步,身体也算健康,没出什么大的毛病,不像2017/2018年那么痛苦了。这一年在生活方面倒是创造了一项个人新记录: 有300+天是背靠床头,半躺在床上工作的。这种工作姿势对我而言比较舒服,连常见的颈椎问题都极少出现了。

情感

虽然我从不过生日,但是2019年12月24号(农历11月29)是我的公历和农历生日难得重合的一次,所以我刚刚满38岁不久。虽然我对结婚这件事一直是随缘、宁缺毋滥的态度,但是我家人还是很操心的,光是这一年就给我介绍了好几个相亲对象,当然我一个都没去见面。找一个合适的人还是很难的,家人的圈子跟我差别非常大,他们认识的人我相信很难适合我的。即便如此,我也没有逆反心理,该跟家人吃晚饭的,还是吃得很开心,他们说的我认真听就好,不会因为结婚的事跟家人闹矛盾。

前女友(如果算的话)在8月份时给我发过一次短信,问我有没有结婚,过得好吗。所有跟她相关的东西我都删除或丢弃了,分了就分了,我也没有回短信,从那次之后到今天她也没再发过。也许不回复就是最好的回复吧,虽然冷漠了点,但总比给人留念想好。

家庭

这里说说跟家人相关的,我有一个姐和一个弟,都已经结婚,也都有了一两个孩子,我爸2008年时就因为晚期肺癌去世了,我妈跟我弟在乡下老家一起过。作为家里唯一一个通过读书改变命运的农村娃,我深知在农村帮扶自己的兄弟姐妹是一件多么不容易的事情,也希望自己的亲人能过上好一点的生活。

有些人不是不努力,而是即便努力了能达到的高度也是有限的,所以我会尽力帮助,哪怕花一些钱能让亲人过得好一点我也愿意。我知道房子这件事情是一件很花钱的事,很多人工作20年也难买一个房子。

所以,这一年我做了一个比较大的决定,因为我暂时不需要太多钱,所以我把我的大半积蓄拿出来,把乡下的老房子拆了,重新起了一套三层的小洋楼,虽然花费不多,包括装修在内也不到35万,但是能让我妈开心我就开心。

我姐也有了孩子,过去在城里一直租的小单间,每次去她那里吃晚饭都不是滋味,刚好她手里也攒了一些钱,我也答应帮她出一部分,当然也不用还,这样她的压力就小一点,才舍得买房,所以在2019年的最后一个月她终于交完首付买到房了。

我妈这一年也来城里帮我姐带一个两岁的孩子,刚好家里起房子没地方住就让她提前体验一下城里的生活。来之前我是预测我妈不会喜欢城里的生活的,果真呆了大半年后就很想回去了,差不多每周都想回乡下看看。上一代人有上一代人的活法,做子女的也没必要强求。

另外,我大叔家也起新房子,但是没什么钱,所以我也借了几万给他,虽然说是借,还不还也无所谓了。

投资

今年没有新的投资项目,气候异常,家里的果树今年结果比往年少了一半,估计又是没钱赚的一年,在农村做养殖的事还是看天吃饭,对我来说这些都不重要的,就当是给家人炼手提升做事能力的一种方式了。

这一年也卖了150股阿里的股票,分了两次卖,有次以181美元每股的价格卖了100股,另一次是188美元卖了50股,现在涨到210+美元了,一下卖亏了好几万RMB。本来想卖了之后得点钱然后多帮帮其他亲友的,结果这钱不能转回来,早知如此我就丢在那不管了,说不定以后还能涨。

2020

2019年就说这么多了,工作方面这一年可以看成是产品研发之年,2020年开始就正式商业化了,虽然在传统行业做了不少商业项目,但是没有做过数据库这些基础软件的商业化工作,所以这对我来说是既陌生又好奇的。公司还是会继续留在我的家乡桂林,往后希望能在自己的家乡做出一点小成绩。

家人方面我已经做了我该做的,没什么太多奢求了,只希望自己的家人平平安安、开开心心就好。也希望我自己能在40岁前能找到一个合适的人吧,这也是我妈唯一操心我的一件事了。

最后,也祝所有的同事、朋友、同学,还有各位网友都能过上自己想要的生活!

云厂商将进一步摧毁开源软件

刚刚又看到一个消息,Confluent 修改 License 除非有商业许可,否则不允许在云厂商的 SaaS 平台上部署。

两年前就在微博上断言,开源软件作者们如果不能从云厂商那里得到合理的利益分配,就只能给云厂商做嫁衣,云厂商将逐步摧毁开源软件行业。

已经看到一批 NoSQL 数据库都修改了 License,包括 Redis、MongoDB、Neo4j 等等。

连 Oracle 公司都已经有选择的把 Java 平台的一些功能商业化了。

做开源软件的出路在哪?

当开源软件还在起步阶段时,为了吸引目光吸引潜在用户,为了行成良好社区,开源作者们只能无怨无悔地付出。等到开源软件用的人多了,如何在开源和商业之间取得平衡?如何防止云厂商不劳而获?

修改 License,基础功能开源免费,并且不允许云厂商免费使用,这是现在知名开源软件的普遍做法。

Coroutine

/*
 * Copyright (c) 2008, Matthias Mann
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Matthias Mann nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package de.matthiasmann.continuations;

import java.util.ArrayList;
import junit.framework.TestCase;
import org.junit.Test;

/**
 * Test the propagation of unhandled exceptions throw a suspendable call
 * 
 * @author Matthias Mann
 */
public class ThrowTest extends TestCase implements CoroutineProto {

    private ArrayList<String> results = new ArrayList<String>();

    public void coExecute() throws SuspendExecution {
        results.add("A");
        Coroutine.yield();
        try {
            results.add("C");
            Coroutine.yield();
            if("".length() == 0) {
                throw new IllegalStateException("bla");
            }
            results.add("E");
        } finally {
            results.add("F");
        }
        results.add("G");
    }

    @Test
    public void testThrow() {
        results.clear();

        Coroutine co = new Coroutine(this);
        try {
            co.run();
            results.add("B");
            co.run();
            results.add("D");
            co.run();
            assertTrue(false);
        } catch (IllegalStateException es) {
            assertEquals("bla", es.getMessage());
            assertEquals(Coroutine.State.FINISHED, co.getState());
        } finally {
            System.out.println(results);
        }

        assertEquals(5, results.size());
        assertEquals("A", results.get(0));
        assertEquals("B", results.get(1));
        assertEquals("C", results.get(2));
        assertEquals("D", results.get(3));
        assertEquals("F", results.get(4));
    }
}

MVCC 的局限性

MVCC (Multi-Version Concurrency Control) 多版本并发控制 在一些只支持单行事务的系统中比较流行也很实用,比如 HBase,但是却依然有两个很大的局限性。

实现 MVCC 最核心的一点就是在事务提交时检测冲突,如果两个事务发生了冲突,可以对其中一个事务进行 rollback 然后抛出异常,或者在 rollback 后在数据库内部重新执行事务。

MVCC 的局限性正好就是在事务发生了冲突之后的处理动作,如果对事务 rollback 然后抛出异常,那么对于客户端是很不友好的,在并发事务数稍微多一点时,发生冲突的概率很大,客户端收到事务冲突异常是很常见的,这种异常只跟具体数据库的内部实现逻辑有关,并不是一种业务相关的异常,如果在客户端代码中随处都要考虑处理这种异常会让开发人员崩溃。

如果在 rollback 后不抛异常,而是在数据库内部重新执行事务会如何呢?
由于在数据库内部看到的事务跟在客户端代码中直观看到的事务不是严格相等的,如果一个事务只涉及单行记录,那么在执行事务重做时并不会产生什么问题,如果一个事务涉及多行记录,或者在客户端代码的同一个事务中后执行的语句需要依赖前一条语句的执行结果,那么在数据库内部重新执行事务就有可能造成奇怪的行为。

比如,这里就有个例子: http://blog.sina.com.cn/s/blog_4673e6030102xlqx.html 这里是官方解释: https://www.zhihu.com/people/huang-dong-xu/posts

例子中的场景就是在同一个事务中后执行的语句需要依赖前一条语句的执行结果,前一条语句返回的结果说明账号里还有钱,所以就执行后一条语句,但是在事务提交时因为数据库使用了 MVCC ,导致提交时产生了冲突,然后事务被 rollback,紧接着在数据库内部重新执行事务,但是因为在内部执行事务时数据库只看到这个事务中的两条语句,无法知道在客户端代码中的逻辑是后一条语句的执行条件是受第一条语句的结果影响的,所以数据库内部只能顺序执行两条更新语句,最后导致结果不一致。

这就是使用 MVCC 后不是所有的事务在 rollback 后都能重新执行的原因,因为数据库内部看到的事务并不严格等价于客户端中的事务。

我很早就意识到 MVCC 的局限性,虽然在 Lealone 数据库的实现中也继续沿用了 MVCC 这个词,但并不是严格意义上的 MVCC,更精确一点说应该是: 行/列锁 + Copy On Write,也就是更新记录时,把原有记录 copy 一份(浅copy),然后再对记录加锁,这样不会阻塞读操作,只阻塞写操作。这样就避免了传统 MVCC 的两个局限性,另外,因为线程不与事务绑定,所以事务在对行/列加锁后,即使在当前线程中执行的事务被阻塞了,也不会阻塞当前线程,当前线程会转去执行其他事务。

DB Benchmark

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

//运行这个例子要在MySQL和PostgreSQL中存在一个test数据库
public class Benchmark {
    static String url = "jdbc:mysql://localhost:3306/test?user=test&password=test";
    // static String url = "jdbc:postgresql://localhost/test?user=test&password=test";
    static Random random = new Random();
    static CountDownLatch latch;

    public static void main(String[] args) throws Exception {
        benchmark();
    }

    static Connection getConnection() throws Exception {
        return DriverManager.getConnection(url);
    }

    static class MyThread extends Thread {
        Statement stmt;
        Connection conn;
        long read_time;
        long randow_read_time;
        long write_time;
        int start;
        int end;

        MyThread(int start, int count) throws Exception {
            super("MyThread-" + start);
            conn = getConnection();
            stmt = conn.createStatement();
            this.start = start;
            this.end = start + count;
        }

        void write() throws Exception {
            long t1 = System.currentTimeMillis();
            for (int i = start; i < end; i++) {
                String sql = "INSERT INTO test(f1, f2) VALUES(" + i + "," + i * 10 + ")";
                stmt.executeUpdate(sql);
            }

            long t2 = System.currentTimeMillis();
            write_time = t2 - t1;
            System.out.println(getName() + " write end, time=" + write_time + " ms");
        }

        void read(boolean randow) throws Exception {
            long t1 = System.currentTimeMillis();
            for (int i = start; i < end; i++) {
                ResultSet rs;
                if (!randow)
                    rs = stmt.executeQuery("SELECT * FROM test where f1 = " + i);
                else
                    rs = stmt.executeQuery("SELECT * FROM test where f1 = " + random.nextInt(end));
                while (rs.next()) {
                    // System.out.println("f1=" + rs.getInt(1) + " f2=" + rs.getLong(2));
                }
            }

            long t2 = System.currentTimeMillis();

            if (randow)
                randow_read_time = t2 - t1;
            else
                read_time = t2 - t1;
            if (randow)
                System.out.println(getName() + " randow read end, time=" + randow_read_time + " ms");
            else
                System.out.println(getName() + "  read end, time=" + read_time + " ms");
        }

        @Override
        public void run() {
            try {
                write();
                read(false);
                read(true);
                stmt.close();
                conn.close();
                latch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static void benchmark() throws Exception {
        Connection conn = getConnection();
        Statement stmt = conn.createStatement();
        stmt.executeUpdate("DROP TABLE IF EXISTS test");
        stmt.executeUpdate("CREATE TABLE IF NOT EXISTS test (f1 int primary key, f2 real)");
        stmt.close();
        conn.close();

        int threadsCount = Runtime.getRuntime().availableProcessors();
        int loop = 1000;
        latch = new CountDownLatch(threadsCount);

        MyThread[] threads = new MyThread[threadsCount];
        for (int i = 0; i < threadsCount; i++) {
            threads[i] = new MyThread(i * loop, loop);
        }

        for (int i = 0; i < threadsCount; i++) {
            threads[i].start();
        }

        latch.await();

        long write_sum = 0;
        for (int i = 0; i < threadsCount; i++) {
            write_sum += threads[i].write_time;
        }

        long read_sum = 0;
        for (int i = 0; i < threadsCount; i++) {
            read_sum += threads[i].read_time;
        }
        long randow_read_sum = 0;
        for (int i = 0; i < threadsCount; i++) {
            randow_read_sum += threads[i].randow_read_time;
        }

        System.out.println();
        System.out.println("threads: " + threadsCount + ", loop: " + loop + ", rows: " + (threadsCount * loop));
        System.out.println("==========================================================");
        System.out.println("write_sum=" + write_sum + ", avg=" + (write_sum / threadsCount) + " ms");
        System.out.println("read_sum=" + read_sum + ", avg=" + (read_sum / threadsCount) + " ms");
        System.out.println("randow_read_sum=" + randow_read_sum + ", avg=" + (randow_read_sum / threadsCount) + " ms");
    }
}

Lealone的过去现在将来

Lealone不是一个市场驱动的投机产品,我更愿意把她看成是我将数据库和分布式系统两个领域融合后,进行基础理论研究和探索的试验品。目前已经到第5代了,前两代总体上是失败了,我可以再次回顾一下。 ​​​​

第1代

第1代仅仅是在HBase之上做分布式SQL引擎,当时我把H2数据库的单机SQL引擎改成分布式之后把HBase当成底层的分布式存储来用,同时用OMID解决分布式系统中与事务相关的所有问题,因为要依赖HDFS和ZK,整个架构涉及的组件太多,底层数据块隔得太远,对于索引的优化很难进行,挂了一个RS,可用性损失很大。

当然,因为HDFS已经帮你管理好数据块的副本问题了,这是好事也是坏事,好事是在HBase之上实现SQL和事务时都不用考虑多副本的问题,坏的一面就是我只能通过一个RS去读写某条记录,我想要更多的读写入口,做不到啊,所以HBase这种方案限制了很多想像力。

对于第一代失败的HBase尝试还是有收获的,比如让我学会了怎么把一条SQL重写后变成一条分布式SQL,让它能并行分发到HBase的不同RS上同时执行,当然,最重要的还是从OMID + HBase这种解决事务问题的方案中得到了新的灵感,让我找到了一个解决分布式事务问题的更好方案(这个就不展开了)。

第2代

第2代的焦点是放在模仿Cassandra身上的, Cassandra那样的架构消除了HBase所有的问题,组件少,每个节点都可以直接读写并且使用的是本地文件系统,所以索引想怎么玩都行。我当时并没有在Cassandra的代码上改造,只是拿了gms/net相关的代码,然后在分布式SQL引擎和H2的MVStore单机存储引擎之间用DHT做sharding。

第2代的最大问题其实也是Cassandra的最大问题,用DHT做sharding只适合做一些简单的单key查询,要按sharding key进行范围查询只能给集群所有节点都发查询请求了,哪怕只是个小范围的查询如果不拆开成多条sql也只能这么干。

对于一个分布式数据库来说,改动DHT这样的sharding算法是伤筋动骨的,Cassandra现在你让它去掉DHT改用HBase那种全局有序range的方案那是要命的,虽然它也有类似range的方案,但那是不能改的,没什么用。还好第2代的失败尝试过程中,又让我找到了新灵感,弄出了一种解决副本强一致性问题的新协议。

第3代

第3代摆脱Cassandra的思维定式后,就彻底解放了,脑洞大开,新的sharding算法不再像Cassandra那样放在SQL引擎和单机存储之间,而是下推到存储引擎内部,我引入了一种叫Leaf-Page-Sharding的算法,同时拥有DHT和range的优点。

第3代Lealone除了包含我原创的分布式事务模型、副本强一致性协议和Leaf-Page-Sharding算法外,当然还有其他很大胆的尝试,包括混合多种运行模式、全链路异步化、基于SQL优先级抢占、在对等架构中实现单表自增字段的全局有序单调递增(非常适合时序数据库的场景)。

第4代

第4代Lealone,换用一个高大上的名字就是“自治数据库”,不过我并不想扯上机器学习,而是从数据库现有的算法和数据结构着手,怎么让它们变得更灵活更动态,比如我最近在试验的消除单机、replica set、 sharding三种模式的差异就是往自治这个方向走。

第4代Lealone,除了优化数据库内部的实现外,我还做了大量能改变应用开发模式的疯狂尝试,从前端到后端应用服务器、web框架、ORM框架,甚至小到连接池这种东西,我都把它们考虑在内,哪些适合放到数据库解决的就干掉,不适合的也给他们提供便利。

第5代

第5代Lealone,新增了列锁、page粒度的混合行列存储、异步化无锁B-Tree、统一异步任务调度器这几个超重量级的特性。详情见: #22

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.