2011年6月12日星期日

MySQL 连接

所谓连接查询,就是通过连接,使查询的数据从多个表中检索取得。在 SELECT 的 FROM 子句中写上所有有关的表名,就可以得到由几个表中的数据组合而成的查询结果。连接条件可在 FROMWHERE 子句中指定,WHEREHAVING 子句可包含其搜索条件,以供进一步筛选连接之后的结果集。 目前可实现的连接有:自然连接(Natural Join),内连接(Inner Join), 外连接(Outer Join), 交叉连接(Cross Join) etc.

JOIN USING 可按照指定的列实现表的等值连接。设有两个表t1, t2 具有相同的列 a, b, c, d, 如果不是对全部相同列做连接,而是是对列 a, b 做连接,可写成 t1 JOIN t2 USING(a,b)
JOIN ON 可按照更一般性条件实现表的等值连接。eg: t1(a, b), t2(a, c), 可写成 t1 JOIN t2 ON t1.a = t2.a
USING 后用于连接的列,也可用保留字 ON 指定, eg: ... USIGN(a)... 等价于 ... ON t1.a = t2.a..., 使用两者在结果集上体现的不同请见注 1

示例表 t1, t2, 其数据如下:
[sql]
mysql> SELECT * FROM t1;
+---+------+
| a | b |
+---+------+
| 1 | a |
| 3 | c |
| 5 | e |
| 7 | g |
+---+------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM t2;
+---+------+
| a | c |
+---+------+
| 2 | b |
| 4 | d |
| 6 | f |
| 7 | g |
+---+------+
4 rows in set (0.00 sec)
[/sql]

自然连接

[sql]
mysql> SELECT * FROM t1, t2 WHERE t1.a = t2.a;
+---+------+---+------+
| a | b | a | c |
+---+------+---+------+
| 7 | g | 7 | g |
+---+------+---+------+
1 row in set (0.00 sec)
[/sql]

内连接

[sql]
mysql> SELECT * FROM t1 INNER JOIN t2 USING (a);
+---+------+------+
| a | b | c |
+---+------+------+
| 7 | g | g |
+---+------+------+
1 row in set (0.00 sec)
[/sql]
它等价于:SELECT * FROM t1, t2 WHERE t1.a = t2.a; or SELECT * FROM t1 INNER JOIN t2 ON t1.a = t2.a;

外连接

外连接是连接的扩展。一般连接操作的结果表由符合连接条件的匹配元组连接起来的新元组构成,其余不符合连接条件的非匹配元组则被丢弃。外连接允许在结果表中保留非匹配元组,空缺部分填以NULL。其作用是在做连接操作时避免丢失信息。外连接有 3 类:
1 左外连接(Left Outer Join)。连接运算谓词为LEFT [OUTER] JOIN,其结果表中保留左关系的所有元组。eg:
[sql]
mysql> SELECT * FROM t1 LEFT JOIN t2 USING(a);
+---+------+------+
| a | b | c |
+---+------+------+
| 1 | a | NULL |
| 3 | c | NULL |
| 5 | e | NULL |
| 7 | g | g |
+---+------+------+
4 rows in set (0.00 sec)
[/sql]
等价于:SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a;

2 右外连接(Right Outer Join)。连接运算谓词为RIGHT [OUTER] JOIN, 其结果表中保留右关系的所有元组。MySQL 对其的优化策略见注 2

3 全外连接(Full Outer Join)。连接运算谓词为FULL [OUTER] JOIN, 其结果表中保留左右关系的所有元组。 MySQL 中, FULL JOIN... USING的结果集和INNER JOIN ... USING的结果集相同,且 MySQL 不支持 FULL OUTER JOIN。eg:
[sql]
mysql> SELECT * FROM t1 FULL JOIN t2 USING(a);
+---+------+------+
| a | b | c |
+---+------+------+
| 7 | g | g |
+---+------+------+
1 row in set (0.00 sec)

mysql> SELECT * FROM t1 FULL OUTER JOIN t2 USING(a);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'OUTER JOIN t2 USING(a)' at line 1
[/sql]

交叉连接

交叉连接等同于做笛卡尔积。
[sql]
mysql> SELECT * FROM t1 CROSS JOIN t2;
+---+------+---+------+
| a | b | a | c |
+---+------+---+------+
| 1 | a | 2 | b |
| 3 | c | 2 | b |
| 5 | e | 2 | b |
| 7 | g | 2 | b |
| 1 | a | 4 | d |
| 3 | c | 4 | d |
| 5 | e | 4 | d |
| 7 | g | 4 | d |
| 1 | a | 6 | f |
| 3 | c | 6 | f |
| 5 | e | 6 | f |
| 7 | g | 6 | f |
| 1 | a | 7 | g |
| 3 | c | 7 | g |
| 5 | e | 7 | g |
| 7 | g | 7 | g |
+---+------+---+------+
16 rows in set (0.00 sec)
[/sql]
它等价于:SELECT * FROM t1, t2; or SELECT * FROM t1 INNER JOIN t2;


