Archive for the ‘数据库’ Category

在php代码里写sql句子,我喜欢用sprintf,这样看起来比较清晰,但是今天就遇到了一个隐藏很深的bug。

有这样一段代码:

        public function update_catogery_ad($id, array $info){/*{{{*/
                if (!$id || !$info){
                        return false;
                }

                $sql = null;
                foreach($info as $a){
                        list($type, $key, $val) = $a;
                        switch($type){
                                case 'int':
                                        $val = intval($val);
                                        break;
                                case 'string':
                                        $val = "'"._es($val)."'";
                                        break;
                        }
                        $sql .= sprintf(" `%s` = $val,", _es($key)); #1
                }
                $sql = substr($sql, 0, -1);

                $sql = sprintf("update %s set $sql where id = %d", $this->_get_catogery_ad(), intval($id)); #2
                return $this->db->update($sql);
        }/*}}}*/

它执行的是,根据传入的info数组中的字段,更新某表。在测试环境,运行的很好,但是上线之后,有同事就反映无法更新数据,也不报错。

出问题的info数组是:

$info = array(

‘a’ => ‘bbbb’,

‘c’ => ‘<area target=”_blank” alt=”" href=”http%3A%2F%2Fitry.try8.info%2Ftaobao%2Ftry%2F%3Fcatogery%3D%E4%BC%98%E8%B4%A8%E8%89%AF%E5%93%81″ coords=”0,100,180,170″ shape=”rect”>’,

);

这里字段a可以被正常更新,但是字段c就维持原样。

仔细观察这段数据,可以看到字段c的value中含有%!在sprintf里,它会被当作特殊字符,于是第一条sql句子会报warning:Too few arguments,由于页面上display_errors关闭了,所以没有看到任何提示,这段代码会默默的执行下去,完成整个功能,看起来就好像c字段被忽略掉一样。

于是动手修改第一条sql句子为:$sql .= sprintf(” `%s` = %s,”, _es($key), $val);

再次运行,直接报sql句子为空!这时发现第二条sql句子也有同样的问题。于是将第二条句子修改为:$sql = sprintf(“update %s set %s where id = %d”, $this->_get_catogery_ad(), $sql, intval($id));

这些人为的bug会隐藏的很深!得多加小心,所以,编程习惯很重要!在使用sprintf的时候,如果变量中可能带有特殊字符,则一定要放在arg里,不能直接写入format里。

tokyo cabinet与tyrant一起,组成了一个可用的NoSQL数据库,cabinet是lib,tyrant是server。其性能和应用性毋庸置疑,希望可以学习,同时也对其稳定性持怀疑态度,希望可以看看是我用的不对,还是其源码有可改进的地方。

从tyrant/ttserver.c的main函数入手:

main(){
    ……
    g_serv = ttservnew();
    int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, kl, logpath,
                  ulogpath, ulim, uas, sid, mhost, mport, rtspath, ropts,
                  skelpath, mulnum, extpath, extpcs, mask);
    ttservdel(g_serv);
    if(extpcs) tclistdel(extpcs);
    return rv;
  }
}

省略的部分是命令行参数处理,ttservnew函数对TTSERV进行初始化,其中初始化了4个锁:

ttservnew(){
    ……
    if(pthread_mutex_init(&serv->qmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed");
    if(pthread_cond_init(&serv->qcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed");
    if(pthread_mutex_init(&serv->tmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed");
    if(pthread_cond_init(&serv->tcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed");
    ……
}
typedef struct _TTSERV {
    ……
    pthread_mutex_t qmtx;                  /* mutex for the queue */
    pthread_cond_t qcnd;                   /* condition variable for the queue */
    pthread_mutex_t tmtx;                  /* mutex for the timer */
    pthread_cond_t tcnd;                   /* condition variable for the timer */
    ……
}

这里就牵涉到两个重要的概念,timer线程和worker线程,稍候会介绍。qmtx和qcnd主要是供主线程和worker线程使用,tmtx和tcnd主要供主线程和timer线程使用。

可以看出,真正的处理逻辑是在proc函数里。这个函数里进行了大量的稳定、log、容错等处理,先都忽略(但是这些才保证了一份好代码的存在!)。

proc函数中主要进行了以下工作:

  1. 如果以daemon模式运行,则调用ttdaemonize函数,建立可运行的子子进程,父进程和子进程都退出
  2. tcadbopen函数,根据数据库文件,为TCADB对象赋值,对于hash类型的db而言,会将数据库文件mmap到共享内存中
  3. 调用ttservaddtimedhandler函数,建立一个tserv的timer对象,其handler是do_slave
  4. 如果有外部脚本相关的,则再调用ttservaddtimedhandler函数,建立N个timer对象,其handler是do_extpc
  5. 调用ttservsettaskhandler函数,将tserv的do_task赋值为do_task函数
  6. 调用ttservsettermhandler函数,将tserv的do_term赋值为do_term函数,处理退出
  7. do{ ttservstart(); }while(g_restart); 之类的g_restart值可以有signal改变,从而使ttserver可以再次执行ttservstart,以达到重启、reload的效果
  8. 在ttservstart函数中,是真正的业务逻辑

以下简单看看ttservstart函数:

  1. 根据host和port,建立socket,可以是unix原始套接口,也可以是TCP套接口
  2. 新建timer个数个线程,等待执行timer->do_timer函数
  3. 新建thnum个worker线程,以空的reqs初始化,等待执行timer->do_task函数
  4. epoll_wait:
    1. 如果是请求建立连接,则accept连接,并将accept的socket端口加入到epoll的监听队列里
    2. 如果是其他请求,则请求qmtx锁,将该端口push到serv->queue里,释放qmtx锁,并发送qcnd信号,触发worker线程

worker线程的主体就是do_task函数,其读取socket中的数据,如果以magic word打头,则是二进制格式的命令;否则就是memcached兼容的命令格式。根据请求内容,再分别调用如do_put、do_get等函数进行处理。

以do_get函数为例,主要是调用tcadbget函数,读取到内容,再通过socket返回给客户端。这里的tcadbget函数,会根据数据库的类型,调用其get函数进行读取,比如hash database,就使用tchdbget方法。tchdbget中,首先请求HDBLOCKMETHOD 的read lock(允许其他读,但是不允许写),对整个db加锁,然后调用tchdbbidx函数计算出key对应在hash数组中的位置,和二次hash的key(为了解决hash碰撞,对于第一次hash key相同的元素,使用tree再进行存储)。调用tchdbgetimpl函数,进行真正的获取数据。

tchdbgetimpl函数中:由于hash database使用了on-memory hash database作为缓存,所以,如果cache不为空,则先会执行tcmdbget在cache中查找,如果找到则返回,否则继续在真正的hash database里查找。首先找到bucket的偏移位置,对于hash database来说,一部分数据会被map到共享内存,如果bucket属于这部分数据,则从map中读取;否则从数据文件读取(可能存在bucket的一部分数据在map,另一部分在数据文件的情况)。

bucket的header数据结构为:

magic word | hash | left child off | right child off | padding size | size of key | size of value |

buckey里的数据是以树存储的,用上面计算出的二次hash的key,与bucket key比较,若二次key较小,则向左子树找;若较大则向右子树找;否则相等时,比较关键字,若相等,则找到结果,返回;如果虽然二次hash的值相等,但是比较关键字不等,则需要解决二次 hash的碰撞。

二次hash碰撞的解决方法,是沿着当前节点的左子树或者右子树下行直至找到二次hash值等、关键字也等的节点,或者找不到匹配的节点,则返回失败。

———————————- 小分割 ——————————————

2012-1-19

今天想解决3个问题:

  1. 复制数据库文件,与tcrmgr copy的区别;后者对锁的使用情况
  2. B+tree数据文件格式,为什么异常退出会导致数据丢失?
  3. 尝试用c代码读取ulog和数据文件的内容
tcrmgr copy的奥秘

tcrmgr copy的响应函数是proccopy,首先tcrdbnew数据库,并init了一个mutex锁,继而调用tcrdbcopy函数,先请求这个mutex锁,再进行具体的copy操作。

这里请求mutex锁的tcrdblockmethod函数,在多处被调用,比如get、put等等。这里是简单的CLI模式,每次数据库都是new的,mutex锁也是新建的,但是可以猜想,在多线程模式下,线程间的读写操作都是互斥的。(这里存疑,为什么用互斥锁,而非条件变量呢?)

tcrdbcopyimpl组装socket的copy包,通过tcrdbsend发送给ttserver。并通过ttsockgetc获取返回值。

可以看出,copy的奥秘不在tcrmgr这里,而是在ttserver怎样处理copy请求里。

ttserver.c的do_copy函数具体处理copy请求,针对B+tree,其实际处理函数为tcbdbcopy。

首先对数据库加写锁,排除一切的读写操作,从cache里读取叶节点和非叶节点,分别存储到lids和nids里。解锁。由于cache里的数据还未写入到数据文件里,所以针对每个节点,调用对应的save函数,写入到数据文件中(写入每个节点的前后加写锁、解锁)。

调用tcbdbtranbegin启动事务,加读锁,调用tchdbcopy函数(据说B+tree的底层存储其实是hash格式)将数据写到目的文件中,解锁。

实际执行复制操作的是tchdbcopyimpl,这里封装了两种操作,一种是直接调用tccopyfile进行复制,另一种是当目的文件以@开头,则执行命令。仅关注前者,很简单,就是将数据库文件中的内容读取出来,写入到目标文件。

那么tcrmgr copy和直接copy有哪些区别呢?

  • tcrmgr copy会先将cache中的数据写回到文件中;而直接copy不会,所以cache中的数据就丢失了
  • tcrmgr copy写备份文件的时候,会加读锁,排斥写,保证备份文件的数据格式是正确的;而直接copy时没有锁,如果正好遇到cache中的数据同步到数据库文件,则备份文件的格式可能是损坏的
B+tree数据库异常情况分析

从上面的copy过程,猜想,异常退出导致文件格式损坏的原因可能有:

  • 数据写在cache里,没有存入文件,cache里的新数据丢失
  • 存入文件过程中,异常退出,导致文件格式损坏,可能很多数据甚至整个文件都会丢失

具体需要看tcbdbputimpl函数。在该函数中,首先从history里查找key对应的叶节点的pid,如果找不到则从根节点开始二分查找,直至找到或者新建了一个leaf节点的pid(细节没看)。然后将新内容作为rec写入pid对应的leaf位置。之后如果需要,会对整个树进行调整

如果该节点所在的recs数目超过了启动ttserver时的预设值lmemb,或者该节点的的size超过了lsmax,则调整。调整的具体过程忽略,可以参考算法中的B+ tree部分。

否则,如果该节点的recs数目<1,则说明put失败,需要调用tcbdbleafkill函数切断该leaf节点与树的联系(疑问,这个leaf里应该还有其他旧数据,岂不是也丢失了?)。

如果不在事务里,则将数据写入cache。如果页节点的cache数目大于设定值lcnum,则需要将叶节点的cache内容写入到数据文件(又调用了hash的put函数,不清楚那边是否也有cache缓存),并清除叶节点cache的内容。同样处理非叶节点。那么,如果非正常退出,cache中的数据就有可能会丢失。

在将数据写入数据文件的过程中,注意到其rbuf的大小是BDBPAGEBUFSIZ,即32768,这就意味着,一个leaf的最大字节数是32768(数据部分应该是比这个要小)。

留个问题,cache里的数据难道不存在于数据文件中吗?

在csdn上看到有人问:mysql_connect后是不是非要mysql_close,是不是自动释放?

大家伙们,PHP数据库连接是不是在页面执行完之后自己释放,即使不用mysql_close()也可以释放掉?
同理PDO连接到数据库后,是不是非要将PDO实例赋值为NULL,才能将PDO连接断掉?

在N久以前我查过资料,但己经模糊不清了,具说PHP处理每一个被请求的页面时,里面有MYSQL连接的,当页面处理完就立刻释放掉所有用到的资料。如:mysql_connect后的连接。这个说法对吗?PDO同理吗?

第一反应是CGI模式下,mysql_connect是当前请求结束自动释放,而mysql_pconnect是可以被同一web进程(线程)中的多个请求重复使用的。但是为什么呢?

查看php.net上的帮助:

mysql_pconnect() acts very much like mysql_connect() with two major differences.

First, when connecting, the function would first try to find a (persistent) link that’s already open with the same host, username and password. If one is found, an identifier for it will be returned instead of opening a new connection.

Second, the connection to the SQL server will not be closed when the execution of the script ends. Instead, the link will remain open for future use (mysql_close() will not close links established by mysql_pconnect()).

This type of link is therefore called ‘persistent’.

首先,mysql_pconnect建立的链接不会在script结束后释放,那么对于CLI模式呢?从道理和实践代码中检测,都是会释放的。因为进程退出时,所有的资源都会被自动回收。通过以下的代码可以简单的验证:

$db = mysql_pconnect(‘localhost’, ‘mysql’, ‘pwd’);
sleep(100);
var_dump($db);
exit;
同时在mysql中执行:
mysql> show processlist;  # sleep期间
+—-+——-+—————–+——–+———+——+——-+——————+
| Id | User  | Host            | db     | Command | Time | State | Info             |
+—-+——-+—————–+——–+———+——+——-+——————+
|  1 | mysql | localhost:63950 | tshare | Sleep   |    1 |       | NULL             |
| 68 | mysql | localhost       | NULL   | Query   |    0 | NULL  | show processlist |
| 73 | mysql | localhost       | NULL   | Sleep   |   20 |       | NULL |
+—-+——-+—————–+——–+———+——+——-+——————+
3 rows in set (0.01 sec)
mysql> show processlist;  # 进程结束
+—-+——-+—————–+——–+———+——+——-+——————+
| Id | User  | Host            | db     | Command | Time | State | Info             |
+—-+——-+—————–+——–+———+——+——-+——————+
|  1 | mysql | localhost:63950 | tshare | Sleep   |    0 |       | NULL             |
| 68 | mysql | localhost       | NULL   | Query   |    0 | NULL  | show processlist |
+—-+——-+—————–+——–+———+——+——-+——————+
2 rows in set (0.00 sec)

那么CLI模式呢?将上面这段代码cp至一个web页面中,通过浏览器访问结束后,show processlist仍然可以看到有连接存在!这就使得多个web请求可以复用同一mysql链接,从而节省了建立连接的过程。但是这样也可能造成问题,就是当允许的web进程(线程)数目超过mysql的max_connections时,会造成web进程连接mysql服务器的阻塞,因为每个web进程都不释放自己的mysql连接,从而可能造成too many connections的mysql错误。可以参考我之前的blog:http://flykobe.com/index.php/2011/01/20/mysql-too-many-connections%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/

那么它会存在多久呢?与mysqld的wait_timeout相关。如果一直没有被复用,那么它会存在wait_timeout时长,单位是秒。

mysql> show variables like ‘wait_timeout’;
+—————+——-+
| Variable_name | Value |
+—————+——-+
| wait_timeout  | 28800 |
+—————+——-+
1 row in set (0.00 sec)
当然,如果web server的进程(线程)结束,mysql_pconnect建立的连接也会结束。

在同一次http请求里,如果多次使用mysql_connect连接同一台数据库server(可以是不同的database),那么使用的其实是同一个连接。

当想连接到同一台db server的不同databases时,可能会造成一些误会。

$db1 = mysql_connect('localhost', 'mysql', 'sdPx#&4iov%');
mysql_select_db('tshare', $db1);
$sql = sprintf('select * from shopdesc limit 1');
$ret = mysql_query($sql, $db1);
var_dump($ret, mysql_error());
#var_dump(mysql_thread_id($db1));
var_dump($db1);

$db2 = mysql_connect('localhost', 'mysql', 'sdPx#&4iov%');
mysql_select_db('happy_try', $db2);
$sql = sprintf("select * from product limit 1");
$ret = mysql_query($sql, $db1);
var_dump($ret, mysql_error());
#var_dump(mysql_thread_id($db2));
var_dump($db2);

$sql = sprintf('select * from shopdesc limit 1');
$ret = mysql_query($sql, $db1);
var_dump($ret, mysql_error());
exit;

第二次的connect实际上并不会创建一个新连接,而是使用第一次的连接,所以mysql_select_db也会影响到$db1,导致第三次的query失败。

当这段代码混淆在一个略大的项目里时,可能会比较难发现问题,这时可以利用debug_print_backtrace打印调用堆栈,找出问题。

mysql和web服务器分开了,感觉页面打开好慢。在测试服务器根本没有这个问题,分开了应当更快才到,但是反而更慢了。记得以前改过这样的问题,时间长了,淡忘了。老了,记性不好了。哈哈。

解决方法:

[mysqld]
skip-name-resolve

在mysqld中加上skip-name-resolve,重起mysql就OK了。

这样也会产生问题,

GRANT ALL ON test.* TO dba@localhost

这里的localhost是域名,是没法解释的,只能用IP了

zz from: http://blog.51yip.com/mysql/1339.html

今天新搞了一个表,其中有个字段如下:

type enum(‘1tag’, ‘0product’)

因为排序的时候需要有类似的句子:order by weight desc, type asc, id desc 。所以想着把type前面加上数字,用来排序。

结果order的时候,本来相同weight的,应该0product在前面,却一直在后面。

后来思考才想起来,enum其实是数字类型,1tag对应的是0,0product对应的才是1!所以,为了意思明确,还是把字段类型改为:

type enum(‘0product’, ‘1tag’)吧。

转载自:张宴  http://blog.s135.com/read.php/362/ 。原文标题为《利用Tokyo Tyrant构建兼容Memcached协议、支持故障转移、高并发的分布式key-value持久存储系统》。

以下为原文:

——————————————————————————

[文章作者:张宴 本文版本:v1.4 最后修改:2010.06.11 转载请注明原文链接:http://blog.s135.com/read.php/362/]

Tokyo Cabinet 是日本人 平林幹雄 开发的一款 DBM 数据库,该数据库读写非常快,哈希模式写入100万条数据只需0.643秒,读取100万条数据只需0.773秒,是 Berkeley DB 等 DBM 的几倍。

点击在新窗口中浏览此图片


Tokyo Tyrant 是由同一作者开发的 Tokyo Cabinet 数据库网络接口。它拥有Memcached兼容协议,也可以通过HTTP协议进行数据交换。

Tokyo Tyrant 加上 Tokyo Cabinet,构成了一款支持高并发的分布式持久存储系统,对任何原有Memcached客户端来讲,可以将Tokyo Tyrant看成是一个Memcached,但是,它的数据是可以持久存储的。这一点,跟新浪的Memcachedb性质一样。

相比Memcachedb而言,Tokyo Tyrant具有以下优势:

1、故障转移:Tokyo Tyrant支持双机互为主辅模式,主辅库均可读写,而Memcachedb目前支持类似MySQL主辅库同步的方式实现读写分离,支持“主服务器可读写、辅助服务器只读”模式。

点击在新窗口中浏览此图片

这里使用 $memcache->addServer 而不是 $memcache->connect 去连接 Tokyo Tyrant 服务器,是因为当 Memcache 客户端使用 addServer 服务器池时,是根据“crc32(key) % current_server_num”哈希算法将 key 哈希到不同的服务器的,PHP、C 和 python 的客户端都是如此的算法。Memcache 客户端的 addserver 具有故障转移机制,当 addserver 了2台 Memcached 服务器,而其中1台宕机了,那么 current_server_num 会由原先的2变成1。

引用 memcached 官方网站和 PHP 手册中的两段话:

http://www.danga.com/memcached/
If a host goes down, the API re-maps that dead host’s requests onto the servers that are available.

http://cn.php.net/manual/zh/function.Memcache-addServer.php
Failover may occur at any stage in any of the methods, as long as other servers are available the request the user won’t notice. Any kind of socket or Memcached server level errors (except out-of-memory) may trigger the failover. Normal client errors such as adding an existing key will not trigger a failover.


2、日志文件体积小:Tokyo Tyrant用于主辅同步的日志文件比较小,大约是数据库文件的1.3倍,而Memcachedb的同步日志文件非常大,如果不定期清理,很容易将磁盘写满。


3、超大数据量下表现出色:

点击在新窗口中浏览此图片

但是,Tokyo Tyrant 也有缺点:在32位操作系统下,作为 Tokyo Tyrant 后端存储的 Tokyo Cabinet 数据库单个文件不能超过2G,而64位操作系统则不受这一限制。所以,如果使用 Tokyo Tyrant,推荐在64位CPU、操作系统上安装运行。


一、安装
1、首先编译安装tokyocabinet数据库

wget http://www.1978th.net/tokyocabinet/tokyocabinet-1.4.45.tar.gz
tar zxvf tokyocabinet-1.4.45.tar.gz
cd tokyocabinet-1.4.45/
./configure
#注:在32位Linux操作系统上编译Tokyo cabinet,请使用./configure –enable-off64代替./configure,可以使数据库文件突破2GB的限制。
#./configure –enable-off64
make
make install
cd ../

2、然后编译安装tokyotyrant

wget http://www.1978th.net/tokyotyrant/tokyotyrant-1.1.40.tar.gz
tar zxvf tokyotyrant-1.1.40.tar.gz
cd tokyotyrant-1.1.40/
./configure
make
make install
cd ../

二、配置
1、创建tokyotyrant数据文件存放目录

mkdir -p /ttserver/

2、启动tokyotyrant的主进程(ttserver)
(1)、单机模式

ulimit -SHn 51200
ttserver -host 127.0.0.1 -port 11211 -thnum 8 -dmn -pid /ttserver/ttserver.pid -log /ttserver/ttserver.log -le -ulog /ttserver/ -ulim 128m -sid 1 -rts /ttserver/ttserver.rts /ttserver/database.tcb#lmemb=1024#nmemb=2048#bnum=10000000

(2)、双机互为主辅模式
服务器192.168.1.91:

ulimit -SHn 51200
ttserver -host 192.168.1.91 -port 11211 -thnum 8 -dmn -pid /ttserver/ttserver.pid -log /ttserver/ttserver.log -le -ulog /ttserver/ -ulim 128m -sid 91 -mhost 192.168.1.92 -mport 11211 -rts /ttserver/ttserver.rts /ttserver/database.tcb#lmemb=1024#nmemb=2048#bnum=10000000

服务器192.168.1.92:

ulimit -SHn 51200
ttserver -host 192.168.1.92 -port 11211 -thnum 8 -dmn -pid /ttserver/ttserver.pid -log /ttserver/ttserver.log -le -ulog /ttserver/ -ulim 128m -sid 92 -mhost 192.168.1.91 -mport 11211 -rts /ttserver/ttserver.rts /ttserver/database.tcb#lmemb=1024#nmemb=2048#bnum=10000000

(3)、参数说明
ttserver [-host name] [-port num] [-thnum num] [-tout num] [-dmn] [-pid path] [-log path] [-ld|-le] [-ulog path] [-ulim num] [-uas] [-sid num] [-mhost name] [-mport num] [-rts path] [dbname]

-host name : 指定需要绑定的服务器域名或IP地址。默认绑定这台服务器上的所有IP地址。
-port num : 指定需要绑定的端口号。默认端口号为1978
-thnum num : 指定线程数。默认为8个线程。
-tout num : 指定每个会话的超时时间(单位为秒)。默认永不超时。
-dmn : 以守护进程方式运行。
-pid path : 输出进程ID到指定文件(这里指定文件名)。
-log path : 输出日志信息到指定文件(这里指定文件名)。
-ld : 在日志文件中还记录DEBUG调试信息。
-le : 在日志文件中仅记录错误信息。
-ulog path : 指定同步日志文件存放路径(这里指定目录名)。
-ulim num : 指定每个同步日志文件的大小(例如128m)。
-uas : 使用异步IO记录更新日志(使用此项会减少磁盘IO消耗,但是数据会先放在内存中,不会立即写入磁盘,如果重启服务器或ttserver进程被kill掉,将导致部分数据丢失。一般情况下不建议使用)。
-sid num : 指定服务器ID号(当使用主辅模式时,每台ttserver需要不同的ID号)
-mhost name : 指定主辅同步模式下,主服务器的域名或IP地址。
-mport num : 指定主辅同步模式下,主服务器的端口号。
-rts path : 指定用来存放同步时间戳的文件名。

如果使用的是哈希数据库,可以指定参数“#bnum=xxx”来提高性能。它可以指定bucket存储桶的数量。例如指定“#bnum=1000000”,就可以将最新最热的100万条记录缓存在内存中:

ttserver -host 127.0.0.1 -port 11211 -thnum 8 -dmn -pid /ttserver/ttserver.pid -log /ttserver/ttserver.log -le -ulog /ttserver/ -ulim 128m -sid 1 -rts /ttserver/ttserver.rts /ttserver/database.tch#bnum=1000000

如果大量的客户端访问ttserver,请确保文件描述符够用。许多服务器的默认文件描述符为1024,可以在启动ttserver前使用ulimit命令提高这项值。例如:

ulimit -SHn 51200

3、停止tokyotyrant(ttserver)

ps -ef | grep ttserver

找到ttserver的进程号并kill,例如:

kill -TERM 2159

三、调用
1、任何Memcached客户端均可直接调用tokyotyrant。

2、还可以通过HTTP方式调用,下面以Linux的curl命令为例,介绍如何操作tokyotyrant:
(1)、写数据,将数据“value”写入到“key”中:

curl -X PUT http://127.0.0.1:11211/key -d “value”

(2)、读数据,读取“key”中数据:

(3)、删数据,删除“key”:

——————————————————————–

转载完毕。

注:

1、启动ttserver时,指定的host ip为监听ip,如果使用127.0.0.1,则仅有本地才允许访问。所以,如果允许网络访问,应该使用192之类的ip。

2、参数含义如下,适用于TT(tokyo tyrant):

  • capnum :设置记录的最大容量
  • capsiz :设置内存型database的内存容量,内存不足记录将按照顺序移除
  • mode : 可选的选项:w (写)、r (读)、c (创建)、t (截断)、t (无锁)、f (非阻塞锁)。默认值为 :wc
  • idx :设置索引的列名,用:分割
  • opts :可选的选项:l (64位bucket数组,database容量可以超过2G)、d (Deflate压缩)、b(BZIP2压缩)、t(TCBS压缩)
  • bnum :bucket的数量
  • apow :specifies the size of record alignment by power of 2. 如果负数,设置无效
  • fpow :specifies the maximum number of elements of the free block pool by power of 2. 如果负数,设置无效
  • rcnum :设置缓存记录的最大数,如果数值不是大于0则会禁用缓存,默认禁用
  • lcnum :设置缓存叶节点(leaf nodes)的最大数,如果数值不是大于0则会禁用缓存,默认值4096
  • ncnum :设置缓存非叶节点(non-leaf nodes)的最大数,如果数值不是大于0则会禁用缓存,默认值512
  • xmsiz :设置额外内存映射容量,如果数值不是大于0则会禁用内存映射,默认值67108864
  • dfunit :specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
  • width :设置记录的固定大小,如果数值不是大于0,则默认是255
  • limsiz :设置数据库文件的大小,如果数值不是大于0,则默认是268435456
  • lmemb :设置每个叶节点页(leaf page)的成员数,如果数值不是大于0,则默认是128
  • nmemb :设置每个非叶节点页(non-leaf page)的成员数,如果数值不是大于0,则默认是256

lmemb和nmemb是B+tree的参数,规定了叶节点和非叶节点个数。回忆下B+ tree哦。

—————–

TCHDB 哈希数据库、TCBDB B+Tree数据库、TCFDB 定长数据库、TCTDB 表格数据库、TCMDB内存哈希数据库、TCNDB 内存B+Tree数据库

在两台linux机器上做主从备份,比如主服务器是192.168.0.1,从服务器是192.168.0.2。 首先修改主服务器的配置文件my.cnf:

server-id= 1 # 保证主从服务器不一样即可
log-bin = /home/mysql/log/mysql-bin
binlog-do-db=database-name-1 # 如果所有db都需要记录binlog,那么可以省略该行;如果有多个特定的db,分开每行写
binlog-do-db=database-name-2
master-connect-retry=60
expire_logs_days=7

重启服务器以使其生效。

然后修改从服务器的my.cnf:

master-info-file = /home/mysql/log/master.info
server-id= 2 # slave
master-host=192.168.0.1
master-user=user-name
master-password="password"
replicate-do-db=database-name
log-slave-updates
master-connect-retry=60
expire_logs_days=7

在主服务器上,给从服务器帐号开放所有权限:

root@(none) 03:55:04>grant all privileges on *.* to 'user-name'@'192.168.0.2' identified by 'password' with grant option;

在主服务器上,触发重新生成binlog,以作为同步的基准,并将老数据同步到从服务器上去:

$/home/mysql/bin/mysqldump -u root -p'password' --all-database --opt --flush-logs --master-data=2 > happy_try.sql

在从服务器上装载老数据:

/home/mysql/bin/mysql -u root -p’password’ happy_try < happy_try.sql

找到这时主服务器的最新binlog:

mysql>show master status;

重启从服务器,使配置生效。

登录到从服务器的数据库,重置并重启同步线程:

mysql>stop slave;

mysql>change master to master_log_file=”mysql-bin.000011“,master_log_pos=0; # 主服务器上的最新bin

mysql>start slave;

mysql>show slave status\G

*************************** 1. row ***************************

               Slave_IO_State: Waiting for master to send event

                  Master_Host: 192.168.80.27

                  Master_User: mysql

                  Master_Port: 3306

                Connect_Retry: 60

              Master_Log_File: mysql-bin.000011

          Read_Master_Log_Pos: 1194

               Relay_Log_File: mysqld-relay-bin.000002

                Relay_Log_Pos: 251

        Relay_Master_Log_File: mysql-bin.000011

             Slave_IO_Running: Yes

            Slave_SQL_Running: Yes

              Replicate_Do_DB: happy_try

          Replicate_Ignore_DB: 

           Replicate_Do_Table: 

       Replicate_Ignore_Table: 

      Replicate_Wild_Do_Table: 

  Replicate_Wild_Ignore_Table: 

                   Last_Errno: 0

                   Last_Error: 

                 Skip_Counter: 0

          Exec_Master_Log_Pos: 1194

              Relay_Log_Space: 407

              Until_Condition: None

               Until_Log_File: 

                Until_Log_Pos: 0

           Master_SSL_Allowed: No

           Master_SSL_CA_File: 

           Master_SSL_CA_Path: 

              Master_SSL_Cert: 

            Master_SSL_Cipher: 

               Master_SSL_Key: 

        Seconds_Behind_Master: 0

Master_SSL_Verify_Server_Cert: No

                Last_IO_Errno: 0

                Last_IO_Error: 

               Last_SQL_Errno: 0

               Last_SQL_Error:

1 row in set (0.00 sec)

在主服务器上做一些修改,看是否同步到从服务器上。

如果发现有错误,请查看从服务器的master.info看配置是否正确,也可以查看从服务器的error-log看是什么原因。如果是初次同步,必要的时候,可以删除掉master.info文件,并重启从数据库以强制更新。

2011-7-21 增加:

master.info里记录了master的相关信息,比如hostname、user、password,还有当前master的binlog名称、slave当前操作到的master binlog的pos偏移位置。

这个文件,在同步开始之后,就不要删除了,否则会丢失slave当前操作到的master binlog的pos偏移位置!!

另外,master、slave的角色是相对的,可以做master-master备份,即互为主从备份(写逻辑需要注意)。也可以将一台master作为其他数据库服务器的slave。但是,一台slave不可以拥有多个master!

2011-9-20增加

数据库同步帐号和正常代码访问帐号,最好分开。

在做数据库迁移的过程中,就可以禁止代码访问帐号写入数据,这样就可以避免主从同步中的数据冲突了。

2012-1-9增加:

当slave出错时,可以先处理完错误,然后:

SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;    start slave;

web页面显示 Mysql connect fail, too many connections。

web server用的是lighttpd,查看其状态:http://hostname/pma-server-status并没有太多的连接。

这时,尝试使用普通账号登陆失败:mysql -u mysql -p,依然报too many connections。

在mysql的官方手册上http://dev.mysql.com/doc/refman/5.5/en/too-many-connections.html可以看到,当连接数过多的时候,仅超级用户才能登陆,所以改用root用户连接mysql。

连接上之后,使用show processlist;命令查看当前的连接情况,发现101个sleep状态的查询。这里面存在的问题是:

1、作为一个生产数据库,将max_connection设为100(可以通过show variables like ‘max%’看到)有点太少

2、大量sleep状态的query是否正常?

作为临时解决方案,可以在mysql里kill掉一些sleep的query(mysql>kill  query_id)。也可以暂时更改其max_con数目和允许的连接时间(过小也不行,一次访问未结束就被kill掉了):

mysql> set global max_connections = 500;

mysql> set global wait_timeout = 1000;

为了彻底解决,需要修改mysql的配置文件,将max_connection调大一些,比如500,然后重启mysql。

当然,更重要的是查看代码中是否存在bug。

Continue reading ‘MySql too many connections解决方案’ »

最近工作需要,自己维护一个MySql的数据库,所在服务器的硬盘空间不是很大、性能也一般。

需求是:由于一些临时的数据分析,需要将N天的log日志格式化处理后,导入到数据库,进行一定的分析计算之后,就可以全部清除了。

一些常用的命令整理如下(我记性很差,经常把各个数据库之间的命令记混):

启动数据库服务

sudo  -u mysql /usr/bin/mysqld_safe &  # 以mysql用户身份,在后台运行

之后可以看到有两条mysqld相关的进程:

mysql    19370  0.0  0.0  88908  1424 pts/0    S    10:03   0:00 /bin/sh /usr/bin/mysqld_safe

mysql    19402  2.9  3.4 187812 71204 pts/0    Sl   10:03   7:18 /usr/sbin/mysqld –basedir=/usr –datadir=/ms/var/mysql –pid-file=/var/run/mysqld/mysqld.pid –skip-external-locking –port=3306 –socket=/var/run/mysqld/mysqld.sock

由于启动时没有指定任何参数,所以basedir等都是从my.cnf文件中读取的默认配置。

停止数据库服务

sudo -u mysql mysqladmin shutdown -u username-p’passwd’

mysql客户端连接

mysql -u username-p’passwd’ [dbname]

为mysql添加用户

mysql> grant all privileges on *.* to ‘username’@'localhost’ IDENTIFIED BY ‘passwd’ WITH GRANT OPTION;

Query OK, 0 rows affected (0.00 sec) # 从localhost连接到数据库的

mysql> grant all privileges on *.* to ‘username’@'%’ IDENTIFIED BY ‘passwd’ WITH GRANT OPTION;

Query OK, 0 rows affected (0.00 sec) #从任意host连接到数据库的

grant all privileges on *.* to ‘mysql’@'192.168.80.0/255.255.255.0′ identified by ‘password’ with grant option; # 从C类子网192.168.80.0连接的

查看当前运行的sql句子进程,删除指定Id的sql句子

mysql> show  full processlist;
mysql> kill 15;

清除mysql log

(因为仅drop掉表,其log还是很很很占空间的!)

mysql> show binary logs;

mysql> purge binary logs to ‘mysqld-bin.000013′; # 不删除13

临时文件无法写入

当执行耗内存的sql句子后,出现无法写入tmp临时文件的时候,可以看下目标dir是否有空闲空间,必要时可以清理,或者写个my.cnf的tmpdir,指向空间较大的磁盘分区。

Continue reading ‘MySql数据库的初级管理’ »