2015年2月4日 谭俊青 没有评论

记得几年前跟未曾谋面的网友 黄晓强@Blizzard 在讨论Hash map hash函数值的唯一性(隐含分布均匀性)以及hash函数本身性能的时候,问及他是怎么做的,结论跟Bloom Filter的原理是一样的。

比如:从分布和唯一性来看,crc32比不上md5,更比不上sha1,但是性能是反过来的。而用于hashmap的hash函数对性能会产生直接影响,合理的hash函数能尽可能保持hash值的唯一性(不至于最后还要进行多次比较),还能保证足够的性能。

这里介绍 Bloom Filter 跟 黄晓强 他们的做法是一样的,就是利用多个简单高效的hash函数计算key的hash值,存入一个大的向量,如果测试一个key的hash值不匹配,那么这个key一定不存在,如果匹配,说明这个key是存在的(这里只是可能,所有通过hash值去判断唯一性都是不对的,只是概率问题)

JS实现: https://github.com/jasondavies/bloomfilter.js

分类: algorithm 标签:
2015年1月21日 谭俊青 没有评论

WEB开发当中获取用户的真实IP地址用的是REMOTE_ADDR (以下简称RA),而服务器上层往往会存在代理,比如Nginx,Netscaler,F5等设备,用户侧还可能有配置代理服务器,这样要获取用户的IP地址,业内通常的做法是使用HTTP_X_FORWARDED_FOR(以下简称XFF)来跟踪用户的IP地址。

然而带来方便的同时,给攻击者留下了可利用的漏洞,因为Header头是可以随意修改伪造的,也就是说攻击者可以随意伪造XFF,这也是很多网站在对IP进行限制(比如下单,注册,登录等)失效的原因。

为了安全,获取用户的IP地址只能使用RA,如果用户使用代理,那么RA就是代理服务器的IP地址,宁可错杀。上面提到的Nginx,Netscaler,F5等因为IP地址有限,可以在WEB服务器的插件中配置,比如Apache的RPAF,将用户的的“真实”IP地址,设置到RA地址中,从而让WEB程序跳过各级代理,获取到用户的IP地址。

到上面为止可以解决大部分场景的问题,但是因为中国的特殊情况,南北互通,各种网路问题,动态CDN有了市场。然后在使用动态CDN的过程中问题又来了,WEB程序获取的RA地址是CDN的IP地址,导致各种防刷机制失效,所以要想办法获取到用户的RA,而又不能引入新的安全问题,思考之后采取了下面的解决方案加以解决。

动态CDN供应商将用户的RA地址设置到XFF中(不管是多少层代理),服务端Proxy正常逐级增加,通过Apache的RPAF将白名单中的所有Proxy IP去掉,取最后一个IP地址,即位用户的RA地址。

OK,这里问题来了,如何判断用户的请求是通过CDN过来的,还是直接过来的请求,因为直接过来的请求有可能会伪造XFF,进而导致WEB程序获取到的RA是伪造的,进一步会规避防御策略。

所以为了区分,CDN过来的Header头加入特定指纹,在最上层代理配置,遇到特定指纹,XFF直接复制CDN传过来的XFF即可,如果没有指纹,那么将请求方的XFF+RA作为XFF传递下去,这样WEB应用程序拿到的RA即是没法伪造的用户“真实”IP地址(也可能是客户端的代理IP)。

分类: network 标签:
2015年1月20日 谭俊青 2 条评论

http://www.infoq.com/cn/presentations/tuniu-multiple-data-centers-distributed-database-synchronization

解决跨数据中心的数据同步和一致性问题,实现跨机房的高可用访问(比如中国特殊网络状况,提供多个数据中心的情况下,解决数据的可用性、一致性)。

分类: Go, MySQL Replication, database 标签: ,
2014年11月26日 谭俊青 没有评论

levelDB采用递增的数字作为版本号不适合大规模分布式系统用于底层存储使用,可以用当前的时间戳(微秒)作为版本号,这样在实现多版本控制的时候就非常方便了。

分类: MySQL 标签:
2014年10月18日 谭俊青 没有评论

这次参加Qcon全球软件开发大会

专题:扩展性、可用性、高性能
主题:多数据中心分布式数据库同步
http://2014.qconshanghai.com/user/702