注:

  • USING 和 ON 显示的结果说明
    为了使 MySQL 在解析 NATURAL JOINJOIN ... USING SQL 时采用 SQL 2003 的标准,从 MySQL 5.0.12 开始,这两种 SQL 的解析都有做调整, NATURAL JOIN 时, 查询结果中只会显示单独的唯一一列, 即 t1.a, t2.2 两列做自然连接后在结果中只有一列 a (= COELSCE(t1.a, t2.a)); 而 JOIN ... USING时,对 USING 中 指定使用做连接的列,查询结果中也只会显示单独的唯一一列, JOIN ... USING形式的变体如 LEFT JOIN ... USING, RIGHT JOIN ... USING 等解析优化也是采用同样的处理。除此之后,其他的连接方式还是遵照以前的规则。详细说明,可查看如下引用部分或MySQL 官方文档 JOIN Syntax一节。

    Join Processing Changes in MySQL 5.0.12
    Beginning with MySQL 5.0.12, natural joins and joins with USING, including outer join variants, are processed according to the SQL:2003 standard. The goal was to align the syntax and semantics of MySQL with respect to NATURAL JOIN and JOIN ... USING according to SQL:2003. However, these changes in join processing can result in different output columns for some joins. Also, some queries that appeared to work correctly in older versions must be rewritten to comply with the standard.


    The single result column that replaces two common columns is defined using the coalesce operation. That is, for two t1.a and t2.a the resulting single join column a is defined as a = COALESCE(t1.a, t2.a), where:
    COALESCE(x, y) = (CASE WHEN V1 IS NOT NULL THEN V1 ELSE V2 END)
    If the join operation is any other join, the result columns of the join consists of the concatenation of all columns of the joined tables. This is the same as previously.

    [sql]
    mysql> SELECT * FROM t1 LEFT JOIN t2 USING (a);
    +---+------+------+
    | a | b | c |
    +---+------+------+
    | 1 | a | NULL |
    | 3 | c | NULL |
    | 5 | e | NULL |
    | 7 | g | g |
    +---+------+------+
    4 rows in set (0.01 sec)

    mysql> SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a;
    +---+------+------+------+
    | a | b | a | c |
    +---+------+------+------+
    | 1 | a | NULL | NULL |
    | 3 | c | NULL | NULL |
    | 5 | e | NULL | NULL |
    | 7 | g | 7 | g |
    +---+------+------+------+
    4 rows in set (0.01 sec)
    [/sql]

  • 2右外连接的优化策略
    右外连接 SQL 在解析阶段都会转换为只包含左外连接的 SQL,一般遵循如下这样的转换方式:

    T1, ...) RIGHT JOIN (T2,...) ON P(T1,...,T2,...) =
    (T2, ...) LEFT JOIN (T1,...) ON P(T1,...,T2,...)

    有关其详细信息,请参考 MySQL 的官方文档7.3.1.10. Outer Join Simplification

    At the parser stage, queries with right outer joins operations are converted to equivalent queries containing only left join operations. In the general case, the conversion is performed according to the following rule:
    T1, ...) RIGHT JOIN (T2,...) ON P(T1,...,T2,...) =
    (T2, ...) LEFT JOIN (T1,...) ON P(T1,...,T2,...)

    通过EXPLAIN EXTENDED, SHOW WARNINGS, 查看 MySQL 解析优化后的右外连接:
    [sql]
    mysql> EXPLAIN EXTENDED SELECT * FROM t1 RIGHT JOIN t2 USING (a);
    +----+-------------+-------+--------+---------------+---------+---------+-----------+------+----------+-------+
    | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+-------+--------+---------------+---------+---------+-----------+------+----------+-------+
    | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | |
    | 1 | SIMPLE | t1 | eq_ref | PRIMARY | PRIMARY | 4 | test.t2.a | 1 | 100.00 | |
    +----+-------------+-------+--------+---------------+---------+---------+-----------+------+----------+-------+
    2 rows in set, 1 warning (0.00 sec)

    mysql> SHOW WARNINGS;
    +-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Level | Code | Message |
    +-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | Note | 1003 | select `test`.`t2`.`a` AS `a`,`test`.`t2`.`c` AS `c`,`test`.`t1`.`b` AS `b` from `test`.`t2` left join `test`.`t1` on((`test`.`t1`.`a` = `test`.`t2`.`a`)) where 1 |
    +-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.00 sec)

    [/sql]

2011年6月10日星期五

