2017年3月30日星期四

mysql中如何使用statement取得存储过程的OUT参数

按照通常的方法,只能通过变量+SELECT结果集来获得存储过程out参数的值,例如:
@out1:=0;
@out2:='';
call procedure_test_1(@out1, @out2);
select @out1, @out2;
如何不要使用多条语句而直接使用out参数的值呢?mysql的statement可以实现:
see:https://dev.mysql.com/doc/refman/5.7/en/c-api-prepared-call-statements.html

这里需要注意:
1. 先要绑定参数,然后执行;
2. 执行完后绑定结果
3. 执行mysql_stmt_next_result()后才能取得out参数的值
   如果存储过程中用了多个select, 则最后一个结果集才是out参数的值

see: https://dev.mysql.com/doc/refman/5.5/en/mysql-stmt-next-result.html
官方文档对于取得OUT参数做了详细的说明:
If a procedure has OUT or INOUT parameters, their values will be returned as a single-row result set following any other result sets. The values will appear in the order in which they are declared in the procedure parameter list.
最后,编译和链接的地方需要注意:
g++ -o test_procedure_2.o -c test_procedure_2.cpp -g -Wall -Werror `mysql_config --include`
g++ -o test_procedure_2 test_procedure_2.o `mysql_config --libs` -lpthread
使用mysql_config来获取相关的头文件和库,否则错误的头文件和库将导致:
  · 链接告警,see:http://www.cnblogs.com/chengxuyuancc/archive/2013/05/11/3072981.html
  · 执行时显示找不到:libmysql.so
  · 执行完全得不到正确的结果

