[精华]MySQL主从服务器的一些技巧

jackxiang 2008-11-17 11:43 | |
作者:老王

问题:主从服务器表类型的选择

一般的共识是主服务器使用innodb,从服务器使用myisam,以便各尽其能。

问题:主从服务器字段类型的选择

字段类型对于分页等操作有很大影响。主服务器一般是innodb,因为不涉及查询,所以可以使用varchar等来存储字符串来节省空间,从服务器一般是 myisam,因为涉及查询,所以必须在char和varchar之间仔细权衡,没有varchar, text, blob字段的表是静态表,反之是动态表,静态表的检索效率要比动态表好若干倍,一般来说,所有涉及大结果集的查询都应该尽可能保证在静态表上完成,这里说一个例子:比如说常见的articles表有title(varchar), body(text)等字段,在做文章列表的时候,因为不是静态表,所以查询不会很快,下面开始重构解决方案:把原来的articles表拆分成 subjects表和contents表,title字段设置为一个足够的char类型放在subjects表里,body字段还保持是text类型放到 contents表里,subjects和contents表之间的关系是一对多,这样,顺带着也方便的实现了多页文章的功能,而且更重要的是在查询文章列表的时候,操作都是在subjects静态表里完成,效率肯定会比前一种方案提升很多。

问题:主从服务器NOW()函数造成数据不一致

假设在主服务器上执行一条INSERT …. VALUES ( …, NOW()),那么在从服务器上也会同样执行一条的SQL语句,但是主从服务器各自的时间设置可能不一致(比如说时区不同),NOW()在两台服务器上的结果就可能不一致。解决方法是显而易见的,就是不要使用NOW(),时间的计算在应用程序里完成。这里介绍一个额外的小技巧:获得时间戳,和time() 相比,$_SERVER[‘REQUEST_TIME’] 少做了一次系统调用,不过是否合适要视客观情况而定。

问题:主从服务器读写分离时读操作失败

先重现一下问题:比如说添加一条新数据,添加成功后根据last_insert_id跳转到新添加数据的浏览页面。在此过程中添加新数据的操作是在主服务器上完成的,浏览新数据的操作实在从服务器上完成的,不过由于主从服务器间SQL同步存在延迟,所以当使用last_insert_id在从服务器上查询的时候,从服务器很可能还没有还没来得及同步到此记录,所以读操作失败。解决思路也不复杂,在代码里加入一个缓存层(可以使用memcached),新添加的数据都顺手放到缓存层里一份,新数据的读操作也先查询缓存层,这样就不会再有读操作失败的问题了,当然删除或者更新数据的时候也要顺带着处理好缓存数据,可以使用观察者模式来搞定。不过这样缓存方案只限于读取单一的记录,对于读取列表的记录的情况,则是无效的。

问题:主从服务器索引是否有必要保持一致

一般都是利用主从服务器完成读写分离,从服务器上进行读操作,主服务器进行写操作,这样的话,主服务器上仅保留主键,外键,唯一索引等必要的索引即可,以便保持数据合法性,而对于那些原本用于优化SELECT操作的索引,可以全部删除,如此的话主服务器的写操作效率会提升很多。

网友评论:
1   
网友:三马
  2008年11月11日 星期二 下午 08:31
受教:)

2   
匿名网友
  2008年11月12日 星期三 下午 10:39
牛!

3   
网友:phzzy
  2008年11月14日 星期五 上午 11:13
组号不要用 $_SERVER[‘REQUEST_TIME’],特别是接口里
如果后台进程调用这个接口,则没有这个值
用 time() 不会比 $_SERVER[‘REQUEST_TIME’] 多耗多少资源

4   
网友:yejr
  2008年11月14日 星期五 下午 01:43
问题:主从服务器NOW()函数造成数据不一致

早就没问题了。
另外,适合自己的业务模式才是最合理的,太古董的东西最好标明发帖时间。

9   
网友:老王
  2008年11月14日 星期五 下午 04:00
@yejr:我查阅了一下官方文档,上面是这么说的:应在主服务器和从服务器上设置相同的系统时区。否则一些语句,例如使用NOW()或FROM_UNIXTIME()函数的语句,将不会正确复制。

http://dev.mysql.com/doc/refman/5.1/zh/replication.html

10   
网友:老王
  2008年11月14日 星期五 下午 04:24
@phzzy:我在命令行下调用,也是有$_SERVER[‘REQUEST_TIME’]的,不知道你说的是何种调用。

11   
网友:phzzy
  2008年11月14日 星期五 下午 05:16
说错了
是在 php 长期执行的进程里,比如server或daemon
任何时候取 $_SERVER[‘REQUEST_TIME’] 都是进程开始的时间,而不是当前时间

12   
网友:zeal
  2008年11月14日 星期五 下午 05:53
NOW()函数不会因为主从服务器之间同步的时间差而造成数据不一致,因为mysql会通过 seconds_behind_master 参数来自动进行校正。
当然前提是主从服务器的系统时间必须保持一致。

15   
网友:老王
  2008年11月14日 星期五 下午 08:34
@phzzy:确实,使用$_SERVER['REQUEST_TIME']要分清场合。

16   
网友:老王
  2008年11月14日 星期五 下午 08:34
@yejr, @zeal:我看到binlog里记录的是NOW()而不是实际的时间,就惯性的以为SQL同步延迟会使NOW()在主从服务器上出现差异,多谢提醒。

作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/1392/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2008-11-17 11:46
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]