作者归档:raywill

学习SQLSTATE

1. 什么是SQLSTATE

shell> SELECT * FROM no_such_table;
ERROR 1146 (42S02): Table ‘test.no_such_table’ doesn’t exist

上面执行一条SQL语句出错后的显示。1146是MySQL自己定义的错误码,42S02是ANSI SQL和ODBC定义的错误码,“Table ‘test.no_such_table’ doesn’t exist”是MySQL返回的错误原因。

其中,42S02就是本文要讨论的SQLSTATE

 

2. 为什么要有SQLSTATE

42S02是ANSI SQL和ODBC定义的错误码,可以理解成是错误码标准。假设没有SQLSTATE,世界会是什么样子?你开发了一款数据库驱动程序,希望兼容MySQL、Oracle、SQLServer。对于锁冲突,MySQL返回错误码2011,Oracle返回9912,SQLServer返回3231(以上3个数据为杜撰),如果你希望检查到锁冲突后,立即执行do_something(),那你需要这样写代码:

if (2011 == conn.errno || 9912 == conn.errno || 3231 == conn.errno) {
    do_something();
}

如果还希望支持Postgre,则需要增加Postgre的错误码处理。这是个悲伤地故事,不想再讲。

可见,数据库自定义错误码是靠不住的,他们各自为政。也许你会想,为什么这些数据库厂商不能协调一下,统一一下错误码呢?理想很丰满,现实很骨感。因为在某个特定数据库内部实现中,可能内部定义了四五个不同的错误码来表示锁冲突,用一个错误码无法满足内部逻辑的需求。所以,完美的解决方式是:

*. 内部,用数据库自己的错误码,爱怎么用就怎么用,当需要把这个错误码输出到外部的时候,先做一个转换,将内部错误码转换成SQLSTATE。
*. 数据库驱动程序只看SQLSTATE,忽略数据库自定义错误码。

 

3. SQLSTATE数据格式详解

SQLSTATE包含5个字母,前两位表示错误类别,后三位表示子类,均有0~9,A~Z(大写)这些字符组成。00000表示没有错误。

前两个字母定义的错误类别:

00 = 没有错误

01 = 有WARNING

02 = 游标NOT FOUND

> 02 表示某种异常,MySQL的异常,详细见http://dev.mysql.com/doc/refman/5.6/en/error-messages-server.html  这里定义了MySQL内部800多个错误码与SQLSTATE的映射

 

并不是每一个内部错误码都能明确映射到一个有意义的SQLSTATE,对于这一类内部错误码,统统都映射到HY000这个SQLSTATE上去,意思就是:我也不知道咱们这个错误码对应哪个SQLSTATE好,就这么凑合着吧。例如:Error: 1004 SQLSTATE: HY000 (ER_CANT_CREATE_FILE)

 关于SQLSTATE的格式,还有很多讲究,详细参考这篇文档,比较清晰:https://mariadb.com/kb/en/sql-99/sqlstate-codes/  


4. 数据库中如何实现SQLSTATE

可以创建一个Map,将错误码映射到SQLSTATE即可。如果错误码的规划设计正好是从0~N,或者0~-N,那么可以直接用数组来实现这个映射,错误码即为数组的下标;更通用的方式,还是用数组,只不过查找方式是二分查找,也很方便。

MySQL中的实现,详见share/errmsg.txt和include/sql_state.h 。

 

5. OceanBase中如何实现SQLSTATE

参见lib/ob_errno.cpp

可以看到,与MySQL相比,OB还多了一个负担:把OceanBase内部错误码尽可能映射成MySQL内部错误码。啥时候别人写数据库的时候能把内部错误码映射成OceanBase的啊?

 

 

 

商业的秘密

最近发生了一些不大不小的事,老余说你应该写一写留作纪念,想想也是,站在黑漆漆的电脑屏幕前洞察到的人性,当然值得大书特书。

具体事情按下不表,得出的经验如下:

1、洞察人性最好的方法就是杀入商场,没有什么比利益的诱惑更能激发出人性的黑暗面。

2、天底下没有永远的朋友或永远的敌人,你看好的背后可能捅你刀子,你憎恨的也许反而有做事的边界。都不重要,重要的是你要想好自己的底牌和后招。

说白了,一切都是博弈。你推我挡,玩得一手好太极,不动声色之下牢牢握住自己的底牌和后招方为上策。

3、做人不能太贪婪,太贪婪什么都得不到,做人也不要太聪明,聪明反被聪明误。