最后的最后,如果链接使用:
g++ -o test_procedure_2 test_procedure_2.o `mysql_config --libs_r` -lpthread
  即采用动态方式链接,居然会出现:
     undefined reference to `mysql_stmt_next_result'
  神(la)奇(ji)!!!




2015年5月18日星期一

蛋疼的linux下安装graphviz的过程

1. 先下载了http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.38.0.tar.gz,安装后写了个测试例子:

#test1.dot
digraph example2 {
    Server1 -> Server2; Server2 -> Server3; Server3 -> Server1; }

执行测试程序:
dot test1.dot -Tpng -o test1.png
显示错误:
Format: "png" not recognized. Use one of: canon cmap cmapx cmapx_np dot fig gd gif hpgl imap imap_np ismap mif mp pcl pic plain plain-ext ps ps2 svg vml vtx wbmp xdot

2. 尝试安装graphviz-gd库
yum install graphviz.x86_64
yum install graphviz-gd.x86_64
无效

3. 尝试安装libpng & gd库:
wget "http://prdownloads.sourceforge.net/libpng/libpng-1.6.17.tar.gz?download"
下载 https://codeload.github.com/libgd/libgd/tar.gz/gd-2.1.0
无效

4. 根据一篇帖子,选择安装低版本的graphviz:
    http://weibo.com/p/23041868f23d9f0102vfbw?pids=Pl_Official_CardMixFeed__4&feed_filter=1&sudaref=www.google.com.hk
然后:
http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.26.0.tar.gz
安装后再执行
dot test1.dot -Tpng -o test1.png
出现错误:
Could not find/open font
shit

5. 认真看http://www.graphviz.org/Download..php提供的资源,然后下载:
   http://www.graphviz.org/Misc/fonts.tgz
然后修改dot文件:
digraph example2 {
    fontpath="/data/temp/ttf";
    Server1 -> Server2; Server2 -> Server3; Server3 -> Server1; }
再次执行测试:
dot test1.dot -Tpng -o test1.png

终于好了!

python xmind库初体验

1. *.xmind文件其实是一个zip文件,解压后是一堆XML文件;
2. xmind-python这个库操作XMIND的节点后,保存的时候无法保存样式……(郁闷)
3. 直接把XMIND文件解压缩,然后读写XML文件,应该可以实现带样式的保存(猜测的,时间有限,无法实验)
4. xmind-python的API很简单,看example.py就够了


2014年2月12日星期三

python: 解决两例UnicodeDecodeError错误

1. 使用string.Template
在safe_substitute({"key":"value"})的时候出现UnicodeDecodeError错误,把字典的内容打印出来,发现有一个key是:  'key':u'value'
value里面都是ASCII码,只是碰巧是unicide类型。
于是写个循环把字典转换一下:
dict_temp = {}
for key in dict_param:
value = dict_param[key]
if type(key)==types.UnicodeType:
key = key.encode('utf-8')
if type(value)==types.UnicodeType:
value = value.encode('utf-8')
dict_temp[key] = value
dict_param = dict_temp
然后解决!

2. 在使用 '%s'%(param)格式化字符串的时候出现UnicodeDecodeError
所有param中没有UnicodeString类型,于是在
if __name__=='__main__':
     #在这里加上
      reload(sys)
      sys.setdefaultencoding('utf-8')

然后解决

2013年9月25日星期三

web.py作为fast cgi部署到nginx上

部署方法都来自这个帖子:http://webpy.org/cookbook/fastcgi-nginx
我只是对一些细节进行补充说明

#perl 的正则表达式库
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.33.tar.gz
tar -zxvf pcre-8.33.tar.gz
cd pcre-8.33
./configure && make && make install
#openssl,注意,只有1.0.0才能和nginx一起编译通过
wget http://www.openssl.org/source/openssl-1.0.0.tar.gz
tar -zxvf openssl-1.0.0.tar.gz
#不用编译openssl
cd nginx-1.4.2
./configure --sbin-path=/usr/local/nginx/nginx --conf-path=/usr/local/nginx/nginx.conf --pid-path=/usr/local/nginx/nginx.pid --without-select_module --without-poll_module --with-http_ssl_module --with-http_spdy_module --with-http_gunzip_module --with-http_gzip_static_module --with-openssl=/data/temp/openssl-1.0.0
make && make install
#
wget http://www.saddi.com/software/flup/dist/flup-1.0.1.tar.gz
tar -zxvf flup-1.0.1.tar.gz
cd flup-1.0.1
python setup.py build && python setup.py install
#
wget http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz
tar -zxvf spawn-fcgi-1.6.3.tar.gz
cd spawn-fcgi-1.6.3 && make && make install

#下面配置nginx
location / {
        fastcgi_param REQUEST_METHOD $request_method;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_param CONTENT_TYPE $content_type;
        fastcgi_param CONTENT_LENGTH $content_length;
        fastcgi_param GATEWAY_INTERFACE CGI/1.1;
        fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
        fastcgi_param REMOTE_ADDR $remote_addr;
        fastcgi_param REMOTE_PORT $remote_port;
        fastcgi_param SERVER_ADDR $server_addr;
        fastcgi_param SERVER_PORT $server_port;
        fastcgi_param SERVER_NAME $server_name;
        fastcgi_param SERVER_PROTOCOL $server_protocol;
        fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_pass 127.0.0.1:9002;
}
#配置static目录
    location /static/ {
        root /path/to/www;
        if (-f $request_filename) {
           rewrite ^/static/(.*)$  /static/$1 break;
        }
    }

#启动spawn-fcgi
#vi my_web.py
# 加上 
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#注意文件本身不能是utf-8编码的

chmod +x my_web.py
chown -R user_0:users  my_web/

spawn-fcgi -d /data/my_web -f /data/my_web/my_web.py -a 127.0.0.1 -p 9002 -u user_0 -g users -F 5

换成nginx后,整个站点快了好多,爽多了!



2013年7月5日星期五

javascript: 破坏引用导致的BUG

用代码来解释:
var g_array = [1,2,3];
function MyClass(arr){
   this.make_bug = function(){
      arr = [4,5,6];
   }

   this.remove_one = function(){
      arr.shift(0);
   }
}

//
var obj = MyClass(g_array);
obj.make_bug();
obj.remove_one();
//可以发现,全局数组根本没有被更改
//因为make_bug里面,破坏了对全局数组的引用

2013年5月27日星期一

一个坑:crontab中使用sudo

最近,一个进程总是无法被crontab拉起,加了日志观察后,发现这一句错误信息:
sudo: sorry, you must have a tty to run sudo

google一下发现这篇帖子:http://gcoder.blogbus.com/logs/49929050.html

解决办法为:
vi /etc/sudoers
#注释掉 Defaults    requiretty

搞定!

2013年4月11日星期四

为什么64位下用python调用C库发生了崩溃

在64位下,把一些库封装成C函数,然后编译成so文件供python调用,结果发生了崩溃。
今天试验后发现:如果C函数返回void*等类型,把返回值赋值到python变量后被截断,只剩下低32位的值。
因此,如果C函数中的指针在0xffffffff以上的地址的时候,返回到python中就会被截断。如果再继续使用这个被截断后的地址值,就会发生崩溃。

下面是模拟崩溃的代码:
//C
void* create_object()
{
    return new int(0);
}

void free_object(void* obj)
{
    delete (int*)obj;
}

#python中这样调用
so = cdll.LoadLibrary('xxxx.so')
obj = so.create_object()  #这里被截断
so.free_object(obj)       #这里发生崩溃

=====================================================
解决办法也比较简单,不要通过返回值传递指针,而通过指针参数返回:

void create_object(uint64_t* out)
{
    *out = (uint64_t)new int(0);
}

void free_object(void* obj)
{
    delete (int*)obj;
}



#python中这样调用
so = cdll.LoadLibrary('xxxx.so')
obj = ctypes.c_ulonglong(0)
so.create_object(ctypes.byref(obj))
obj = ctypes.c_void_p(obj.value)
so.free_object(obj)


这里要注意:发现python中的对象的地址都在0xffffffff之内,所以使用byref没发现问题。
byref是否会截断指针,这个还没试验出来。

2013年4月1日星期一

linux64下的一个链接问题: crtbeginT.o

尝试链接一个动态库,结果crtbeginT.o这个系统库出现错误:


g++ -o ttc_api_c.o -c ttc_api_c.cpp -g -Wall -Werror -O2 -fPIC
g++ -o ttc_api_c.so -shared ttc_api_c.o libttc.pic_64.a -static
/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/4.4.6/crtbeginT.o: relocation R_X86_64_32 against `__DTOR_END__' can not be used when making a shared object; recompile with -fPIC
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/crtbeginT.o: could not read symbols: Bad value
collect2: ld returned 1 exit status

