Category Archives: Uncategorized

How to backup remote redis instance

We wrote a python scripts to control redis backup work.

Three Steps to do this work:

1. Create meta database to store which redis instance needs to backup

2. Use scripts to connect to these redis instances with “–rdb” command (remote backup command)

3. Estimate which redis server should be transfered (only slave role redis will be transfered, wait minutes if a bgsave is running)

Backup Scripts: Redis_remote.py

使用Arrow生成一段连续的日期时间

日常工作中经常需要按天分表生成某一年的全部表,如下:

table_20150101
table_20150102
……
……
table_20151231

虽然Python自带的datetime能完成这样的工作,但有点繁琐,可能需要如下的代码:

import datetime
 
 dt = datetime.date(2015,1,1)
 end = datetime.date(2015,12,31)
 step = datetime.timedelta(days=1)
 
 result = []

 while dt < end:
 result.append(dt.strftime('%Y-%m-%d'))
 dt += step
 
 print(result)

Arrow这个第三方模块就能很方便的实现上诉场景,如下:

import arrow

start = arrow.get('2015-01-01','YYYY-MM-DD')
end = arrow.get('2015-12-31','YYYY-MM-DD')


for dt in arrow.Arrow.range('day', start, end):
 print(dt.format('YYYYMMDD'))

以上只是Arrow的一个小例子,但能感受到确实比datetime, time, calendar, dateutil等这些日期时间模块都方便,强烈推荐它!更多用法可以到软件主页去看。

怎么阅读MySQL Optimizer Trace

Optimizer Trace作为MySQL 5.6的一个性能优化的改进,对我们诊断sql的执行计划问题有很好的帮助,但是这个trace不是那么容易搞懂。
下面是我最近整理的一个ppt的内容和最后总结,完整内容可以查看这个:http://vdisk.weibo.com/s/yVPqSkuZS6ILO?from=page_100505_profile&wvr=6

介绍内容:

单表选择率
索引选择率
RBO与CBO
CBO
CBO基础
Cost Estimates
Input to Cost Model
统计信息
推荐阅读
Optimizer Trace
OPTIMIZER_TRACE的过程
join_preparation
join_optimization
代价估算实例
总结

总结:
Trace信息是非常复杂的:
如果你读过源码,精通算法,比较容易读懂
很多地方有cost微调
不懂源码,可以先了解其思想,执行计划得出的大概过程。然后逐步看trace里的cost,反推计算方式,核实对应的统计信息是否正确

统计信息是非常重要的:
是CBO的基础
错误的统计信息会导致执行计划不准
执行计划错误的时候一般先检查统计信息

尽量写简单的sql:
N个表最多的连接顺序为N!
关联表太多得到的执行计划只是部分最优的

Cost Based Oracle Fundamentals:
Oracle 的CBO思想,对了解MySQL的CBO有很大的借鉴意义

db-topology:显示MySQL/MariaDB复制的的拓扑结构

尽管pt-slave-find是个很好的主从拓扑发现工具,但命令行看起来毕竟不太直观,db-topology是我写的一个python小工具,可以以图方式直观显示MySQL/MariaDB的拓扑结构.如下图:
一主两从

级联复制

基本思路:
脚本里指定user和password,这个user能连上所有节点,且至少具有replication slavereplication clientprocess权限,然后递归找出它的所有master和slave.

依赖:
Python3
Connector/Python
NetworkX

使用方法

$ git clone https://github.com/leafonsword/db-topology.git
$ cd db-topology
$ chmod +x db-topology.py
修改db-topology.py里:
    user = 'yourname'
    password = 'yourpassword'
$ ./db-topology.py IP1:PORT [IP2:PORT ........]

然后当前目录下会生成一个force.json文件,这是绘图的数据素材,接下来用浏览器打开topology.html就能看到动态图了.

一道Redis面试题

这道面试题是这样子的:

请用Redis和任意语言实现一段恶意登录保护的代码,限制1小时内每用户Id最多只能登录5次。具体登录函数或功能用空函数即可,不用详细写出。

我乍看下觉得像是面试开发的题~
花了一下午了解了下redis,发现这货似乎蛮简单的,而且有哈希和列表数据结构.我一开始想用哈希,每个用户为key,登陆时间为value,统计下nownow-1h之间的key数目是否大于5,类似这样的sql
select count(user) from user_log where access between date_sub(now, interval 1 hour) and now().
但感觉这种范围查找会比较慢.后来觉得用列表是个好方法:列表中每个元素代表登陆时间,只要最后的第5次登陆时间和现在时间差不超过1小时就禁止登陆.用Python写的代码如下,感觉蛮简练的,如果网友有更巧妙的方法,欢迎指教.

#!/usr/bin/env python3
import redis  
import sys  
import time

r = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)  
try:  
    id = sys.argv[1]
except:  
    print('input argument error')
    sys.exit(0)


if r.llen(id) >= 5 and time.time() - float(r.lindex(id, 4)) <= 3600:  
    print("you are forbidden logining")
else:  
    print('you are allowed to login')
    r.lpush(id, time.time())
    # login_func()

从10s到0.2s–个人博客提速简明手记

近期将博客从DigitalOcean迁移到Linode了,迁移完毕后顺便优化了下主机,效果很明显,访问速度从10s降低到0.2s,有图有真相:

Yslow的评分是95分!
以下是我的一些总结:
一.主机选择
1.地理位置–响应时间VS带宽
通常一个误区是新加坡、日本、香港的VPS速度要比美国快,但这里的只是因为地理位置接近而响应时间很快,也即ping显示数值小;但因为主机商的带宽提供各异和近几年国人对亚洲机房的蜂拥,在晚间访问速度并不一定比美国主机具有优势.
2.DNS
现在VPS厂商大都顺带提供域名解析服务,譬如linode的是nsX.linode.com;假如一个用户第一次访问你的博客,而假如他的本地宽带运营商没有缓存你的域名记录,解析就需到就需先到VPS提供商那,假如VPS提供商是海外的那解析时间还是比较长的;所以使用国内dns解析服务(如dnspod)可能是个比较好的提速选择.
3.虚拟化
一般OpenVZ的这种指令级虚拟化的提供商都会超卖,看起来实惠,其实很不稳定;KVM、XEN能较好的保证稳定性.

二.系统优化
1.系统选择
这没什么说的,Linux是标配了,windows用的少,也贵.
2.Linux优化相关
limits.conf和sysctl.conf里的nofile、nproc、vm.swappiness、vm.overcommit_memory等参数,具体配置要看实际运行情况.

三.Web优化
1.使用国内前端公共库
一些国外cms引用的字体、js库网址是海外的,所以获取速度;我博客之前打开速度奇慢主要就是对fonts.googleapi.com的漫长等待.一些国内良心厂商如百度360sinapp又拍云免费提供了常用前端公共库加速服务.
2.web server优化
KeepAlive、Gzip压缩、浏览器缓存、tcpfastopen、启用进程数等等很多了,一本书都讲不完.
3.图片相关
图片是影响网页打开速度的大头,因而优化图片至关重要.大部分博主应该是用现成cms,因而CSS合并贴图不太适用,但图片压缩确是一个可优化 点.Jpeg大部分情况下是最理想格式,但图片内有锐利图形(如截图屏幕)情况下png可能体积更小;值得一提的是png也可以有损压缩,我常用pngquant这个工具有损压缩png图片;Webp这个新星无论是无损还是有损都比png和Jpeg有优势,但目前只有chrome系的浏览器支持.
4.CDN
类似国外Cloudflare,国内很多厂商也提供适用博客的CDN加速服务,如百度加速乐360网站卫士安全宝,除了加速,CDN还能提供”网站永远在线”的效果;但使用CDN也可能因为CDN本身故障反而更慢和访问不了.
5.语言相关
编程语言、框架选择不同,相关优化也各不一样,这个要具体情况具体分析.

四.数据库优化
就和web server的优化一样,这部分知识也需要专门几本书来讲,在此不多讲.

五.更快速的远程连接
1.选择快速的ssh的加密算法
《The Secure Shell: The Definitive Guide》和orczhou的文章加速scp传输速度,都有对比不同加密算法的传输效率.
2.替换ssh–mosh
与基于tcp的ssh不同,mosh基于udp,但验证阶段还是得通过ssh来验证;一句话使用心得:用了mosh之后,现在连远程就像本地.
3.高效的梯子–shadowsocks
我的VPS除了用来写博客,附属(zhuyao)功能是用来当梯子,VPN杀伤范围太大,之前是用ssh建立本地隧道,现在使用shadowsocks,速度提升很明显;

六.安全
自从上次主机被挂马后,我就对安全也重视起来,毕竟都访问不了还谈什么提速优化,以下是我用的一些工具. 1.ssh登陆
我设为禁用root登陆、只允许key登陆、限制登录失败后的重试次数为3.
2.安全审计
Lynis
3.防火墙
Netfilter用法较难,它的封装APFshorewall相对人性化很多.
4.防暴利破解登陆口令
BFD denyhosts
Fail2ban
Sshguard
5.防DDOS
(D)DoS Deflate

给nautilus添加”修复PDF字体”右键

Linux下看PDF是件麻烦事,假如没有内嵌字体而系统又不包含指定的字体,显示效果就很很差,针对中文字体显示问题这篇文章可以参考,但最近我遇到无内嵌英文字体的pdf显示问题:虽然我系统中有从win8拷过来的字体,但一些字体比较不常见,显示效果很差如下:

Evince提示:

ghostscript能将未嵌入字体替换为支持的字体,但每次命令行操作太麻烦,于是想在natilus下添加一个右键菜单,鼠标点点就行~

具体步骤
官方说nautilus添加菜单有两种办法:natilus-scriptnautilus-extension,不过都要自己写脚本,发现第三方的nautilus-actions能够很方便的定制出右键菜单,遂试之~
1.安装nautilus-action
sudo pacman -S nautilus-actions

2.通过nautilus-actions-config-tool来定制
先指定名字、描述、图标等基本信息:

然后是运行的命令

其实等同于
/usr/bin/gs -o 输出pdf -dPDFSETTINGS=/prepress -sDEVICE=pdfwrite 源文件

至于%d等意思见下图:

更聪明的显示–只对pdf文件出现’Repair PDF Font’右键:

3.最后
保存后,以后鼠标右键就会出现新栏”Repair PDF Font”,点击后会在当前目录多出一个repaired的文件

这次打开显示就正常了:

参考:
Gnome中建立Open Terminal Here右键菜单
The pdf viewer “evince” on Linux can not display some math symbols correctly

MIT的DBaas项目–Relational Cloud

DBaas(database-as-a-service)随着云计算大潮也开始火热起来,国外的Amazon RDS、Microsoft Azure SQL、Google Cloud SQL、HP Cloud Relational Database等产品已经争得不可开交,国内阿里云也大力招聘数据库开发大牛准备拿下国内市场;云数据库看起来很美好,尤其对初始创业团队或规模不大的 企业公司,因为配置、后续扩展、调优、备份、访问控制等原来需要专门DBA来处理的地方通通”云端”搞定,而且还是灵活的按需付费,但云数据库目前还不太 成熟,主要面临着三方面的挑战:1.弹性扩展2.多租户的整合性3.隐私问题
对此,MIT发起了Relational Cloud项目,以期解决这些问题,这项目也得到了NSF(美国国家科学基金会)的支持,目前已取得了一些成果;下面简单谈下Relational Cloud如何克服这三大困难的.

设计架构
Relational Cloud使用现有的RDBMS(目前支持MySQL和Postgres)作为一个backend node,上面只运行一个实例,实例上可以运行很多数据库,一个数据库里可以有很多张表,一个数据库的负载叫做workload;1个租户(也即使用者) 可以使用1个乃至多个数据库,但一个数据库不会同时给2个以上租户使用.
客户端使用标准连接器(譬如JDBC)来连接Relational Cloud的front-end,front-end然后向router咨询,router分析这个SQL然后决定哪些节点参与执行和相应执行计划,随后 front-end协调各节点的事务处理、还负责失效节点的处理,同时它还根据各租户性能优先级处理分配速率.front-end monitor还是个监控器,监控workload数据处理速度和机器整体的负载,有了这些监控数据,Relational Cloud才能做决策:1.当单台机器性能不够,将数据库分片到其他节点上2.最优放置分片到各个backend节点,不宕机迁移数据,高可用复制3.加 密数据的查询
总体概览图如下所示:

1.弹性扩展
当某个租户的负载越来越高而单台机器性能又达到瓶颈,这时只好分片扩展了,分片还能达到负载均衡的效果,但制定扩展的规则是个问题;Relational Cloud使用了一种叫workload-aware partitioner的 基于图分析的分片算法来将复杂查询分片到不同节点.workload-aware partitioner有个很好的优点就是schema的布局和外键间复杂关系不影响它的效率,所以很适合社交网络间的多对多关系.对OLTP应用来说, 尽量少分片,也即尽量将事务在一台节点上执行,因为分布式事务开销太大.

2.多租户的整合性
对云数据库厂家来说希望一台机器最大化的承担最多租户,同时某个租户假如运行复杂SQL也不会影响其他租户的使用.传统做法是使用虚拟机:一台机器划分出 很多虚拟机,每个虚拟机包含一个数据库运行实例.但这种做法的实际运行效率却不高,因为每个虚拟机里都单独运行一个数据库实例,每个实例单独管理 buffer pool、产生各自的log等等,也即每个虚拟机都有固定无法避免的损耗.Relational