4、凡做过必留下痕迹,墨菲定律是对的,你害怕它发生的往往最终会发生,所以做坏事之前自己先掂量掂量善后能不能做好,否则干脆别做。

5、渠道的力量不可忽视,不会借助别人力量来做事的人做不了大事,也永远无法成功。

 

为谁而生

一个企业,首先要知道的是为谁而生。企业的直接客户是谁,潜在客户是谁,客户的特征是什么,这是否是一块盐碱地,还是一片蓝海?

即使是传统成熟行业,作为一个新入者,如果他还有所追求,为谁而生这个问题一定是淌出来的,而不是想出来的。

不怕路远,路难,方向要对。为谁而生这个问题想对了,路就走对了,企业的价值观也会依附于它。而这个问题想错了,企业之路必然会更加崎岖不平。

X毕业一流大学,光学专业,从事的是照明行业。他的企业之路颇有意思。2年前他想做一件照明领域的工具产品,提供给别人用。却又发现如果做出来了,用户在哪里却不知道。于是转型,借着微信的东风专注于创建照明领域的内容开发,主要就是通过微信公众号,结合志愿者模式做内容传播,前半年主要靠自己,后来逐步有了影响力,开始有大量志愿者通过公众号平台聚集到一起,从事更多翻译和创造性的工作。有了这个基础,事业开始步入正轨。
照明行业,聚集了众多中小企业,从上游芯片厂商到中游设计到下游施工队,整体比较浮躁浅薄,国内产业以抄袭为主。鲜有人静心想为行业做点什么,内容贡献就更不用提。这便是X的机会。
建立行业影响力后,这些中小企业就增加了一个媒体渠道,这时候公司就能够有一些基本的现金流。但是,仅仅是媒体渠道,还不足以让体现一个公司的理想,一个有理想的公司,应该是能够变革行业的。下一步怎么走?玩法很多。

[转] 免费的编程中文书籍索引


免费的编程中文书籍索引

来源:http://www.v2ex.com/t/143671
我在github上fork了一个分支:https://github.com/raywill/free-programming-books-zh_CN

OceanBase0.5中RPC的层次

在OceanBase0.5开源版本中,RPC其实是有一个层次的。自底向上,分别是:
Network Framework — libeasy,负责底层网络框架,packet级别
Client Manager — 负责跟libeasy交互,封装了回调逻辑、异步/同步逻辑
Rpc Stub — 封装了请求的序列化、反序列化逻辑,是应用代码与网络代码的粘合剂

应用代码直接使用Rpc Stub来使用网络最方便,跟调用本地函数区别不大。不需要关心序列化、反序列化过程中的内存问题。但是,应用层如果希望获得一些更为复杂的网络交互逻辑时Rpc Stub则不能提供。比如,希望在A函数post一个请求,然后希望在B函数里wait这个请求的返回值,rpc stub无能为力。

应用代码也可以直接用Client Manager,可以获得很大的灵活性。上一个例子中Rpc Stub搞不定的情况,Client Manager可以搞定。但是直接使用Client Manager会增加一些代码复杂度,例如:需要自己分配DataBuffer,如果追求高性能,还需要自己管理Thread Specific Buffer。

当业务逻辑的对网络需求的复杂度超过Client Manager能力范围时,可以直接用libeasy来解决问题。这是终极方案。在0.5中的SQL模块里用到了该方法,但使用经验证明这种方法可维护性很差,必须对其进行一定的封装。

需要传递哪些参数到远端的自问自答

思考是技术进步的阶梯

A向B机器发送一个plan执行的时候,随着plan本身还需要发送哪些结构?

分析:

OB0.5开源版中发送了SimpleScanParam,但这个参数并不是单独发送的,而是包装在了一个ObHuskTabletScan运算符中,在CS端根据运算符中的参数重新构造子Plan。

答案:

只需要发送plan本身即可。其余要发送的内容都应该封装到operator内部。

进一步发问:

operator内部要封装什么呢?

答案:

比如,读数据超时时间。在运行时用户可以通过命令修改读超时时间,需要将新的时间让plan知晓,以便序列化到B端。
再比如,读数据的版本号。随着时间流逝plan被反复执行,plan需要携带的版本号应该要随时间变化。
这些内容的变化,都应该封装到operator内部。具体做法可以是设置一个IParamProvider()给operator,operator可以随时通过IParamProvider获得最新的数据。IParamProvider的实现比较简单,它持有整个SQL Plan运行时环境的引用,可以随时从中获得最新的参数提供给operator。