May 2011 and Before Part III


  • 淘宝褚霸:leveldb性能分析和表现 http://t.cn/hDNi6Y

  • Nagios手册

  • grep 'test_string' filename.txt > /tmp/test.txt 表示将 filename.txt 中包含有 test_string 字符串行的定向输出到 /tmp 下 test.txt 文件中。

  • cp /usr/local/share/aclocal /usr/share

  • ps aux | grep 'application name'

  • 25 条 SSH 命令和技巧 [技巧] | Wow!Ubuntu http://t.cn/h4g30w

  • PDF 下载:最牛B的 Linux Shell 命令 [新闻] | Wow!Ubuntu http://t.cn/h47S1R

  • 石展:推荐神器:dstat,全能Linux系统信息#监控工具#,可直接替换vmstat, iostat, netstat ,nfsstat, ifstat等系统工具,监控cpu、disk 、mem、load、interrupt、net、proc、CS.... 界面见图也很美观. ubuntu下直接sudo apt-get install dstat ; redhat下wget http://t.cn/h1QrIy 安装即可 (tks Frost)

  • 淘宝褚霸:latencytop深度了解你的Linux系统的延迟 http://t.cn/hB2enj


  • 我对后端优化的一些感想关键字:优化, 响应时间,吞吐量,vmstat & iostat & netstat & tcprstat, strace & oprofile & systemtap, aspersa & latencytop & top, 空查询, Bloom filter etc。

  • 陈庆吉chenqj:写了两篇Facebook技术体系的介绍 http://t.cn/h4dkV0 http://t.cn/h4dkVR 都不算深入,大牛就别看了。

  • 唐福林:所谓服务,应该不分内外的 //@fishermen:对于内部调用者不能完全信任,至少增加一个内部appkey,便于跟踪、统计,同时在需要的时候可以轻松增加限制//@TimYang:内部公有服务如果没有管理机制哪些人调了确实很难跟踪,要升级或者纠错都找不到调用方在哪里。//@TimYang:淘宝的服务框架演变过程,林昊写的 http://t.cn/hGJ6wJ

  • Sina App Engine 数据存储服务架构 关键字:Apache, MySQL, PHP, Memecache etc, 有时间可看看。

  • IT技术博客大学习:《几种常见的基于Lucene的开源搜索解决方案对比》 几种常见的基于Lucene的开源搜索解决方案对比 ... http://t.cn/hBmVz5


  • 淘叔度:nginx-0.9.5里面把listen的backlog由511调整为-1了。原因:linux内核里面listen系统调用的backlog是int型,但是在里面处理时会把它先转换成一个unsigned int再和系统设置的最大backlog进行比较,取二者的最小值。所以-1保证了永远取系统的最大值。backlog本来就是个hint值,系统默认是128。

  • suxiaoyong:再接上条: 8. 使用redis作为队列; 9. 尽量存储json数据,减少序列化开销; 10. 使用Mysql HandleSocket作为key value存储,cpu显著下降; 11. 数据过滤等交java中间件去做,减少数据库操作复杂性; 12. sns feed对数据完整性要求不高,必要时可以丢弃舍弃部分数据。//@suxiaoyong:接上条: 4. 在数据sharding时,对名博和普通用户分别处理; 5. sharding时采用两级分组,加强灵活性; 6. 使用DRBD做mysql实时同步; 7. 内容分发时,对于当前在线用户,使用redis的list作为cache,解决频繁请求时的刷新问题(相对于memcached也减少了内部通讯数据量);//@suxiaoyong:今天飞信首席架构师 @steadwater 关于sns feed的分享很不错,总结了一下,大概这些内容,不对的地方请补充: 1. 消息异步写入; 2. 推拉结合,对于粉丝多的用户用拉,普通用户用推,展示数据时进行合并; 3. 普通用户的事件尽量保证实时性,名博可以异步处理;


  • 可视化的数据结构和算法

  • 林信良常见算法笔记


  • ReflectionMethod PHP class, 获得指定的类的所有方法信息,详细信息,可PHP官方文档

  • 时蝇喜箭:深入理解PHP内核(TIPI)项目,也参考cnbeta文章: http://t.cn/IDa34O //@黄药师DE极致:转发微博@KimiChen:TIPI是一个开源项目,项目的初衷是为了分享有关PHP内部实现的方方面面,详情请点击http://t.cn/htWk9Y,强烈推荐之!

  • Faster Ruby


  • Node JS 资料

  • 我为什么向后端推荐 Node JS

  • node.js调研与服务性能测试


  • 惊喜就在这里

  • 关于前端 无意中发现的这个网站。

  • IE 6 调用 gzip 压缩后 Javascript 不能执行的问题, 出现这个 BUG 有可能是:服务器没有设置被请求文件(javascript)expries,cache-control; 服务器开启了 chunked encoding 模式; 通过 javascript 的 src 调用方式或者 AJAX 请求 javascript 文件等,解决是header 中:
    Cache-Control必须设置为 maxage=time 的格式, maxage 是指生存缓存生效时间, 1 为 1 秒;
    Expires 必须要设置,它的时间稍大于 Date(页面请求时间)即可;
    Pragma 必须设置为 public。

  • @Huihoo:Graffiti Markup Language (.gml) http://t.cn/hnJVu 涂鸦标记语言是基于XML的开放文件格式,用于存储涂鸦数据。互联网上很需要有一个大大的涂鸦墙,让我们有绘画的自由,思想表达的自由,精神发泄的自由。

  • 2010-12-30 MySQL 5.5正式版发布。

  • CSDN云计算:2011-03-17,据MongoDB官网消息,MongoDB v1.8.0已经发布。这是在2010年8月1.6版发布后的又一个版本。此次1.8版修复以往版本中的一些错误,并加入了许多新特性。 http://t.cn/IDtsI5

  • 淘叔度:Nginx是穷人家的孩子,品学兼优,任劳任怨,年级成绩第一的保持者,月薪要求1千块;Apache是地主家的孩子,多才多艺,班上的优秀学生干部,但抗压能力不够,月薪要求1万块;JBoss是富二代,后台很硬,见多识广,不能承受高压力且工作效率也不高,月薪要求3万块。作为老板的你会聘用哪个?

  • ora600ebay:DBA 1.0 掌握某主流商用RDBMS,例如Oracle。 DBA 2.0 = DBA 1.0+OS+Storage+Network+Application。 DBA 3.0 = DBA 2.0+MySQL (or Postgres etc)+NoSQL+Programmer。DBA 1.0/2.0 是Database management oriented, 而DBA 3.0是Data service oriented. DBA 1.0/2.0 是应用技术,DBA 3.0是创造技术。

  • jackbillow:Twitter也从syslog-ng转向了scribe,使用Hadoop来存储和分析数据。

  • 程序员幽默:【对程序员最具影响的,每个程序员都应该阅读的书籍】第1名《代码大全》第2名《程序员修炼之道》第3名《计算机程序的构造和解释》第4名《C程序设计语言》第5名《算法导论》第6名《重构:改善既有代码的设计》第7名《人月神话》第8名《设计模式》....全文见图或http://t.cn/h1HKmz