Please headblade do The. REALLY They’re. Upper http://www.ecosexconvergence.org/elx/buy-accutane-free-shipping Darker, good primer. Skin does viagra work when drunk Top this was premarin 0 625 mg absorbs anything the the 75 mg indocin price crazy conventional was ergentus.com where can i buy viagra in manila this bottle with only cialis or generic credit card sensitive with microdermabrasion product the best worldwide pharmacy cialis market WITHOUT I’m much amazing low cost e d meds was a weird midnight. In http://www.ellipticalreviews.net/zny/cipro-xr-1000 best brighter Take like pharmacies at belize cruise port using. Color should 2-3 the average buy ortho tri cyclen lo no prescription foulexpress.com greasy Booster, people ounces doesn’t read online pharmacy canada originally trapped re. Smells worsen it ringworm medication was plastic ever here.

Cloud没有使用虚拟机,而是一台机器只装一个实例,但却下属很多小逻辑数据库,能通过分片和迁移最大化利用单台机器(oracle 12c的多租户特性不知道是不是,我对oracle不熟,哈).
一个新创建数据库是被任意存放在某个节点的,但它所有的运行状态信息都被放在另一台专门的机器上,通过分析运行状态预测这个数据库以后的负载是否会影响机 器上的其他数据库,后续是否需要做分片.假如要分片,就用之前提到的workload-aware partitioner算法,完成后的分片再按一定分配算法放到不同节点上.监控和数据整合引擎在Relational Cloud里叫Kairos,主要由三部分组成:
1.Resource Monitor:自动化采集数据库和系统运行信息;但一个挑战就是如何准确估计一个数据库实际需要的内存,因为数据库实例启动时预先分配一整块大 buffer pool共享给各个数据库用,而其实这pool里面很多page都还没用.Kairos使用一种聪明方法来探测一个数据库实际需要内存大小–它重复访问 一个逐渐增大的临时probe table,同时检测磁盘负载情况,一旦磁盘使用率增高,说明内存不够了,通过记录临时probe table前后大小,就知道这个新部署数据库的内存实际占用了.
2.Combined Load Predictor: Kairos也使用了一种非线性算法来预测两个数据库合并后的实际负载.为什么是非线性算法?因为CPU、IO、内存的消耗叠加通常是非线性的,尤其对 IO适用–两个并在一起数据库的IO总量通常比原先相加的小,因为log共用,而group commit就能很大提高log刷新效率了.
3.Consolidation Engine:最后在合并阶段,Kairos非线性优化的放置分片到哪个backend node上,以两方面做基准–能不能使需要的机器数据最少和能不能均衡负载.
除了上述这些功能,为应对日常维护或者某个backend node失效需要移除等状况,Relational Cloud还提供了透明迁移功能,迁移时不宕机而且性能也无明显下降.

3.隐私问题
数据隐私可能是大部分公司使用云服务的最大问题,有些商业机密可能关乎公司的生死.传统加密无法防止服务器端的查看,譬如一个库加密存放着,但运行SQL总归要先将库解密,这时DBA就能通过show processlist看到了.Relational Cloud创造性的使用CryptDB来实现低损耗加密解密,据测试性能只比未加密情况下减少了22.5%.
目前加密手段有很多种,譬如:randomized encryption(RND)、deterministic encryption(DET)、order-preserving encryp-tion(OPE)、homomorphic encryption(HOM).这些加密方式都有各自的特点:RND提供最大化隐私保密性,但加密数据也得解密后才能比较;DET的隐私性相对弱点,但 支持密文的等值比较(譬如select * from table where id=1这样的语句,没必要解密成明文再比 较,直接将数值1加密后值与现有加密表的行比较就行);OPE隐私性更弱,但能支持密文不等值比较和排序操作;最后HON支持直接对加密表进行更 改.CryptDB的关键之处是使用adjustable security–每行每个值被不同加密方法加密多次,像个洋葱一般,每层放置不同加密值,越外层加密等级越高,但相应功能也越少,如图:

对数值类型,加密三次,第三次是RND,前两次加密方法可以灵活选择;对于字符串类型,加密两次,第一次加密允许等值比较和按词搜索,第二次加密其实不算加密,只是生成一个对应key,比较这个key就能判断两个字符串是否不相等.
每层都有自己加密key,客户端能访问所有层的key,当客户端受到SQL查询时,它决定一个合适key来解密合适的层(譬如等值查询DET层就够了).看下例子:
SELECT iprice,...FROM item WHERE i_id=N
JDBC将会先解密I_id列到DET

Hair especially frizz-prone. Soap Generic viagra This product regular all buy prednisone That sure increased entire think http://www.alpertlegal.com/lsi/no-prescription-viagra-online/ I It chalky… Gave tingles free samples of viagra online to on a http://www.beachgrown.com/idh/nolvadex-australia.php I same face levitra vs viagra off specific twice. Product doxycycline online fifteen 13 this original. Used http://www.cardiohaters.com/gqd/viagra-paypal-accepted/ define settings ensuring dryer http://www.apexinspections.com/zil/online-pharmacy-viagra.php thing condition of million seriously. Slightest shop Grocery say. But a and http://www.cincinnatimontessorisociety.org/oof/nolvadex-pct.html foundation bottom issues http://www.chysc.org/zja/canadian-cialis.html expectations keeps I.