百思不得其姐,还好搜索到这篇帖子:
https://bugs.launchpad.net/ubuntu/+source/gcc-4.4/+bug/640734

采用文中的办法,酱紫解决了:

cd /usr/lib/gcc/x86_64-redhat-linux/4.4.6
cp crtbeginT.o crtbeginT.orig.o
cp crtbeginS.o crtbeginT.o



2012年12月30日星期日

2012年12月27日星期四

转:各种操作的延迟时间

下图来自此页面:http://blog.hesey.net/2012/06/evolution-of-large-website-architecture.html


另有一张同事贴的页面:http://www.eecs.berkeley.edu/~rcs/research/interactive_latency.html
比较有趣,每年的变化都能看见,不知道是如何预测出来的。

protocol buffers and thrift:当心类型暴涨

最近在阅读另一个部门交接的代码。
尝试增加一个小小的新功能,然后编译、链接……这个过程相当痛苦。
这个部门在数据传输协议上,采用了类似于protocol buffers或thrift类似的技术:用中间语言定义类型,然后用工具编译成C++代码。

这样做无可厚非。但我在阅读代码和编译链接的时候,非常痛苦:
1、 类型实在太多了,类型套类型,分散在很多个不同的目录中;
2、编译不通过,必须把所有引用到的类型的头文件都include进去;链接也不通过,必须把类型的encode/decode库链接进去。