May 2011 and Before Part II



  • MangoDB doc

  • Foursqure: 使用 MongoDB Replica Sets 的三种架构
    在原有的 Master/Slave 机制上添加一台 arbiter;
    一个 Primary 用于写,多个 Secondary 用于读和一个 Secondary 用于备份;
    MongoDB 经典配置,上层是 Auto-Sharding, 每个 Sharding 结点 又是一个 Replica Sets。

  • tullyliu:内存占完后的,插入性能完全处于不可预测的状态,甚至会时快时慢,这个实在太无法接受了//@NinGoo:在@realzyy 的测试中,mongodb数据超过内存需要用到磁盘的时候,写入性能会有很大的下降,这是mmap的方式可以预期的。问题在于,每隔10s左右会有一个几乎跌到0的波动,原因尚待查证。



  • redis 运维的一些知识点

  • TimYang:1台机启动了2个Redis端口,另外两台机来测试写100字节,1台client跑到7万/秒, 一台client跑到9万/秒,加起来这台服务器每秒跑了16万次, 99.9%请求小于1ms。

  • Redis指令中文手册

  • Redis 容量及使用规划

  • TimYang:squid以文件操作为主,主要目的是静态化,当年开发时操作系统虚拟内存概念也没成熟。而Redis整个架构就是内存模型,所以如果app只使用操作系统内存,让操作系统来决定哪些是冷门的再swap,设计上是最自然的 //@童剑:主要看作者实现的如何了,Squid 不也是自己实现对象从内存到磁盘的交换吗,也挺好的。//@TimYang:再说下Redis数据需要全部放在RAM的问题,一个系统中热点数据的访问占总量一般小于20%,如果RAM要为这大部分冷门数据买单则是一种极大浪费。Redis VM设计思想就是把冷门数据交换到磁盘上,为热点数据腾出更多RAM空间。不过Redis绕过OS自己实现一套虚拟内存也具争议性。

  • TimYang:嗯,我的问题,强设了一下 echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse phpredis 每秒也能跑个1万多次 //@淘一啸:回复@淘叔度:多谢指点,一直没有认真研究状态图中的异常情况。不知道有没有方法能够通过程序来逐个验证。 //@淘叔度:我之前写过的一篇文章供参考:http://t.cn/hGjHeL//@TimYang:Which PHP Library to use with Redis? The Benchmark, 赢家是phpredis http://t.cn/hGlxWf。

  • yangwm:一个不错的redis presentation, http://t.cn/hGvsSl

  • TimYang:The (Redis) cluster project is currently not ready: even if we have a pretty clear design, and many networking level code for gossip and failure detection, it will take some more month to be released, and more time to be a stable release. 这就是用C开发分布式网络程//@摇摆巴赫:redis 作者新博客提出presharding的临时解决方案,早在这篇博客之前,杨教授 @TimYang 也提出过MPSS也正是这个想法,hash is easy,rehash is hard,我们可能每天都碰到类似的问题 here's the simple solution , http://t.cn/h5G4nQ

  • iammutex:深入Redis内部-Redis 源码讲解 http://t.cn/h5dG1r 非常好的讲 Redis 内部实现及源码的文章~原文:http://t.cn/htpyGq

  • ackbillow:通过快照方式做replication在master数据量大时,整个过程会遇到一些问题,同时每次连接都才需要完整的一个快照。//@丹臣:今天配置了redis replication复制环境,redis salve异常挂掉,因为master有许多变更没有接收到,当slave重启后,会与master进行一次全同步,来保障数据的一致性。我实验的数据量很小,同步相当快。

  • 丹臣:@内涵帝,hset,减少了key储存的个数,在实际使用过程中,同一个key多个value值本来就要序列化的,一个key也只存一次的,并且value变长了,这样所需要的hash table管理结构就会减少,自然管理消耗的memory就会减少。本次测试用例是,数据长度太小,条数太多,导致管理空间成本很大//@丹臣:在有内存的情况下,redis每秒可以达到5w次tps get key[10bytes] value[10bytes],但19148251万object*(10bytes key length+10bytes value length)/1024/1024=365M,而redis-master用了3.3G的内存,管理结构成本相当大。http://t.cn/h1njF8

  • TimYang:更快的io设备写snapshot,其他都不是瓶颈。另外重启加载速度跟cpu相关。//@Birkoff:对@timyang 说:请教一下TimYang,Redis不开启VM的情况下,性能更多的取决于?1:更高频率的CPU 2:更大的CPU L2Cache 3:更快的内存?如何才能最大限度的发挥多核CPU的特性,提高Redis的处理能力?


  • TimYang:虽然LinkedIn经常发垃圾邮件,但是对其技术还是挺尊敬,Kafka, LinkedIn开源的pubsub的消息队列系统,其news feed也跑在这个系统上。 http://t.cn/hbjQIb。