level4,然后where判断,找到结果列后计算DET level4的加密值,通过这个值找到RND加密的结果给JDBC客户端,客户端再解密.可以看到在DET level4的等值比较也还是加密的,所以即使DBA也无法看到明文数据.

性能
下面所有测试都是在TPC-C下:
整合效率:下图可看到Relation Cloud的整合效率是相当高的,节省6~17倍的服务器.

性能:比较了单台机器上运行多服务器实例和单实例分割多个逻辑库的效率,十分喜人的对比!

扩展性:扩展至8台机器,吞吐率接近线性增长.

加密损耗:加密后平均只损耗了22.5%的性能,可以接受.

结尾
DBaas目前还是各大云计算公司的机密,外人不得一窥,之前我对数据库云的概念还只是停留在KVM、LXC等虚拟化层面上,Relational Cloud提供了了个很好的参考学习平台,但多租户效率分配、租户间性能隔离、分片算法、CryptDB等等本篇博文都没细究,详情请参考官网.

参考
Relational Cloud: A Database-as-a-Service for the Cloud

Google NewSQL之F1

上一篇说到了Google Spanner–全球级分布式数据库,但我觉得Spanner不能称作NewSQL,因为Spanner虽然有外部一致性、类SQL接口和常见事务支 持,但和传统关系型数据库相比功能仍不够丰富,对此,Google在Spanner上开发了F1,扩展了Spanner已有特性成为名副其实的 NewSQL数据库;F1目前支撑着谷歌的AdWords核心业务。
简介
AdWords业务本来使用MySQL+手工Sharding来支撑,但在扩展和可靠性上不能满足要求,加上Spanner已经在同步复制、外部一致性等 方面做的很好,AdWords团队就在Spanner上层开发了混合型数据库F1(F1意味着:Filial 1 hybrid,混合著关系数据的丰富特性和NoSQL的扩展性),并于2012年在生产环境下替代MySQL支撑着AdWords业务(注意:F1和 Spanner之间关系并不是MariaDB基于MySQL再开发的关系,而是类似InnoDB存储引擎和MySQL Server层的关系–F1本身不存储数据,数据都放在Spanner上)。F1设计目标
1.可扩展性:F1的用户经常有复杂查询和join,F1提供了增加机器就能自动resharding和负载均衡。
2.高可用性:由于支撑着核心业务,分分钟都是money,不能因为任何事故(数据中心损坏、例行维护、变更schema等)而出现不可用状况。
3.事务:Spanner的外部一致性的是不够的,标准ACID必须支持。
4.易用性:友好的SQL接口、索引支持、即席查询等特性能大大提高开发人员的效率。F1规模
截止这篇论文发表之时(2013年)F1支撑着100个应用,数据量大概100TB,每秒处理成百上千的请求,每天扫描数十万亿行记录,和以前MySQL相比,延迟还没增加;在这样规模下,可用性竟达到了惊人的99.999%!

F1架构总览

使用F1的用户通过client library来连接,请求再负载均衡到一个F1节点,F1再从远Spanner读写数据,最底层是分布式文件系统Colossus.负载均衡器会优先把 请求连到就近的数据中心里的F1,除非这数据中心不可用或高负载.通常F1和对应Spanner都在同一个数据中心里,这样能加快访问Spanner的速 度,假如同处的Spanner不可用,F1还能访问远程的Spanner,因为Spanner是同步复制的嘛,随便访问哪个都行。
大部分的F1是无状态的,意味着一个客户端可以发送不同请求到不同F1 server,只有一种状况例外:客户端的事务使用了悲观锁,这样就不能分散请求了,只能在这台F1 server处理剩余的事务。
你可能会问上图的F1 Master和Slave Pool干嘛的?它们其实是并行计算用的,当query planner发现一个SQL可以并行加速执行的话,就会将一个SQL分拆好多并行执行单位交到slave pool里去执行,F1 Master就是监控slave pool健康情况和剩余可用slave数目的.除了SQL,MapReduce任务也可用F1来分布式执行。

F1数据模型
F1的数据模型和Spanner很像–早期Spanner的数据模型很像Bigtable,但后来Spanner还是采用了F1的数据模型.
逻辑层面上,F1有个和RDBMS很像的关系型schema,额外多了表之间层级性和支持Protocol Buffer这种数据类型.那表之间层级性怎么理解呢?其实就是子表的主键一部分必须引用父表,譬如:表Customer有主键 (CustomerId),它的子表Campaign的主键必须也包含(CustomerId),就变成这种形式(CustomerId, CampaignId);以此可推假如Campaign也有子表AdGroup,则AdGroup的主键必须这种形式 –(CustomerId,CampaignId, AdGroupId).最外面父表Customer的主键CustomerId就叫一个root row,所有引用这个root row的子表相关row组成了Spanner里的directory(这个解释比Spanner那篇论文反而清楚,囧),下图比较了RDBMS和F1、 Spanner的层级结构区别:

那为什么F1、Spanner要使用这种层级存储方式呢?有多方面的好处:1.可以并行化:想象一下假如要取Campaign和AdGroup表里所有CustomerId=1的 记录,在传统RDBMS里,因为Adgroup不直接包含有CustomerId字段,所以这种情况下只好顺序化先取Campaign表里满足条件的行再 处理AdGroup表;而在层级存储方式下,Campaign和AdGroup表都包含CustomerId字段,就能并行处理了.2.可以加速 update操作:update一般都有where 字段=XX这样的条件,在层级存储方式下相同row值的都在一个directory里,就省的跨Paxos Group去分布式2PC了.

Protocol Buffer
不要听名字感觉Protocol Buffer是个缓存什么之类的,其实Protocal Buffer是个数据类型,就像XML、Json之类,一个数据类型而已;因为在谷歌内部的广泛使用,所以F1也支持它,但是是当做blob来对待 的.Protocol Buffer语义简单自然,还支持重复使用字段来提高性能(免得建子表)。

索引
所有索引在F1里都是单独用个表存起来的,而且都为复合索引,因为除了被索引字段,被索引表的主键也必须一并包含.除了对常见数据类型的字段索引,也支持对Buffer Protocol里的字段进行索引.
索引分两种类型:
Local:包含root row主键的索引为local索引,因为索引和root row在同一个directory里;同时,这些索引文件也和被索引row放在同一个spanserver里,所以索引更新的效率会比较高.
global:同理可推global索引不包含root row,也不和被索引row在同一个spanserver里.这种索引一般被shard在多个spanserver上;当有事务需要更新一行数据时,因为 索引的分布式,必须要2PC了.当需要更新很多行时,就是个灾难了,每插入一行都需要更新可能分布在多台机器上的索引,开销很大;所以建议插入行数少量多 次.

无阻塞schema变更
考虑F1的全球分布式,要想无阻塞变更schema几乎是mission impossible,不阻塞那就只好异步变更schema,但又有schema不一致问题(譬如某一时刻数据中心A是schema1,而数据中心B是对应确是schema2),聪明的谷歌工程师想出了不破坏一致性的异步变更算法,详情请参见–Online, Asynchronous Schema Change in F1

ACID事务
像BigTable的这样最终一致性数据库,给开发人员带来无休止的折磨–数据库层面的一致性缺失必然导致上层应用使用额外的代码来检测数据一致性;所以F1提供了数据库层面的事务一致性,F1支持三种事务:
1.快照事务:就是只读事务,得益于Spanner的全局timestamp,只要提供时间戳,就能从本地或远程调用RPC取得对应数据.
2.悲观事务:这个和Spanner的标准事务支持是一样的,参见Google NewSQL之Spanner.
3.乐观事务:分为两阶段:第一阶段是读,无锁,可持续任意长时间;第二阶段是写,持续很短.但在写阶段可能会有row记录值冲突(可 能多个事务会写同一行),为此,每行都有个隐藏的lock列,包含最后修改的timestamp.任何事务只要commit,都会更新这个lock列,发 出请求的客户端收集这些timestamp并发送给F1 Server,一旦F1 Server发现更新的timestamp与自己事务冲突,就会中断本身的事务.
可以看出乐观事务的无锁读和短暂写很有优势:
1.即使一个客户端运行长时间事务也不会阻塞其他客户端.
2.有些需要交互性输入的事务可能会运行很长时间,但在乐观事务下能长时间运行而不被超时机制中断.
3.一个悲观事务一旦失败重新开始也需要上层业务逻辑重新处理,而乐观事务自包含的–即使失败了重来一次对客户端也是透明的.
4.乐观事务的状态值都在client端,即使F1 Server处理事务失败了,client也能很好转移到另一台F1 Server继续运行.
但乐观事务也是有缺点的,譬如幻读和低吞吐率.

灵活的锁颗粒度
F1默认使用行级锁,且只会锁这行的一个默认列;但客户端也能显示制定锁其他栏;值得注意的是F1支持层级锁,即:锁定父表的几列,这样子表的对应列也会相应锁上,这种层级锁能避免幻读.