这个细节不需要scheduler关心。

结论:

  1. 需要发送plan到B端,而不是自己裸发operator tree到远端。因为plan里面提供了一套遍历op tree(序列化)、分配operator(反序列化)的机制。相当于是一个query的管理器。
  2. 发送到B端的plan并不是一个全功能的plan,有精简的空间。
  3. plan之外并不需要传额外的东西到B端。

自我节奏的调整

最近在做OceanBase RootServer重构后的测试,主要测试工具是用我们牛逼QA开发出的obtest神器。

测试结果总是不太稳定,各种细节错误且不完全复现,很耗人。导致积极性下降。

调整策略:测试期间给自己安排一些优化/重构的活儿,穿插着做事情。

自主做事最精彩

写了两年半的OceanBase,知识体系却依然没有完善,思考原因:缺乏实践。OceanBase中大家都是螺丝钉,各自负责一亩三分地,别人地里种了什么虽然能看个大致,比如施的什么肥,打的什么桩,搭的什么架子,种的哪种秧,但若问为何是这种肥,怎么非得打个桩,架子斜着搭,却又种南瓜秧,则是问着我了。知其然,不知其所以然,是学习OceanBase的最大的难题。

企图从分析源码的角度来体会作者意图的方式失败了。总是不得要领,收益甚浅。

还是祭出老办法:好记性不如烂笔头。动手写码,写一个简单一些的NoSQL系统出来。俗话说,麻雀虽小五脏俱全,只要这个系统能完整做下来,一定会受益甚丰。

于是,MacrayKV诞生。取这个名字是因为这个项目带上了Macduan学弟一起做,我的id是Raywill,俩名字一凑,macray,读起来还蛮上口的。KV则表示key-value存储的意思。就这样了。

没多少天就发现,自主做事最精彩!创新的激情在自主做事的情况下能得到最大激发。比如今天:

基于tcmalloc写了一个线程安全的全局内存分配器,写完之后必须测试一下。为了测试线程安全的特性,必须使用多线程编程。淘宝多隆写的tbsys系统库提供了比较好的多线程封装,用起来已经比较方便了,但在测试代码中多处用多线程时则会发现有大量的冗余代码。每一个需要并发的代码片段都需要创建一个类和一个回调方法,把并发的代码填在回调方法里面。这么做有两个坏处:(1)打乱了测试代码的连贯性。 (2)代码冗余不易维护。(3)还得记忆多线程封装库的用法,我记性太差,每次都要查。

今天早上突发奇想,能不能有一种类似intel #progrm parallel这样的directive呢?这种想法很大程度上是受了前不久coolshell上《数据即代码:元驱动编程》这篇文章的影响。摸索了三十分钟,一个C++的宏方案出台,相当好用:

TEST_F(ObTCFactoryTest, mt_basic_test)
{
  // 第一次并发测试
  BEGIN_THREAD_CODE(my_run, 50)
  {
    for (int i = 0; i < 1000000; ++i)
    {
      Base *b1 = base_tc_factory_t::get_instance()->get(0);
      ASSERT_TRUE(NULL != b1);
    }
    base_tc_factory_t::get_instance()->stat();
  } END_THREAD_CODE(my_run);


  // 一些同步测试代码
  // put your code here


  // 第二次并发测试
  BEGIN_THREAD_CODE(my_run2, 50)
  {
    for (int i = 0; i < 1000000; ++i)
    {
      Base *b1 = base_tc_factory_t::get_instance()->get(0);
      ASSERT_TRUE(NULL != b1);
    }
    base_tc_factory_t::get_instance()->stat();
  } END_THREAD_CODE(my_run2);

}

其实现也是极简的:

#define BEGIN_THREAD_CODE(class_name, thread_count) \
  class _##class_name : public tbsys::CDefaultRunnable \
  { \
    public: \
    _##class_name() { _threadCount = thread_count; } \
  void run(tbsys::CThread *thread, void *arg) { \
    UNUSED(thread); UNUSED(arg); \

#define END_THREAD_CODE(class_name) \
  }};\
  _##class_name my_##class_name; \
  my_##class_name.start();  my_##class_name.wait();

 

more code details here

然后,我就把这个方案移植到了OceanBase,得到了@郁白 的大力赞扬。OceanBase UpdateServer模块的单元测试对于并发测试的需求非常旺盛,希望以后能在代码中处处看到它的使用。

希望自己坚持下去,马上做,做精彩。