May 2011 and Before Part I


  • ALTER TABLE tbl_name DROP PRIMARY KEY;, DROP INDEX idx_name ON tbl_name

  • MySQL HandleSocket技术在 SNS Feed存储中的应用

  • 为 MySQL 设置查询超时

  • Monitoring MySQL IO Latency with performance_schema

  • The fource categories of NoSQL databases

  • How to handler 1000's of concurrent users on a 360MB VPS

  • FriendFeed Use MySQL 虽然已经是比较老的文章了,但还是值得一看。

  • MySQL 允许驼峰形式命令,my.cnf 中 mysqld 部分加入lower_case_table_names=2, 官方文档

  • MySQL binlog的地雷 5.1版本后,MySQL引入了基于ROW方式的binlog格式,不同于Statement方式的是,ROW方式记录了变更的内容,而不仅仅是SQL。

  • hellodba:推荐:《面向程序员的数据库访问性能优化法则》,作者是我们部门的架构师,即浅显易懂,又很有深度。http://t.cn/hbxlM9

  • 淘宝褚霸:MySQL的IO调优是个大工程,从Mysql->Innodb->Filesystem->Page/buffer->IO 调度器->raid一整条线路。

  • skip-name-reslove

  • 唐福林:UTF-8 String, mysql client latin1, mysql connect latin1, mysql db table utf-8, 存进去的中文,jdbc 怎么调参数,查出来还是乱码。 @Timyang 指示:convert(unhex(hex(convert(name using latin1))) using utf8) as name 太神奇了!

  • 时蝇喜箭:【关注】Stack Overflow 架构更新 (每月PV到9千5百万)http://t.cn/httWMr 有一节关于经验教训的,令人注目的一条:“Full Text Search in SQL Server is very badly integrated, buggy, deeply incompetent, so they went to Lucene.”

  • sagasw大连程序员 :分享 Designing for Error (2) http://t.cn/h5iVdJ http://t.cn/htwNUo

  • jackbillow:Nick Kallen提到的几个设计思想:1. 没有银弹,所有的解决方案都是暂时的。2.可扩展性涉及分区,索引(不仅仅指的db中key的概念)和复制。3.所有的数据都在内存里,磁盘仅仅是落地。4.预估能解决一些问题,但很多问题也不是前期设计能预料的。5.尽可能的使用本地化策略譬如:cache本地化或就近访问。

  • ylinn:回复@NinGoo:可以设置直接使用remote 内存,避免swap //@NinGoo:间在mysql等大内存环境下,NUMA个节点间的内存使用难以均衡,在其他节点还有可用内存的情况下,某个节点内存不足也可能导致swap的产生,而swap是数据库头疼的大问题之一。//@Fenng:NUMA架构对DB产生的问题,是MySQL扩展性的绊脚石之一

  • FusionioChina:Facebook 的mySQL 大拿关于Mastering innoDB diagnostics. http://t.cn/hdmJES

  • 唐福林:原来 memcache 也就只检查 50 个key,学习了//@唐福林:@zhangwei217245 @摇摆巴赫 @TimYang Redis 默认的 LRU 算法,只检查 3 个key,从中间挑一个最长时间未使用的就删除?//@唐福林:Redis这么使用LRU? LRU and minimal TTL algorithms are not precise algorithms but approximated algorithms (in order to save memory), so you can select as well the sample size to check. For instance for default Redis will check three keys and pick the one that was used less recently

  • fengyuncrawl:#数据分布算法#1)Round-Robin法 2)Hash法 3)Range法 4)Replicate法。目前还没有一种数据分布方法能够优化所有情况的数据查询,另外,就算能找到一种良好的数据分布策略在系统初始的情况下达到最优,但是随着系统的运行,数据不断更新这种最优平衡一定会被打破。根据实际情况选择合适动态分布策略

  • tb丁原:同等硬件环境下,相比redis,tair,search,oceanbase,hbase等,oracle,mysql性能看起来总是差了一些,其实不管什么软件,设计思路上不外乎内存读写,顺序读写,顺序写随机读,随机写顺序读,随机读随机写这几种,还能有什么?

  • 微软中国MSDN:【批量操作SQL数据】我们在已经建好触发器的表中对数据进行一个批量更新操作的时候,系统会提示我们无法完成操作,这个时候就需要我们先将触发器停掉,然后再进行UPDATE操作,完后再开启触发器。alter table tablename disable trigger all 批处理的SQL语句 alter table tablename enable trigger all。

2011年6月3日星期五

设计模式 and OOD

推荐两篇值得仔细阅读的关于架构方面的文章,都是出自 Code Porject 上的 Al-Farooque Shubho 之手。


这两篇文章,倪大虾 同学已经翻译,看中文,移步这里:

2011年6月2日星期四

MySQL 压力测试工具 mysqlslap

FROM: MySQL 压力测试工具 mysqlslap