核心思想是2地三机房的数据读写一致性,同时保证一定的性能。
基于类Paxos的算法,这里用的是Raft 还有MySQL的semi-sync Replication。
三机房是为了保证可用性以及防止出现脑裂现象
2地,可以保证足够的性能(其中2个机房在一个地方,延时小)

我最终的目标是想做出一款能够实现金融级的分布式可扩展的数据库,它应该具备:
准确性
强一致性(全局一致性需要做的工作很多,比如用物理时钟去保证)
高可用
可扩展(使用必须在一定规则下)
分布式事务(内部shard/chunk之间的分布式事务)

分类: database 标签:
2014年5月5日 谭俊青 没有评论

github上托管私有代码是要付钱的,于是决定自己搭建个git仓库,目前看起来不错的选择当gitlab莫属。
成果:https://git.mysqlab.net/

官方安装说明(环境是Debian/Ubuntu):
https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md

我自己的环境是CentOS,因此这里还是有些出入的,另外根据文档安装过程中遇到了一些问题,花费了整个晚上总算一一解决。

bundle, rake 命令可能需要使用绝对路径(eg:/usr/local/bin/bundle, 也可以加PATH,或者加软链)
/home/git/gitlab-shell/config.yml #如果用ssl,也需要修改这里 gitlab_url: “https://git.mysqlab.net/” 否则提交代码的时候会报错:fatal: Could not read from remote repository.
/home/git/gitlab/config/unicorn.rb # 如果采用ngixn配置反向代理使用 unix:socket,那么listen 端口监听建议注释掉,性能好,而且相对安全
/home/git/gitlab/config/unicorn.rb # gitlab.yml ,既然是私有代码托管,建议只用 https, 80端口默认302至443端口

PS: 常才河边走,哪有不湿鞋。这次mysql因为没有配置 skip_name_resolve 导致gitlab一直打不开,出现500错误。

分类: git 标签:
2013年5月15日 谭俊青 10 条评论

Golang1.1昨天发布,性能提升不少。让我想起去年写的mysql-proxy,于是拿出来又改了一下调用最为频繁的一个函数,发现性能已经非常理想。这里放出来给有兴趣的朋友试用。
目前支持的功能是读写分离(非事务,如果用事务的话不会分发到slave上)和slave的负载均衡,未做失效检查。另外prepare可能会有问题。
如果有什么需求可以后续添加,golang改起来非常方便,当初这个proxy我也就1个礼拜完成的。

下载地址:http://www.mysqlab.net/products/mysql-proxy/lbproxy64.tar.gz (linux 64位)
(有需要win版本的可以密我)

配置:
1. 需要配置用于连接proxy的用户名和密码(sha1(plain password))
2. 需要配置连接后端master和slave的用户和密码(sha1(plain password))
3. slaves需要按照proxy.ini 格式写,因为解析ini文件的目前还没有容错
(本来是打算配置用web界面来做的,被其他事情给耽搁了)

启动:
目前启动方式很山寨
cd /path/to/proxy && ./proxy
(这个容易改,我已经有现成的python manager,套用过来就行)

大家有什么反馈可以q我或者这里留言(第一次需要审核,防止垃圾广告。)

分类: Go, MySQL 标签:
2012年11月28日 谭俊青 2 条评论

之前的DMB系统,因为一直没有放出linux版本,使用范围比较窄。

这次应朋友们的要求,计划推出for linux单机版,暂时命名为LBOMP,现截图一张先睹为快。

后续推出的工具/系统 有:

MySQL多线程复制外挂
MySQL多主一从外挂
MySQL读写分离代理(这儿发布过
MySQL健康检查及高可用组件
大规模MySQL集中管理平台

分类: MySQL 标签: , ,
2012年6月1日 谭俊青 4 条评论

前段时间还在几个群里说我准备做数据库,跨数据中心实时同步关系型数据库,而且我域名都注册好了,LegendBase.org,名字就准备叫LegendBase的。

计划:

  • 实时同步:采用Paxos算法,推荐至少5台服务器(2F+1)
  • 高可用,避免像MySQL replication那样会有数据不一致的情况
  • 数据自动全局分布
  • 支持事务和snapshot(基于时间线)
  • 延时相对很高(因为Paxos,和事务2PC的原因)
  • 高并发高吞吐

想不到Google的动作这么块,它们的Megastore已经升级为F1了… 看功能和特性跟我上面提到的这些基本一致。
看新闻说Google已经在adwards上弃用MySQL,改用F1。

我在想,F1会开源吗,我的LegendBase还需要做吗?

2012年6月1日 谭俊青 3 条评论

lbmysqlping (LegendBase MySQL Ping) is a tool that does MySQL service availability check for MySQL HA solution.

Of course, it could be used to monitor MySQL servers availability. If some server down, LBMySQLping will run “Down” command that could send email or message to your mobile.

有不少朋友在做MySQL数据库高可用(Heartbeat,keepalived)和主从负载均衡(LVS,F5,A10等)的时候都面对MySQL服务可用性检测的问题.

检测可用性面对的几个问题不太容易解决:

  • 端口能连上,但是SQL语句很可能无法执行,其实这时候MySQL服务是不可用的
  • 连上了,SQL语句执行堵塞,长时间不能返回,因为MySQL是堵塞式的
  • MySQL Slave延时太大,应该从路由表里面清除

而且我了解到的有不少人都是用检测端口的方法,这是有风险的.

今天贡献的lbmysqlping通过可配置的方式,能够灵活的对MySQL服务进行检测,如果出现异常\超时或者Slave延时超过配置大小,可以执行指定的失效命令,当服务恢复可用的时候又可以执行恢复命令.

另外lbmysqlping还可以做简单的报警系统,配置好默认信息后,其他realserver 只需要配置ip地址即可,如果端口不一样,改下端口。然后在down命令和up命令里面填上相关的脚本或者报警程序,这样一个简单的MySQL服务可用性报警系统就出来了,简单、高效。

配置文件格式如下:

【阅读全文·MySQL实验室】

2012年6月1日 谭俊青 2 条评论

最近用Go语言写给legendbase.com写网站的时候实现了个MVC框架,原理是根据uri请求,用reflect方法找到相应的controller跟method,然后执行之.当没有找到就根据uri后缀是否是php,如果是则调用fastcig-client执行配置目录下的php文件.如果再没有就写404错误. Go语言官方package没有client的实现,于是花了点功夫实现了个,开源出来,给需要的朋友节省点时间.

地址: http://code.google.com/p/go-fastcgi-client/

使用示例: 

func main() {

        reqParams := "name=value"

        env := make(map[string]string)
        env["REQUEST_METHOD"] = "GET"
        env["SCRIPT_FILENAME"] = "/Users/ivan/work/test/fcgi/test.php"
        env["SERVER_SOFTWARE"] = "go / fcgiclient "
        env["REMOTE_ADDR"] = "127.0.0.1"
        env["SERVER_PROTOCOL"] = "HTTP/1.1"
        env["QUERY_STRING"] = reqParams

        fcgi, err := fcgiclient.New("127.0.0.1", 9000)
        if err != nil {
                fmt.Printf("err: %v", err)
        }

        content, err := fcgi.Request(env, reqParams)
        if err != nil {
                fmt.Printf("err: %v", err)
        }

        fmt.Printf("content: %s", content)
}
 

分类: Go 标签: ,
2012年5月24日 谭俊青 1 条评论

Go语言1.0出来之后,原来可用的MySQL驱动在新版本上基本不可用了,于是这几天自己写了个简单的MySQL的驱动,暂不支持prepare,没有implement sql/driver. 或许有能用的上的朋友,所以分享出来. 先介绍用法,后面提供下载链接.
先直接上例子

package main

import (
        "log"
        "liblbmysql"
)

func main() {
        settings := make(map[string]interface{})

        settings["host"] = "localhost"
        settings["port"] = 3306
        settings["uname"] = "ivan"
        settings["pass"] = "******"
        settings["dbname"] = "test"
        settings["charset"] = "utf8"
       
        conn, err := liblbmysql.Connect(settings)
        if err != nil {
                log.Fatal("Connect to MySQL error: %v", err)
        }

        var qr *liblbmysql.QueryResult

        qr, err = conn.ExecuteFetch("select * from ivan")
        if err != nil {
                log.Printf("query error: %v", err)
        } else {
                for {
                        m := qr.FetchMap()
                        if m != nil {
                                log.Printf("row map: %v\n", m)
                        } else {
                                break
                        }
                }
        }
        qr.Free()
        conn.Close()
}
 

【阅读全文·MySQL实验室】

分类: Go, MySQL 标签: ,
2012年3月29日 谭俊青 8 条评论

今天很高兴得知Go1 release, 其实我一只在等它正式发布,因为我用go语言自己实现了一个MySQL代理,命名为LegendBase Proxy for MySQL(LPM),等Go1 release之后,我也可以尽快release出来了.

代码摘录:

        case COM_STMT_EXECUTE, COM_QUERY:
                /* if we get a OK in the first packet there will be no result-set */
                switch *qStage {
                case PARSE_COM_QUERY_INIT:
                        switch pkt[4] {
                        case 0xff, 0×00: // ERR, OK, NULL
                                /* ERR: e.g. SELECT * FROM dual -> ERROR 1096 (HY000): No tables used */
                                /* OK:  e.g. DELETE FROM tbl */
                                finished = true
                                break
                        case 0xfb: // NULL
                                *qStage = PARSE_COM_QUERY_LOAD_DATA
                                finished = true
                                break
                        case 0xfe: // EOF
                                err = errors.New(fmt.Sprintf("COM_(0x%02x), packet should not be (NULL|EOF), got %02x", cmd, pkt[4]))
                                break
                        default:
                                *qStage = PARSE_COM_QUERY_FIELD
                                break
                        }
                        break

                case PARSE_COM_QUERY_FIELD:
 

写这个的初衷是因为除了部分程序开发者自己实现了读写分离\或者采用sharding方式之外,还有大量的程序是针对单一的MySQL数据库接口. 然而随着业务量的增加,db压力增大,单台db没法承载,需要扩展MySQL数据库为主从架构,这时候需要代理层来分发读写,因为改造程序的代价往往太大.

新开发的MySQL proxy的配置如下:

[proxy]
port = 3307
sock = /tmp/proxy.sock
fetch_schemas_interval = 10  # seconds

# For management
admin_user      = admin
admin_password  = 100c4e57374fc998e57164d4c0453bd3a4876a58   # sha1
admin_port      = 8288

# For connecting to proxy
proxy_user      = ivan
proxy_password  = 100c4e57374fc998e57164d4c0453bd3a4876a58   # sha1

# For proxy to connect backend mysql server
mysql_user      = ivan
mysql_password  = 100c4e57374fc998e57164d4c0453bd3a4876a58    #sha1
mysql_dbname    = test # default db

# connections setting
connections_per_weight_max = 32

# master and slaves setting. ip:port:w  w: weight of load
master          = 127.0.0.1:3306
#slaves         = 127.0.0.1:3306:1|192.168.2.200:3306:2|192.168.2.203:3306:1
slaves          = 127.0.0.1:3306:1
 

其中配置的密码是明文密码的sha1, 防止非相关人员获取数据库的密码,安全性能得到一定的保障.
通过其他的配置也可以看出proxy实现的功能,即能够配置多个slave,并且能够指定它们的权重.

分类: MySQL 标签:
2011年12月23日 谭俊青 4 条评论

这几天CSDN数据库明文密码泄密闹得满城风雨,人人自危。大家开始关注网络安全和隐私问题。由这次的问题,对加密验证过程我也思考过,有没有一套相对安全的存储和验证方法?这里我根据各种验证方法,确实总结了一套可行且相对安全的存储和验证方法,且看我细细道来,最后提出MySQL当前验证方法的改进。– by 谭俊青

一个系统对用户请求的合法性验证都是通过sessionid来判断的,这个层面的攻击和破解这里不涉及,我们只关心在session建立前的验证过程。密码存储的方式和验证方法大致可以归结为以下几种:

  1. 密码明文存储。比如CSDN的做法,用户登陆系统的时候直接提交明文密码,跟数据库中保存的密码进行比较,如果一直,验证通过,建立session。这时最简单,也是最傻的一种做法,所以导致了这次的泄密。对于密码一致的用户已经体会到了它的危害严重性。
  2. 密码加密存储。对用户密码进行加密存储,登陆验证的过程跟上面的验证过程类似,只是对密码进行了加密,或者对存储的密文进行了解密,然后验证。这种方法稍微提高了一定的安全性,但是还远远不够,没有从根本上解决问题,因为如果黑客拿到了密文,然后破解加密算法,可以解密所有的密文,获得所有用户的明文密码,还会导致像今天CSDN这样的局面。
  3. 密码单向hash存储。这个应该说在根本上解决了问题所在。比如存储md5(password),sha1(password),验证时只需要比较用户输入的密码的hash值跟存储的是否一致即可验证。当然这里问题又来了因为有人会提出md5破解的问题,破解碰撞的概率现在还是相当低的,比中5亿彩票大奖的概率要低得多。但是现在有很多现有的密码字典,并且已经生成了hash值,值需要比较hash值,即可获得等价的登陆密码。因此我们对这个方法进行改进,得出下面的方法。
  4. 密码还是hash存储,不过是采用hash(hash(passwordd)+salt)的方式。这里salt可以在注册是随机生成,也可以取巧用用户名,这样可以在验证的过程增加随机串,而不用将salt输出止客户端。

对第4种方法进行变种,可以得到很多验证方法,都相对安全可靠得多,但是都存在一个问题,这个问题在mysql4.1之前的版本也存在着,就是如果有人得到存储的hash值,即可以模拟登陆,并验证通过。

现在我们回到MySQL密码的存储和验证过程。在4.1之前,mysql的验证方法相对简单,方法如下:

  1. 首先server给client发送一个随机串
  2. client将用户输入的password的hash值对随机串进行加密,并将加密串发送给server
  3. server用存储在user表中的password hash串对之前发送给client的随机串进行加密,生成加密串
  4. server对接收道的加密串和生成的加密串进行比较,如果一致,验证通过。

这样的验证方法看似比较安全可靠,即使你监听网络也不会获得password或者其hash值。但是如果有人通过其他途径,获得了password的hash值,那么就可以模拟登陆系统,因此我们需要对验证过程进行改进。下面介绍mysql4.1之后的密码存储和登陆验证过程:

  • server存储mysql.user.Password = sha1(sha1(password))
  • server 发送随机串random_string至client
  • client 计算
    • stage1_hash  = sha1(password) –password为用户输入的明文密码
    • token = sha1( random_string + sha1(stage1_hash)) xor stage1_hash
  • client 将 token 发送给server
  • server 计算
    • stage1_hash_2 = token xor sha1( random_string + mysql.user.Password)
  • sever 比较 sha1(stage1_hash_2) 跟 mysql.user.Password 即可验证

这个验证过程看似复杂,其实整个过程就是为了保证 sha1(password)不被监听到,用随机串进行了干扰。至此我们的密码存储方式和验证方法的安全性都得到了极大的提升。 当然我们发现mysql 4.1之后的密码存储还是有点小问题,没有加salt,因此存在一定的风险,可以建议官方版本改进,比如将 salt 用用户名代替,整个验证过程几乎不用大的改动。

分类: MySQL 标签:
2011年12月12日 谭俊青 4 条评论

MySQL InnoDB使用 Raid 1+0 stripe size 大小该如何配置?

By: 谭俊青@MySQL实验室

要理解Raid 1+0,我们首先要先理解Raid 0,看下图:

左图为 4kb stripe size;右图为 64kb stripe size
存储文件大小:红色:4kiB,蓝色:20kiB,绿色:100kiB,紫色:500kiB

Raid 0 准确的来说应该称 AID 0
大家可以看到,如果stripe size设置过大,在单线程的情况下起不到提速作用。而设置过小又会产生多次IO操作。因此我们通过简化模型,将大部分请求的文件块大小(IO_SIZE)除以RAID0 的磁盘数量(DISK_NUM)来估算stripe size大小(stripe size=IO_SIZE / DISK_NUM)。

比如MySQL InnoDB,假设平时大部分请求文件块的大小为1M(16kb*64),那么在8块盘组成的Raid1+0的情况下stripe size=1024kb / 4 = 256kb。这时候将Raid的条带大小配置为256kb是比较合适的。MyISAM存储引擎使用情况相对复杂。

实际上管理和磁盘定位等还有一定开销,更为主要的是不同的业务,请求的文件块大小相差很大,所以在实际环境中很难一刀切说多大的stripe size最佳,因此在估算之后可以上下浮动评测,选择适合自己系统的最优大小的stripe size。

顺便今天在MySQL实验室的2个群里调查了下,stripe size大小为64kb、256kb基本各占50%。

-- -------------

SDO:测试下8块及以上盘阵 strip size 256k最佳

点评网:256k

畅游:512k 综合效果更好 (来源评论

继续统计更新…

分类: MySQL, MySQL Innodb 标签: ,
Pages: 1 2 3 4 5 6 7 Next