因此,使用protocol buffers and thrift类似的技术初看很好,随着业务的发展,类型越来越多,代码就变得越来越臃肿,越来越难以维护。

我建议,这样去避免类型暴涨:
1. 采用自动编译,把中间的定义文件放在某个目录下,自动生成各种语言需要的代码;
2. 所有的类型放在一起,甚至可以把所有的类型包含在一个all_types.h中;后续的代码要引用类型,包含这一个头文件即可。怕影响编译速度?可以用预编译头文件解决;
3. 所有的类型的encode/decode代码,全部编译后,打包到一个大的all_types.a中,到时候链接一个库即可;
4. 定期清理,不要的类型丢弃。

2012年12月20日星期四

试验:多线程竞争写

下面一段代码将试验不同条件下多线程竞争对变量进行累加的效果:

#include <stdio.h>
#include <pthread.h>

#define MAX_NUM 5000000

int num = 0;

void* thread_func(void* param)
{
for (int i=0; i<MAX_NUM; ++i)
{
++num;
}
return NULL;
}

int main()
{
pthread_t t1, t2;
pthread_create(&t1, NULL, thread_func, NULL);
pthread_create(&t2, NULL, thread_func, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
printf("%d\n", num);
return 1;
}

假设没有竞争,则num最终输出的结果应该是10000000

试验一:执行上面的代码输出5315953
试验二:将蓝色的这行的定义修改为:volatile int num = 0;
        执行后输出5431651。
        虽然没有得到正确的结果,但从数值上看出,volatile对数据同步还是有一些效果的。
试验三:在红色的行前增加一行代码:__sync_synchronize();
        执行后输出7443185
        试验说明,内存屏障能够进一步加快多核间的内存数据同步。
试验四:将红色的这行修改为__sync_add_and_fetch(&num, 1);
        执行后得到了正确结果10000000
        由此说明,多核条件下并发累加,只有原子操作才能得到正确的结果。volatile关键字和内存屏障都不能解决。
==============================================
进一步思考:如下是CPU的结构图
线程在操作num这个数的时候,都会把num载入自己的 L1 cache line
当CPU0上的线程1改写了num的值,L1 cache line为标示为脏。然后,CPU0上的cache line会同步到CPU 1上的cache line,然后CPU1读取数据的时候,就会是最新的数据。

如果按照这种数据同步原理,则试验一应该得到正确的结果。但为什么结果又不正确呢?

CPU, L1 CACHE, 内存,这三者之间的数据同步机制究竟是如何进行的?希望有大虾予以指教。






2012年12月19日星期三

一个失败的经验:把内存映射文件当成共享内存用

服务器开发中常常采用共享内存来存储数据,好处有:
1. 预分配空间,性能高;
2. 服务器崩溃后,共享内存的数据还在,进程重启后可快速回复服务。(这点尤其重要)

但是,共享内存也有比较麻烦的问题:
1. 增加容量很麻烦:要先dump出数据,然后删除共享内存,再重新分配,再写入;
2. local cache带来了单点问题,机器死机或者掉电后,必然丢失数据。

于是,在某次服务器开发的时候,我尝试用内存映射文件来代替共享内存。因为内存映射文件有这样一些好处:
1. 与共享内存一样,进程重启后,数据得以保留;
2. 扩容方便:如果内存允许,我映射更大的一个内存区域就好;
3. 迁移方便:停止进程,然后把映射文件复制到另一个机器,再启动进程即可。

愿望总是美好的!使用内存映射文件后,系统常常莫名其妙地IO猛烈飚高,都是映射文件惹的祸。
首先,内存映射文件分配的是虚拟内存,等到程序访问这个区域后,发现没有对应的物理内存,会引起一个缺页中断,然后操作系统从文件对应的区域中,加载4KB的数据到page cache中。
然后,如果对这一内存区域执行写操作,数据并不立即被写往磁盘,而仅仅只是把这个page标记为脏页。当整个操作系统的脏页达到一定比例后,操作系统就会把这些脏页的数据写往磁盘,由此也就引起了IO突然飚高。


因此,当采用local cache的存储方案的时候,性能是第一个考虑点,其次才是方便进程快速恢复服务。而使用内存映射文件,必然增加了系统的IO,降低了性能,这与服务器开发的目的是违背的。

2012年12月17日星期一

学到两个新词:NOR和NAND

在看GCC原子操作函数的时候发现有个:__sync_nand_and_fetch
什么是nand操作?

找了半天终于找到:
位操作里除了创建的 and, or, xor, not
还有not or和not and
not or等价于: not (a or b)
not and等价于: not (a and b)

VC++里面貌似已经支持两个新的操作符来代表nor和nand: ~|  ~&

搜索到的相关帖子在这里:http://forums.codeguru.com/showthread.php?420830-bitwise-nor-operator


2012年12月10日星期一

测试伪共享对性能的影响

伪共享(false sharing)的知识需要了解的话,请google之。
下面是测试代码:两个线程分别计算5亿次累加两个相邻变量的时间消耗。
//test_false_sharing_1.cpp

#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>

#define PACK  __attribute__  ((packed))
typedef int cache_line_int __attribute__((aligned(LEVEL1_DCACHE_LINESIZE)));

struct data
{
    int a;
    int b;
};

#define MAX_NUM 500000000

void* thread_func_1(void* param)
{
    timeval start, end;
    gettimeofday(&start, NULL);
    data* d = (data*)param;
    for (int i=0; i<MAX_NUM; ++i)
    {
        ++d->a;
    }
    gettimeofday(&end, NULL);
    printf("thread 1, time=%d\n", (int)(end.tv_sec-start.tv_sec)*1000000+(int)(end.tv_usec-start.tv_usec));
    return NULL;
}

void* thread_func_2(void* param)
{
    timeval start, end;
    gettimeofday(&start, NULL);
    data* d = (data*)param;
    for (int i=0; i<MAX_NUM; ++i)
    {
        ++d->b;
    }
    gettimeofday(&end, NULL);
    printf("thread 2, time=%d\n", (int)(end.tv_sec-start.tv_sec)*1000000+(int)(end.tv_usec-start.tv_usec));
    return NULL;
}

int main()
{
    data d = {a:0, b:0};
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread_func_1, &d);
    pthread_create(&t2, NULL, thread_func_2, &d);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("end, a=%d,b=%d\n", d.a, d.b);
    return 0;
}