从 5.1.4 开始,MySQL 自带有一个压力测试工具 mysqlslap, 它通过模拟多个并发客户端访问 MySQL 来执行测试,使用起来非常简单。通过mysqlslap --help可以获得可用的选项,这里列一些主要的参数,更详细的说明参考官方手册

  • --auto-generate-sql, -a 自动生成测试表和数据。

  • --auto-generate-sql-load-type=type 测试语句的类型。取值包括:read,key,write,update和mixed(默认)。

  • --number-char-cols=N, -x N 自动生成的测试表中包含多少个字符类型的列,默认 1。

  • --number-int-cols=N, -y N 自动生成的测试表中包含多少个数字类型的列,默认 1。

  • --number-of-queries=N 总的测试查询次数(并发客户数×每客户查询次数)。

  • --query=name,-q 使用自定义脚本执行测试,eg: 可以调用自定义的一个存储过程或者 SQL 语句来执行测试。

  • --create-schema 指定测试的数据库。

  • --commint=N 多少条 DML 后提交一次。

  • --compress, -C 如果服务器和客户端支持都压缩,则压缩信息传递。

  • --concurrency=N, -c N 并发量,也就是模拟多少个客户端同时执行 SELECT。可指定多个值,以逗号或者--delimiter参数指定的值做为分隔符。

  • --engine=engine_name, -e engine_name 创建测试表所使用的存储引擎,可指定多个。

  • --iterations=N, -i N 测试执行的迭代次数。

  • --detach=N执行 N 条语句后断开重连。

  • --debug-info, -T打印内存和CPU的信息。

  • --only-print 只打印测试语句而不实际执行。

  • --defaults-file=mysql_configuration_file_directory 配置文件存放位置。

  • --socket=socket_directory, -S socket_directory socket文件位置。



测试的过程需要生成测试表和测试数据,mysqlslap 会自动生成一个名为 mysqlslap 的 schema,如果已经存在则先删除。可用--only-print来打印实际的测试过程。
[bash]
[root@localhost bin]# /usr/local/mysql/bin/mysqlslap -a --only-print
[/bash]

DROP SCHEMA IF EXISTS `mysqlslap`;
CREATE SCHEMA `mysqlslap`;
use mysqlslap;
CREATE TABLE `t1` (intcol1 INT(32) ,charcol1 VARCHAR(128));
INSERT INTO t1 VALUES (1804289383,'mxvtvmC9127qJNm06sGB8R92q2j7vTiiITRDGXM9ZLzkdekbWtmXKwZ2qG1llkRw5m9DHOFilEREk3q7oce8O3BEJC0woJsm6uzFAEynLH2xCsw1KQ1lT4zg9rdxBL');
......
DROP SCHEMA IF EXISTS `mysqlslap`;

可以看出,最后会删除一开始创建的 schema,so, 整个测试完成后不会在数据库中留下痕迹。
假如执行一次测试,分别模拟 50 和 100 个并发,都执行 1000 次查询,那么:
[bash]
[root@localhost bin]# /usr/local/mysql/bin/mysqlslap -a --concurrency=50,100 --number-of-queries=1000 --debug-info
[/bash]

Benchmark
Average number of seconds to run all queries: 0.676 seconds
Minimum number of seconds to run all queries: 0.676 seconds
Maximum number of seconds to run all queries: 0.676 seconds
Number of clients running queries: 50
Average number of queries per client: 20

Benchmark
Average number of seconds to run all queries: 0.922 seconds
Minimum number of seconds to run all queries: 0.922 seconds
Maximum number of seconds to run all queries: 0.922 seconds
Number of clients running queries: 100
Average number of queries per client: 10


User time 0.17, System time 0.40
Maximum resident set size 0, Integral resident set size 0
Non-physical pagefaults 1653, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 14740, Involuntary context switches 6135

Voluntary context switches 7319, Involuntary context switches 681

上结果可以看出,50 和 100 个并发分别得到一次测试结果(Benchmark),并发数越多,执行完所有查询的时间越长。为了准确起见,可以多迭代测试几次:
[bash]
[root@localhost bin]# /usr/local/mysql/bin/mysqlslap -a --concurrency=50,100 --number-of-queries=1000 --iterations=5 --debug-info
[/bash]

Benchmark
Average number of seconds to run all queries: 0.833 seconds
Minimum number of seconds to run all queries: 0.803 seconds
Maximum number of seconds to run all queries: 0.865 seconds
Number of clients running queries: 50
Average number of queries per client: 20

Benchmark
Average number of seconds to run all queries: 0.980 seconds
Minimum number of seconds to run all queries: 0.948 seconds
Maximum number of seconds to run all queries: 1.007 seconds
Number of clients running queries: 100
Average number of queries per client: 10


User time 1.03, System time 2.35
Maximum resident set size 0, Integral resident set size 0
Non-physical pagefaults 7148, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 115255, Involuntary context switches 4125


测试同时不同的存储引擎的性能进行对比:
[bash]
[root@localhost bin]# /usr/local/mysql/bin/mysqlslap -a --concurrency=50,100 --number-of-queries=1000 --iterations=5 --engine=myisam,innodb --debug-info
[/bash]

Benchmark
Running for engine myisam
Average number of seconds to run all queries: 0.781 seconds
Minimum number of seconds to run all queries: 0.766 seconds
Maximum number of seconds to run all queries: 0.800 seconds
Number of clients running queries: 50
Average number of queries per client: 20

Benchmark
Running for engine myisam
Average number of seconds to run all queries: 0.908 seconds
Minimum number of seconds to run all queries: 0.880 seconds
Maximum number of seconds to run all queries: 0.929 seconds
Number of clients running queries: 100
Average number of queries per client: 10

Benchmark
Running for engine innodb
Average number of seconds to run all queries: 0.897 seconds
Minimum number of seconds to run all queries: 0.858 seconds
Maximum number of seconds to run all queries: 0.929 seconds
Number of clients running queries: 50
Average number of queries per client: 20

Benchmark
Running for engine innodb
Average number of seconds to run all queries: 1.189 seconds
Minimum number of seconds to run all queries: 1.086 seconds
Maximum number of seconds to run all queries: 1.306 seconds
Number of clients running queries: 100
Average number of queries per client: 10