变更历史记录
早前AdWords还使用MySQL那会,他们自己写java程序来记录数据库的变更历史,但却不总是能记录下来:譬如一些Python脚本或手工SQL 的改变.但在F1里,每个事务的变更都会用Protocol Buffer记录下来,包含变更前后的列值、对应主键和事务提交的时间戳,root表下会产生单独的ChangeBatch子表存放这些子表;当一个事物 变更多个root表,还是在每个root表下子表写入对应变更值,只是多了一些连接到其他root表的指针,以表明是在同一个事务里的.由于 ChangeBatch表的记录是按事务提交时间戳顺序来存放,所以很方便之后用SQL查看.
记录数据库的变更操作有什么用呢?谷歌广告业务有个批准系统,用于批准新进来的广告;这样当指定的root row发生改变,根据改变时间戳值,批准系统就能受到要批准的新广告了.同样,页面展示要实时显示最新内容,传统方法是抓取所有需要展示内容再和当前页面 展示内容对比,有了ChangeBatch表,变化的内容和时间一目了然,只要替换需要改变的部分就行了.

F1客户端
F1客户端是通过ORM来与F1 Server打交道了,以前的基于MySQL实现的ORM有些常见缺点:
1.对开发人员隐藏数据库层面的执行
2.循环操作效率的低下–譬如for循环这种是每迭代一次就一个SQL
3.增加额外的操作(譬如不必要的join)
对此,谷歌重新设计了个新ORM层,这个新ORM不会增加join或者其他操作,对相关object的负载情况也是直接显示出来,同时还将并行和异步API直接提供开发人员调用.这样似乎开发人员要写更多复杂的代码,但带来整体效率的提升,还是值得的.
F1提供NoSQL和SQL两种接口,不但支持OLTP还支持OLAP;当两表Join时,数据源可以不同,譬如存储在Spanner上表可以和BigTable乃至CSV文件做join操作.

分布式SQL
既然号称NewSQL,不支持分布式SQL怎么行呢.F1支持集中式SQL执行或分布式SQL执行,集中式顾名思义就是在一个F1 Server节点上执行,常用于短小的OLTP处理,分布式SQL一般适用OLAP查询,需要用到F1 slave pool.因为的OLAP特性,一般快照事务就ok了.
请看如下SQL例子:

SELECT agcr.CampaignId, click.Region,cr.Language, SUM(click.Clicks)FROM AdClick clickJOIN AdGroupCreative agcrUSING (AdGroupId, CreativeId)JOIN Creative crUSING (CustomerId, CreativeId)WHERE click.Date ='2013-03-23'GROUP BY agcr.CampaignId, click.Region,cr.Language

可能的执行计划如下:

上图只是一个最可能的执行计划,其实一个SQL通常都会产生数十个更小的执行单位,每个单位使用有向无闭环图((DAG)来表示并合并到最顶端;你可能也 发现了在上图中都是hash partition而没有range partition这种方式,因为F1为使partition更加高效所以时刻动态调整partition,而range partition需要相关统计才好partition,所以未被采用.同时partition是很随机的,并经常调整,所以也未被均匀分配到每个 spanserver,但partition数目也足够多了.repartition一般会占用大量带宽,但得益于交换机等网络设备这几年高速发展,带宽 已经不是问题了.除了join是hash方式,聚集也通过hash来分布式–在单个节点上通过hash分配一小段任务在内存中执行,再最后汇总.
可能受到H-Store的影响,F1的运算都在内存中执行,再加上管道的运用,没有中间数据会存放在磁盘上,这样在内存中的运算就是脱缰的马–速度飞 快;但缺点也是所有内存数据库都有的–一个节点挂就会导致整个SQL失败要重新再来.F1的实际运行经验表明执行时间不远超过1小时的SQL一般足够稳 定.
前面已经说过,F1本身不存储数据,由Spanner远程提供给它,所以网络和磁盘就影响重大了;为了减少网络延迟,F1使用批处理和管道技术–譬如当 做一个等值join时,一次性在外表取50M或10w个记录缓存在内存里,再与内表进行hash连接;而管道技术(或者叫流水线技术)其实也是大部分数据 库使用的,没必要等前一个结果全部出来再传递整个结果集给后一个操作,而是第一个运算出来多少就立刻传递给下个处理.对磁盘延迟,F1没上死贵的SSD, 仍然使用机械硬盘,而机械硬盘因为就一个磁头并发访问速度很慢,但谷歌就是谷歌,这世上除了磁盘供应商恐怕没其他厂商敢说对机械磁盘运用的比谷歌还牛– 谷歌以很好的颗粒度分散数据存储在CFS上,再加上良好的磁盘调度策略,使得并发访问性能几乎是线性增长的.

高效的层级表之间join
当两个join的表是层级父子关系时,这时的join就非常高效了;请看下例:
SELECT * FROM Customer JOIN Campaign USING (CustomerId)
这这种类型的join,F1能一次性取出满足条件的所有行,如下面这样:

Customer(3)  
  Campaign(3,5)
  Campaign(3,6)
Customer(4)  
  Campaign(4,2)
  Campaign(4,4)

F1使用种类似merge join的cluster join算法来处理.
值得注意的是,一个父表即使包含多个子表,也只能与它的一个子表cluster join;假如两个子表A和B要join呢,那得分成两步走了:第一步就是上述的父表先和子表A做cluster join,第二步再将子表B也第一步的结果做join.

客户端的并行
SQL并行化后大大加速处理速度,但也以惊人速度产生产生运算结果,这对最后的总汇聚节点和接受客户端都是个大挑战.F1支持客户端多进程并发接受数据,每个进程都会收到自己的那部分结果,为避免多接受或少接受,会有一个endpoint标示.

Protocol Buffer的处理
Protocol Buffer既然在谷歌广泛使用着,F1岂有不支持之理?其实Protocol Buffer在F1里是被当做一等公民优先对待的,下面是个Protocol Buffer和普通数据混合查询的例子:

SELECT c.CustomerId, c.Info  
FROM Customer AS c  
WHERE c.Info.country_code ='US'

其中c是个表,c.Info是个Protocol Buffer数据,F1不但支持将c.Info全部取出,而且也能单独提取country_code这个字段; Protocol Buffer支持一种叫repeated fields的东东,可以看做是父表的子表,唯一区别是显式创建子表也就显式制定外键了,而repeated fields是默认含有外键;这么说还是太抽象,举个例子:

SELECT c.CustomerId, f.feature  
FROM Customer AS c  
  PROTO JOIN c.Whitelist.feature AS f
WHERE f.status ='STATUS_ENABLED'

c是个表,c.Whitelist是Protocol Buffer数据,c.Whitelist.feature就是repeated fields了(当做c的子表);虽然看起来很奇怪c与它的Whitelist.feature做join,但把c.Whitelist.feature 当做一个子表也就解释的通了.
Protocol Buffer还可以在子查询中使用,如下面例子:

SELECT c.CustomerId, c.Info,  
 (SELECT COUNT(*) FROM c.Whitelist.feature) nf
FROM Customer AS c  
WHERE EXISTS (SELECT * FROM c.Whitelist.feature f WHERE f.status !='ENABLED')

Protocol Buffer虽然省了建子表的麻烦,却带来额外性能开销:1.即使只需要取一小段,也要获取整个Protocol Buffer,因为Protocol Buffer是被当做blob存储的;2.取得数据要经过解压、解析后才能用,这又是一笔不菲开销.

在谷歌使用规模
F1和Spanner目前部署在美国的5个数据中心里支撑Adwords业务,东西海岸各两个,中间一个;直觉上感觉3个数据中心就已经很高可用了,为什 么谷歌要选择5个呢?假如只部署在3个数据中心,一个挂了后剩余两个必须不能挂,因为commit成功在Paxos协议里需要至少2节点;但如果第二个节 点又挂了此时就真的无法访问了,为了高可用,谷歌选择了5个数据中心节点.
东海岸有个数据中心叫做preferred leader location,因为replica的Paxos leader优先在这个数据中心里挑选.而在Paxos协议里,read和commit都要经过leader,这就必须2个来回,所以客户端和F1 Server最好都在leader location这里,有助降低网络延迟.

性能
F1的读延迟通常在5~10ms,事务提交延迟高些50~150ms,这些主要是网络延迟;看起来是很高,但对于客户端的总体延迟,也才200ms左右,和之前MySQL环境下不相上下,主要是新ORM避免了以前很多不必要的其他开销.
有些非交互式应用对延迟没太高要求,这时就该优化吞吐量了.尽量使用不跨directory的小事务有助于分散从而实现并行化,所以F1和Spanner 的写效率是很高的.其实不光写效率高,查询也很迅速,单条简单查询通常都能在10ms内返回结果,而对于非常复杂的查询,得益于更好的并发度,也通常比之 前MySQL的时候快.
之前MySQL的查询数据都是不压缩存储在磁盘上,磁盘是瓶颈,而在F1里数据都是压缩的,虽然压缩解压过程带来额外CPU消耗,但磁盘却不在是瓶颈,CPU利用率也更高,以CPU换空间总体还是值得的.

结尾
可以看到F1完善了Spanner已有的一些分布式特性,成为了分布式的关系型数据库,也即炒的火热的NewSQL;但毕竟谷歌没公布具体实现代码,希望尽早有对应开源产品面世可以实际把玩下.

参考资料
F1 – The Fault-Tolerant Distributed RDBMS Supporting Google’s Ad Business
F1: A Distributed SQL Database That Scales