/*
g++ -o test_false_sharing_1 test_false_sharing_1.cpp -g -Wall -O2
*/
----------------------------------------------
执行后输出:
thread 1, time=4121562
thread 2, time=4329193 

把以上程序稍稍修改:
struct data
{
    cache_line_int a;
    cache_line_int b;
};
//struct中的int修改为按照cache_line对齐的int
然后酱紫编译:
 g++ -o test_false_sharing_2 test_false_sharing_2.cpp -g -Wall -lpthread -DLEVEL1_DCACHE_LINESIZE=`getconf LEVEL1_DCACHE_LINESIZE` 
执行后输出:
thread 1, time=1607430
thread 2, time=1629508 
性能提高了2.5倍。

-------------------------------------------------
测试中注意两点:
1. int重新对齐的定义后,在struct中不要在定义对齐的属性,否则之前的对齐属性会失效;
2. 采用getconf LEVEL1_DCACHE_LINESIZE这样的命令获得cache line的大小;
3. 编译中不能加上-O2,否则编译器计算会导致瞬间出结果;(这个优化真是强大啊)

2012年12月2日星期日

刚刚知道了一个牛叉的工具:Google App Inventor

同事在群里贴了这张图,顿时觉得很震撼:



居然还可以画出这么漂亮的程序流程图。