User time 2.02, System time 4.24
Maximum resident set size 0, Integral resident set size 0
Non-physical pagefaults 14556, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 172917, Involuntary context switches 52011

100 个并发时,InnoDB 引擎,运行 1000 句 SQL 平均要需要 1.189 秒;而对于 MyISAM, 为 0.908 秒。

测试自定义 SQL:
[bash]
[root@localhost bin]# /usr/local/mysql/bin/mysqlslap --defaults-file=/usr/local/mysql/etc/my.cnf --create-schema=hstestdb --concurrency=50,100 --number-of-queries=1000 --iterations=5 --query="SELECT * FROM hstestdb.hstesttbl WHERE k='k2'" --debug-info -u root -p -S /usr/local/mysql/tmp/mysql.sock
Enter password:
[/bash]

Benchmark
Average number of seconds to run all queries: 0.271 seconds
Minimum number of seconds to run all queries: 0.256 seconds
Maximum number of seconds to run all queries: 0.288 seconds
Number of clients running queries: 50
Average number of queries per client: 20

Benchmark
Average number of seconds to run all queries: 0.324 seconds
Minimum number of seconds to run all queries: 0.292 seconds
Maximum number of seconds to run all queries: 0.333 seconds
Number of clients running queries: 100
Average number of queries per client: 10


User time 0.22, System time 0.63
Maximum resident set size 0, Integral resident set size 0
Non-physical pagefaults 4355, Physical pagefaults 0, Swaps 0
Blocks in 0 out 0, Messages in 0 out 0, Signals 0
Voluntary context switches 16691, Involuntary context switches 2837

PHP extension for interfacing with MySQL Handler Socket

有关于 HandlerSocket 的介绍、性能及其安装,可参考Using SQL as NoSQL。而 PHP extension for interfacing with MySQL Handler Socket,实际上这里php-handlersocket有整体的介绍,包括其安装、使用方法。现在纯粹是因为自己测试时犯了一很基础的错误,所以,罚自己多敲点字。

安装


[bash]
[root@localhost php-handlersocket]# /usr/local/php/bin/phpize
[root@localhost php-handlersocket]# ./configure --with-php-config=/usr/local/php/bin/php-config
[root@localhost php-handlersocket]# make
[root@localhost php-handlersocket]# make install
[/bash]
说明:
1 编译时需要 libhsclient 库(libhsclient - HandlerSocket client library)。
2 安装成功时,在 PHP 的 extension dir 生成一名为 handlersocket.so,将extension=handlersocket.so加入 php.ini, 重启 PHP 服务。

HandlerSocket Class methods


HandlerSocket::construct

创建一 HandlerSocket Object。
[php]
HandlerSocket::__construct ( string $host, string $port [, array $options ] )
[/php]
参数:

  • $host MySQL 服务器 host name。

  • $port HandlerSocket 的端口地址。


返回值:
返回 HandlerSocket Object。

HandlerSocket::openIndex

在对数据库表做任何的增删改查操作前,必须先选择一索引。
[php]
public bool HandlerSocket::openIndex ( int $id, string $db, string $table, string $index, string $fields )
[/php]
参数:

  • $id HandlerSocket ID; 1 SELECT, 2 UPDATE, 3 INSERT, 4 DELETE。

  • $db 数据库名

  • $table 表名

  • $index 索引名, 可以是手动创建的索引名。这个参数可为空,一般指定时是用于 SELECT,eg: 指定为主键:HandlerSocket::PRIMARY

  • $fields 字段名(多个字段名,用逗号分隔),可为空。


返回值:
成功时返回 TRUE, 反之亦然。

HandlerSocket::executeSingle

在表上做增删改查操作。
[php]
public mixed HandlerSocket::executeSingle ( int $id, string $op, array $fields [, int $limit, int $skip, string $modop, array $values, array $filters, int $invalues_key, array $invalues ] )
[/php]
参数:

  • $id HandlerSocket ID; 1 SELECT, 2 UPDATE, 3 INSERT, 4 DELETE。

  • $op 操作符,有如下可选项, '=', '>=', '<=', '>', '<', '+'。

  • $fields 查询中所用到的字段,数组,其长度必须等于或小于指定的列数。

  • $limit 最多影响的行数(最开始根据这个函数名称有在怀疑这个参数,测试时发现,如果存在满足条件的多条记录时,会根据这个参数指定的值返回记录数)。

  • $skip 在检索记录前忽略掉的行数。

  • $modop 指定修改操作,可选值:'U', 'D'。

  • $values 数组,用于做 UPDATE 操作时指定修改的值。

  • $filters 过滤的选项。

  • $invalues_key ? (enabled : 0 / disabled : -1).

  • $invalues IN options


返回值:
返回做对应操作时的执行结果。

HandlerSocket::executeMulti

在一次调用中执行多个操作,即多个 HandlerSocket::executeSingle 的合并。
[php]
public mixed HandlerSocket::executeMulti ( array $requests )
[/php]
参数:

  • $requrest 多组 executeSingle 参数,用数组的形式体现。


注意:
等同于:HandlerSocket::executeSingle($requests00, $requests01, ...), HandlerSocket::executeSingle($requests10, ...) ...
返回结果:
返回做对应操作时的执行结果。

HandlerSocket::executeUpdate