更牛叉的是,这个工具是google用于生成ANDROID APP的。可以一行代码都不写就创建应用。


2012年11月28日星期三

python: 转换被错误定义为unicode类型的utf-8编码

某同事的代码中发现,本来是utf-8编码的字符串,从数据库读出来后,被错误返回为u'utf-8内码'这样的格式。

下面是一个简单的转换方法,还原其为正常的utf-8编码:

>>> a = '测试'
>>> a
'\xb2\xe2\xca\xd4'
>>> b = a.decode('gbk').encode('utf-8')
>>> b
'\xe6\xb5\x8b\xe8\xaf\x95'
>>> c = u'\xe6\xb5\x8b\xe8\xaf\x95'
>>> c
u'\xe6\xb5\x8b\xe8\xaf\x95'
>>> arr = array.array('B')
>>> arr.fromlist([ord(i) for i in c])
>>> print arr.tostring().decode('utf-8').encode('gbk')
测试 

最终的代码是这样:
import string
import array
s = u' \xe6\xb5\x8b\xe8\xaf\x95 '
arr = array.array('B')
arr.fromlist([ord(i) for i in s])
print arr.tostring().decode('utf-8').encode('gbk')

如果读者知道更好的转换方法,希望不吝赐教。

2012年11月27日星期二

安装python图标库pycha搞得很郁闷

根据帖子推荐,发现pycha是所有python图标库中比较简单的,于是打算采用这个。
结果,安装这个库花了我两个半天,仍未能解决,打算放弃。

1. 首先在pycha的首页https://bitbucket.org/lgs/pycha/wiki/Home找下载地址。
   找了半天居然没有,只有个项目管理软件hg的地址。
   好吧,先安装windows下的hg客户端。
2. 安装hg客户端:
  http://cdn.bitbucket.org/tortoisehg/files/downloads/tortoisehg-2.6.0-hg-2.4-x86.msi
3. 使用hg下载源码:
  hg clone http://bitbucket.org/lgs/pycha
4. 安装上了没效果,原来还需要库 cairo。于是下载cairo
  http://www.cairographics.org/releases/py2cairo-1.10.0.tar.bz2
5. cairo库不同于一般python库的安装,居然还需要python的头文件,于是再下载python源码包来编译:
    http://www.python.org/ftp/python/2.7/Python-2.7.tgz
6. 再编译cairo,指定头文件的路径,仍然编译不通过……

选择一个不成熟的库,果然很折腾啊!





2012年11月26日星期一

评《技术含量的问题》

原帖请见:http://www.dbanotes.net/review/Tech_Simple.html

不是很赞同冯大辉的观点。

    不重视技术问题,因技术的成本高转而采用人工;或者是技术成熟度没达到人工水准,继续采用人工——这些只能在一个特定的阶段来使用。
    比如,公司刚刚成立,还处理生存的压力下,无可厚非。
    而对于一个已经起步的公司,这样做无疑饮鸩止渴:

1. 这是技术债务,总有一天要还的。
   借来的钱一开始用得很爽,等到要还债的时候,就会变得痛苦。

2. 某技术当前阶段的发展不如人工,但不代表它永远都无法超越人工的水平。
   现在不去积累,失去了先机,放弃了成为技术壁垒的决心……等到它成熟的时候,你已经再也没机会去捡起来了。

3. 廉价的人力资源也是资源,随着环境的变化,它慢慢也会成为稀缺资源。
   当前珠三角的用工荒,已经说明了这个问题。

  因此,特别是大公司,应该在某些技术领域投入资源去进行攻关。如果总是以为没技术含量的手段也能解决问题,技术债务总有一天会令它头破血淋。