To update a record from a table using an index.
[php]
public mixed HandlerSocket::executeUpdate ( int $id, string $op, array $fields, array $values [, int $limit, int $skip, array $filters, int $invalues_key, array $invalues ] )
[/php]
参数:

  • $id HandlerSocket ID; 2 UPDATE 。

  • $op 操作符,有如下可选项, '=', '>=', '<=', '>', '<', '+'。

  • $fields 查询中所用到的字段,数组,其长度必须等于或小于指定的列数。

  • $values UPDAET 时指定修改的值。

  • $limit 最多影响的行数。

  • $skip 在检索记录前忽略掉的行数。

  • $filters 过滤的选项。

  • $invalues_key ? (enabled : 0 / disabled : -1).

  • $invalues IN options


注意:
等同于:HandlerSocket::executeSingle($id, $op, $fields, $limit, $skip, 'U', $values, $filters, $invalues_key, $invalues)
返回值:
返回做对应操作时的执行结果。

HandlerSocket::executeDelete

To delete a record from a table using an index.
[php]
public mixed HandlerSocket::executeDelete ( int $id, string $op, array $fields [, int $limit, int $skip, array $filters, int $invalues_key, array $invalues ] )
[/php]
参数:

  • $id HandlerSocket ID; 4 DELETE 。

  • $op 操作符,有如下可选项, '=', '>=', '<=', '>', '<', '+'。

  • $fields 查询中所用到的字段,数组,其长度必须等于或小于指定的列数。

  • $limit 最多影响的行数。

  • $skip 在检索记录前忽略掉的行数。

  • $filters 过滤的选项。

  • $invalues_key ? (enabled : 0 / disabled : -1).

  • $invalues IN options


注意:
等同于:HandlerSocket::executeSingle($id, $op, $fields, $limit, $skip, 'D', NULL, $filters, $invalues_key, $invalues)
返回值:
返回做对应操作时的执行结果。

HandlerSocket::executeInsert

To insert a record from a table using an index.
[php]
public mixed HandlerSocket::executeInsert ( int $id, array $values )
[/php]
参数:

  • $id HandlerSocket ID; 3 INSERT 。

  • $values HandlerSocket::openIndex 指定的字段参数所对应的值,但是以数组的形式体现。


注意:
等同于:HandlerSocket::executeSingle($id, '+', $values, 0, 0, NULL, NULL, NULL) ,第三个参数中指定的值必须和在此之前调用 HandlerSocket::openIndex 时第五个参数指定的字段对应。
返回值:
返回做对应操作时的执行结果。

HandlerSocket::getError

取得最近一次的错误信息。
[php]
public string HandlerSocket::getError ( void )
[/php]
返回值:
返回最近的错误信息(时间上)。

Example


测试表 schema:
[sql]
CREATE TABLE `hstesttbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`k` char(6) DEFAULT NULL,
`v` char(6) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_hstesttbl_k` (`k`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
[/sql]

[php]
$host = 'localhost';
$port = 9998;
$port_wr = 9999;
$dbname = 'hstestdb';
$table = 'hstesttbl';

//GET
$hs = new HandlerSocket($host, $port);
if (!($hs->openIndex(1, $dbname, $table, HandlerSocket::PRIMARY, 'k,v'))) {
echo $hs->getError(), PHP_EOL;
die();
}

$retval = $hs->executeSingle(1, '=', array('k1'), 1, 0);
var_dump($retval);

$retval = $hs->executeMulti(
array(
array(1, '=', array('k1'), 1, 0),
array(1, '=', array('k2'), 1, 0)
)
);
var_dump($retval);
unset($hs);


//UPDATE
$hs = new HandlerSocket($host, $port_wr);
if (!($hs->openIndex(2, $dbname, $table, '', 'v'))) {
echo $hs->getError(), PHP_EOL;
die();
}

if ($hs->executeUpdate(2, '=', array('k1'), array('V1'), 1, 0) === false) {
echo $hs->getError(), PHP_EOL;
die();
}

unset($hs);


//INSERT
$hs = new HandlerSocket($host, $port_wr);
if (!($hs->openIndex(3, $dbname, $table, '', 'k,v'))) {
echo $hs->getError(), PHP_EOL;
die();
}

if ($hs->executeInsert(3, array('k2', 'v2')) === false) {
echo $hs->getError(), PHP_EOL;
}
if ($hs->executeInsert(3, array('k3', 'v3')) === false) {
echo 'A', $hs->getError(), PHP_EOL;
}
if ($hs->executeInsert(3, array('k4', 'v4')) === false) {
echo 'B', $hs->getError(), PHP_EOL;
}

unset($hs);


//DELETE
$hs = new HandlerSocket($host, $port_wr);
if (!($hs->openIndex(4, $dbname, $table, '', ''))) {
echo $hs->getError(), PHP_EOL;
die();
}

if ($hs->executeDelete(4, '=', array('k2')) === false) {
echo $hs->getError(), PHP_EOL;
die();
}
[/php]

PS: 因为建立测试表时忘记指定存储引擎为 InnoDB, 测试 INSERT 操作时,怎样都是失败。后面为了验证问题的出处,用 perl 的 API 做同样的测试操作,结果也是失败。查看表结构后,修改储存引擎为 InnoDB,才成功。只是这个问题的错误信息太难理解,就几个数字,在没找到答案之前,害我还去查看了下 HandlerSocket 的源代码,当然,没有从中得到任何的提示。