数据访问层DAL架构和实践(5)数据分片 
Version 5.8.20140312 
刘胜liusheng@umpay.com 
联动优势B办公区 
北京市西城区安德路甲104号证通商务楼F6层(100120)
2 
0 版本历史 
 关于DAL的历史版本 
 20091224数据访问层DAL概念和实践1(刘胜).ppt 【概念/设计/原型】 
 20100330数据访问层DAL概念和实践2(刘胜).ppt 【实现/统一/远景】 
 20100902数据访问层DAL架构和实践3(刘胜).ppt 【接口/本地/模板】 
 专利201010506543.2 数据访问方法及装置 
 20120613数据访问层DAL架构和实践4(刘胜).pptx 【新特性….】 
 20130606数据访问层DAL架构和实践5(刘胜).pptx 【数据分片】 
 最近培训材料 
 20120613数据访问层DAL架构和实践4新的特性(刘胜).pptx 
 20121012联动优势技术大会(刘胜)三年技术规划v1.3.4.pptx 
 20121208联动优势2012年度述职报告(刘胜)1206正式版.pptx 
 20130325理解REST设计模式和反模式(刘胜)草案.pptx 
 20130326号码集合的存储和访问方法兼谈专利申请(刘胜).pptx 
 20130604代码质量和单元测试(刘胜).pptx 
 20130604联动优势企业架构介绍(刘胜).pptx 
 20130606数据访问层DAL架构和实践5数据分片(刘胜).pptx
3 
0*内容大纲(耗时1H+) 
 1 概念 
 数据访问层DAL 
 数据库分区与分片 
 高可用数据存储架构 
 2 背景 
 问题,需求,典型场景 
 3 现状(DAL实现) 
 关键技术选择,演化路线,专利产品,服务平台,在系统中的地位 
 4 设计和实现 
 设计方案要点,实现方案权衡,脚本语言选择,相关约定 
 5 使用和测试 
 用例:环境准备和测试步骤,例1/2/3/…/8,例9 
 测试:功能测试,性能自测 
 6 总结 
 需求满足度,新需求,相关培训,发布地址,参考索引, Q&A
4 
1*概念:高可用数据存储架构(问) 
 Share-Anything架构 
 案例:Oracle RAC共享缓存和存储 
 缺点:扩展能力有限(<10) 
 Share-Storage主备架构 
 案例:DB2双机主备方案 
 缺点:存储是单点 
 Share-Nothing分区架构 
 案例:DB2v9分区方案 
 缺点:管理节点是单点? 
 Share-Nothing主从架构(问:区别?) 
 案例:MySQL主从单向复制 
 缺点:写库是单点,也是瓶颈 
 Share-Nothing主主架构 
 案例:MySQL主从双向复制 
 缺点:未读写分离 
 Share-Nothing双主多从 
 优点:写库非单点,第一写库可停机 
 缺点:写库是瓶颈,第二写库不能停机 
DAL 
代理?
5 
1 概念:数据库分区与分片 
 分片vs. 分区 
分片(Sharding) 分区(Partition) 
技术支持中间件开发商(多库) 数据库厂商 
存储依赖可跨DB类型,可跨物理主机不能跨DB类型,可跨表空间 
数据划分根据ID,时间,范围等根据ID,时间,范围,Hash,混合分区等 
存储方式分布式集中式/分布式 
扩展性横向扩展(Scale-Out) 纵向扩展(Scale-Up) 
可用性无单点存在单点(DB管理节点) 
价格低廉适中(DAS直连式存储),昂贵(NAS或SAN) 
应用场景常见于Web2.0网站多数传统应用 
 开源软件: 
 1,Hibernate Shards 
 2,HiveDB/Hibernate shards:横向切分,可跨服务器数据,但不能做order/join 
 3,MySql/Proxy + Hscal插件,不能跨服务器的 
 4,Spock Proxy:是3的分支:a)消除LUA脚本。b)客户端登录验证。c) 动态连接池。 
 5,PL/Proxy:针对PL语言,其理念是代理远程函数调用同样标签创建的函数。 
 6,PyShards:基于Python的Sharding方案,仅支持MySQL库。
6 
1+概念:数据分片Shard 
 实现shard 需要改变思维(DB设计,App使用) 
 尽量避免join - 关联查询 
 数据冗余/ 反范式 
 例:数据冗余for shard 
 shard before – comment(id, blog_id, content) 
 shard after – comment(id, blog_id, content, user_id) 
 常见shard 策略 
 垂直分片 
 按功能分【如:论坛,博客】 
 水平分片 
 2 * N 【如定单,购买者与网店各一份】 
 N / n 【按日期或ID 范围分区】 
 Hash(N) % n 【按hash 分】 
 分区表查找法
7 
A 
P 
P 
User1 
User2 
DAL 
Proxy 
1*概念:数据访问层DAL实现 
 实现方式 
 进程内DAL API 接口组件 
 Java: Hibernate Shard,HiveDB, … 
 Python: Pyshards 
 进程外DAL Proxy 服务器 
 MySQL:MySql/Proxy,Amoeba,Cobar 
 PgSQL: PL/Proxy (Skype), PgPool-II 
 考虑因素: 
 业务场景:业务应用多少?——有很多 
 功能需求:需求是否固定?——不固定 
 分片规则:是否一成不变?——不确定 
User1 
User2 
DAL 
API 
A 
P 
P
8 
1*概念:数据访问层DAL/Proxy 
 数据访问层DAL的定义 
 Data Access Layer(Layer=Services) 
 DAL是一系列服务的集合,是DAO的自然延伸,是 
SOA的具体实现。 
 数据访问层DAL的好处 
 简化客户端接口,规范数据访问操作 
 保持客户端接口,动态升级服务端实现 
 支持更多客户端:Java/C/PHP/… 
 共享数据库连接,减少总数据连接数 
 更细粒度访问控制 
 方便实施服务治理:权限/管理/监控/分析/优化/… 
 方便实施数据库的迁移/升级/重构/… 
 方便实施读写分离,支持高并发访问 
 方便实施数据分片,支持海量数据库 
 。。。
9 
1+背景:已有的DAL Proxy方案 
 方案1:Mysql-Proxy@MySQL官方 
 不支持分库分表,且性能较差。 
 方案2:Atlas@Qihoo360(王超) 
 基于mysql-proxy-0.8.2版 
 方案3:Amoeba@盛大(陈思儒) 
 for {MySQL | Aladdin | MongoDB} 
 不支持事务/存储过程/分库分表/输出大结果集。 
 方案4:Cobar@AliBaBa(贺贤懋) 
 基于amoeba-0.34版 
 开源版只支持MySQL,不支持读写分离。 
 方案5:TDDL@Taobao 
 复杂度相对较高,文档较少。 
 只开源动态数据源,不开源分库分表。 
 方案6:S3@Amazon 
 基于标准REST和SOAP接口的超级SAN存储
10 
2*背景:问题(简) 
 问题1:核心数据库不堪重负(201106) 
 数据量大 
 用户表:tuser + tuserBank 
 交易表:transAll  七天翻滚表 
 数据冗余 
要么分区,要么分片。 
分区成本高,依赖厂商技术。 
分片阻力大,应用端改动多。 
 数据表多个版本混存,每个用户记录两次,每条交易记录两次。【双写】 
 个别应用过度依赖于TransAll表,未做日表翻滚。【分片】 
 系统迁移不彻底,有残留数据。(用户管理系统:望京丰台) 【多查】 
 读写不均【读写分离】 
 淘宝读写比测算:10:1(来自iDataForum) 
 联动读写比估算:用户表(20:1),交易表(2:1) 
 连接池问题: 【连接共享】 
 应用太多太分散,容易使数据库连接总数超限,导致DB中断 
 问题2:针对复杂的数据库部署,应用开发困难 
 同时连多个数据库 
对于简单分片, 
也能凑活实现。 
 依次查多个数据库表:丰台-望京;七天翻滚表;在线-离线库
11 
2*背景:需求 
 最终目的:替核心数据库减负,同时降低开发难度 
 数据冗余:根据读写操作,使用不同的库。【Full-DB】 
 横向拆分:根据应用拆分多个库。【ShardDB】 
 纵向拆分:根据数据拆分多个库和多个表。【ShardDB】 
 需求细分: 【 实际场景千变万化] 
 后端访问方式: 【暂不考虑NOSQL存储】 
 基于JDBC的访问【普通SQL/预编译SQL/存储过程】 
 数据分片策略: 
 按读写操作的类型【读写分离】 
 按分片数据库库名【相同库相同表,相同库不同表】 
 按分片数据库表名【不同库相同表,不同库不同表】 
以不变应万变 
 按单个分片字段的类型【整数ID值,字符串DT时间】 
 按单个分片字段值个数 
 没有分片字段值(全局) 【全局查询】 
 单个分片字段值(单条) 【单表查询】 
 两个分片字段值(范围) 【多表查询】 
如何 
 按多个分片字段值分片,通过计算产生单一分片关键字【如hash】
12 
2*背景:典型应用场景 
 场景1:客服系统/海量短信日志查询 
 说明:将短信日志归档到多个MySQL库的多张日表。 
 查询:按日期查单个表/按日期范围查多个表/按手机号询全部表 
 场景2:通信账户/海量支付交易日志查询 
 说明:将系统业务日志归档到多个MySQL库的多张日表。 
 查询: (同场景1) 
 场景3:用户管理/数据迁移后的冗余查询 
 说明:系统从望京迁移到丰台,但有部分遗留数据未作迁移。 
 查询:先查丰台数据库,如未找到,则再次查望京数据库。 
 场景4:用户管理/用户信息分表查询 
 说明:将用户表按手机号拆分到多个库的多张表中,并提供查询。 
 查询:按手机号查询单张表/按其他字段查询全部表。 
 场景5:交易表按7日翻滚 
 说明:根据交易日期所在的星期几,选择对应的表进行操作。 
 查询:按交易日期查单张表/按日期范围查多张表/按其他字段查全部表
13 
3 现状:DAL关键技术的选择(简) 
 1,服务组织风格【RESTful】 
 2,中间层编程语言【Java vs C/C++】 
 3,后端存储方式【NO-RDB】 
 4,前端访问接口【NO-JDBC,NO-SQL】 
 NO-SQL 【用sqlid/callid替代sql语句】 
 抛弃复杂的sql语句,解析更容易,执行更安全。 
 NO-JDBC 【可支持NOSQL存储】 
 抛弃复杂的jdbc,服务端更容易解析 
 抛弃复杂的jdbc,客户端更容易使用 
 还可支持更多的存储方式:1)NOSQL库;2)服务(如Tuxedo) 
 5,交互协议(传输协议+数据协议) 
 数据传输协议【HTTP/CM20/…】 
 序列化技术【java+gz/hessian2+gz/xml/json/…】 
 结果集表示【List<Map<String,Object>>】 
 6,负载均衡: 【避免单点】 
NO=Not Only
14 
3 现状:DAL专利产品(简) 
1 应用逻辑层2 数据访问层(DAL) 
1 应用系统 
21 NIO网络 
协议适配器 
22 Dalet引 
擎 
211 消息头 
解析器 
212 消息体 
解析器 
221 资源 
分发器 
222 操作 
映射器 
23 Dalet容器 
● Dalet1 
● Dalet2 
… 
http 
11表现层 
12逻辑层 
13持久层 
11表现层 
12逻辑层 
13持久层 
http 
Method+URI 
3 数据资源层 
2 数据访问服务(1) 
1应用系统 
2 数据访问服务(2) 
21 22 23 
HTTP/CM20 
JDBC 
Cache Hadoop 
Call 
DB2 MySQL Search 
APIs 
APIs 
专利201010506543.2数据访问方法及装置
15 
3 现状:DAL服务平台(简) 
 服务平台= LBL + DAL + IoC + API 
DAL Remote API 连接池管理序列化和压缩… HTTP 客户端序列化 
BSL基础服务平台 
Mina2框架 
IoService 
过 
滤 
器 
链 
DB2 
BS3框架 
—— 
IoC 
AOP 
JMX 
Log 
REST 
AUTH 
FSM 
XUMP 
… 
Oracle 
Mysql 
过滤器 
IoHandler 
DAL服务器 
NIO协议解析器 
http/cm20/um32/… 
数据访问引擎 
DalEngine 
Dalet扩展 
—— 
Auth 
PSqlid 
MCache 
MCacheAW 
MCacheNWR 
… 
序列化和压缩 
java/hessian/json/…/gz 
DB连接池管理 
c3p0/dbcp/bonecp/… 
线程池 
协议解析器 
… 
DAL Local API 
DalEngine Dalets 
… 
DB连接池管理 
中间件 
—— 
Tuxedo 
CICS
16 
3 现状:DAL在技术规划中的地位(简) 
负载均衡层 
CDN 区域负载 
(可选) 
F5/LVS 
+Nginx/ 
Haproxy 
Web前置集群综合前置FSL 
异 
步 
订阅 
同 
步 
服务层缓存层持久层 
综合前置 
FSL 
通知和反馈SMS,Email,IM,… 
Memcached 
+ Membase 
二级缓存 
应用切分 
水平切分 
垂直切分 
读写分离 
双主多从 
F5/LVS 
+Nginx/ 
Haproxy 
主备机制 
JMS 
Camel 
通过DAL访问 
NOSQL 
DB 
Hadoop / HBase 
… 
NameNode2 
复制 
Master DB 
(行式)在线交易库 
Master / Slave,分片,集群 
DataNodes 
同NameNode1 
步 
 
异 
步 
页面缓存 
Squid 
Varnish 
OSCache 
静态资源 
合作机构 
银行 
移动 
商户 
分布式服务集群 
应用服务:ASL 
基础服务:BSL 
数据访问:DAL 
监控服务定时服务:TSL 
集群 
… 
读 
(列式)离线分析库 
写 
读… 
复 
制 
Slave DB
17 
3 现状:DAL演化路线(第5阶段) 
 第一阶段目标:访问代理 
 平台中立【DAL服务端,基于Java开发,可部署在多个平台上】 
 语言中立【DAL客户端,支持多种语言访问(JAVA/PHP/C/Ruby/…)】 
 厂商中立【支持多个DB厂商;支持多个DB版本;支持NOSQL存储;】 
 第二阶段目标:服务治理 
 资源共享【多种连接池,共享连接池;共享缓存(MCached/EhCached)】 
 权限控制【身份认证,连接管理,访问控制】 
 访问日志【业务日志,SqlLog,BackLog】 
 监控优化【性能瓶颈分析和优化】 
 第三阶段目标:读写分离 
 读写分离【主从结构,需要额外的DB复制技术】 
 访问集群【负载均衡:HaProxy】 
 第四阶段目标:非结构化存储【针对RO/CO/KV/DO的访问】 
 参考:三备份/NWR模型/多版本/… 【无需额外的复制,使用NWR模型】 
 支持:Mcache/MongoDB/… 
 支持:MCache高可用模式【永远可写,永远可读】 
 第五阶段目标:数据分片 
 多种执行方式:普通SQL,预编译SQL,存储过程。 
 多种分片策略:单列|多列,无值|单值|多值,多库&多表 
 多种扩展方式:Java类扩展,脚本语言扩展。
18 
3*现状:多库同表的读写分离(问) 
 类名:Dalet4PSqlid 
 测试:DalClientsTest2.test_sqlrw_ok() 
 配置: 
 在dal_vrdb中配置虚拟数据库(第一为写库,其他为读库) 
 vdb.testdb = NWR111,testdb,testdb1,testdb2 
 在dal_psqlid中配置,以REST风格根据GET/PUT操作来区分读写。 
 GET.sql_select_user1 = SELECT id,other FROM user1 WHERE username like '%lius%' 
 PUT.sql_update_user1= UPDATE user1 SET `id`=id WHERE username like '%lius%’ 
 PUT.sql_select_user1 = SELECT id,other FROM user1 WHERE username like '%lius% 
 PUT.psql_select_user1= SELECT id,other FROM user1 WHERE username like '%lius% 
 测试(使用vdb.testdb而不是testdb) 
 http://localhost:8081/dal/psqlid2/vdb.testdb/sql_select_user1.txt 读库读(testdb1,testdb2) 
 http://localhost:8081/dal/psqlid2/vdb.testdb/sql_select_user1.txt?METHOD=PUT 写库写操作 
 http://localhost:8081/dal/psqlid2/vdb.testdb/sql_update_user1.txt?METHOD=PUT 写库读操作 
 http://localhost:8081/dal/psqlid2/testdb/sql_select_user1.txt 直接操作写库 
 http://localhost:8081/dal/psqlid2/testdb1/sql_select_user1.txt 直接操作读库 
 http://localhost:8081/dal/psqlid2/vdb.testdb/psql_select_user1.txt?METHOD=PUT 预编译 
 问题:如表名也不同?插入后查询操作间隔小于复制延迟?
19 
3 现状:同库多表的查询(问) 
 类名:Dalet4PSqlid 
 配置:在dal_psqlid中配置语句。 
 GET.user_cross0 = select * from user1,user2; 
 GET.user_union0 = select * from user1 union all select * from user2; 
 GET.user_cross = select user1.other,user2.other from user1,user2; 
 GET.user_union =select other from user1 union all select other from user2; 
 测试: 
 http://localhost:8081/dal/psqlid2/testdb/user_cross.txt 
 http://localhost:8081/dal/psqlid2/testdb/user_union.txt 
 问题: 
 上面两个查询语句的区别?
20 
4 设计和实现(简) 
 设计方案的要点 
 设计思路和原则 
 设计方案 
 设计要点 
 实现方案的权衡 
 多种执行方式:SQL,预编译SQL,存储过程 
 多种分片策略:。。。 
 多种扩展方式:Java扩展,JS扩展 
 脚本语言的选择: 
 新旧方式的对比: 
 其他相关约定项:命名规范优于配置。
21 
4*实现:设计方案的要点(问) 
 设计思路和原则 
 约定优于配置。 
 诚实原则:展现真实,不粉饰太平。 
LESS, 
BUT 
BETTER. 
LESS 
IS 
MORE. 
& 
 少即是多:用较少的类支持较多的分片策略(Do more with less) 
 二八原则:不过度设计。 
 做好20%最常用功能特性【有80%使用者将会用到】 
 覆盖80%大部分应用场景【剩下20%实现成本太高】 
 放弃部分功能特性【多线程并发/数据合并/二次排序/…】 
 设计方案 
 基于已有DAL架构平台,定义新的Dalet来访问分片数据。 
 实现方案 
 复用已有的Dalet4PSqlid类来扩展,尽量重用原有模式。 
 引入动态脚本语言来支持各种千变万化的分片策略。 
 问题 
 实现多线程并发查询?新建线程还是线程池?——优点?缺点? 
 实现跨库或跨表的查询结果合并,二次排序?——优点?缺点?
22 
4+实现:实现方案的权衡(问) 
 多种执行方式 
 支持: 普通SQL,预编译SQL 
 放弃存储过程【问:为什么放弃?】 
 多种分片策略 
 分片字段单列,多列【问:如何支持多列?】 
 字段的值无值,单值,多值【问:对应的场景?】 
 分片数据多库& 多表【问:需命名限定?】 
 多种扩展方式 
Java接口扩展脚本语言扩展 
定制方式基于接口实现基于脚本函数 
定制内容实现一个Java文件,并配置配置一行函数定义的脚本语言 
运行环境JAVA5+ JAVA6+ 
部署方式字节码或类库更新配置文件 
支持分片策略较少很多,基本覆盖所有场景 
支持动态加载N/A N/A 
混合多种分片多个类单个类:Dalet4Sharding
23 
4+实现:脚本语言的选择(问) 
 Java环境: 
 Java5:独立库,如Jython/JRuby/Jacl/BeanShell/JudoScript/OGNL/Rhino 
 Java6:全面支持JSR-223规范(Scripting for the Java Platform),内嵌 
Rhino解释器。对PHP、Groovy、BeanShell等语言的支持也正在进行中。 
 脚本语言的选择 
脚本语言/引擎Jre5 Jre6 jsr 运行效率熟悉程度其他 
Java 内嵌内嵌★★★★★ ★★★★★ 问:如何脚本化? 
Ruby/JRuby 库库jsr292 ★★★★ ★★☆ 
Python/Jython 库库jsr223 ★★★ ★★☆ 不支持单行配置 
JavaScript/Rhino 库内嵌jsr223 ★★☆ ★★★★ 
Groovy 库库jsr241 ★☆ ★★★ 兼容Java语法 
BeanShell 库库jsr274 ★ ★★★ 兼容Java语法 
Tcl/Jacl 库库BSF ☆ ★ 
Scala 库库支持N/A ★ 函数式 
PHP 库库jsr292 N/A ★★★★ 
Clojure 库库jsr331 N/A ★ 类Erlang
24 
4+实现:新旧方式的对比 
方式一(旧) 方式二(新) 
类实现类Dalet4PSqlid Dalet4Sharding 
功 
能 
特 
性 
防止SQL注入Dalet4PSqlidSafe 支持 
SQL和预编译SQL 支持支持 
存储过程支持不支持 
读写分离/同表名支持支持 
读写分离/不同表名不支持支持 
读写分离/单写表支持支持 
读写分离/多写表不支持支持 
同库多表查询简单支持(union all)完全支持 
多库多表查询不支持支持 
配 
置 
项 
文件格式Properties Properties 
脚本函数无需xxx.js = getTables() {…} 
xxx.js = getTables(id) {…} 
xxx.js = getTables(id1,id2) {…} 
全局定义IMPORT.FILE.JS=外部js函数库
25 
4*实现:相关约定项(问) 
 约定(Dalet4Sharding类) 
 分片策略:按照单一字段分片为主(将多字段分片转化为单字段分片) 
 分片参数 
 分片表名{SHARDING_TABLES} 
 分片字段值{SHARDING_KEY1},{ SHARDING_KEY2} 
 查询方式{SHARDING_FIRST}…只查第一个有效数据集 
 JS函数定义 
 getTables() 无SHARDING_KEY1和2参数 
 getTables(id) 仅有SHARDING_KEY1参数 
 getTables(id1,id2) 有SHARDING_KEY1和2参数 
 JS函数返回格式db1:s.tbl11,s.tbl12;db2:s2.tbl21,s2.tbl22;… 
 服务返回值类型Map<K,T> 
分隔符:逗号改分号?! 
 其中K=String/库名:表名,T={Exception|Integer|List<ROW>} 
 其中ROW= {JavaBean | Map<String,Object>} 
 其中Object = {String | Integer | Long | Double | …} 
 问题: 
 返回类型中ROW是否需要转JavaBean对象?有几种分隔符?
26 
5 使用和测试 
 环境准备和测试步骤 
 需求和示例(例1..例9) 
 功能测试(黄进) 
 性能自测(殷舒)
27 
5 使用:环境准备和测试步骤 
 1,准备数据 
 在testdb库中建user1和user2表,在twitter库中建user_1和user_2表。 
 2,配置testdb库参数,修改dal_vrdb配置 
 3,确定uri对应的Dalet类,修改dal_rest配置 
 /dal/shard2/ = com.bs3.app.dal.engine.Dalet4Sharding 
 /dal/shard2/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding 
 4,确定Dalet4Sharding类对应的配置,修改beans2配置 
 com.bs3.app.dal.engine.Dalet4Sharding = com/umpay/v3/dal/dal_sharding.properties 
 5,定义sqlid对应的sql和js语句,修改dal_sharding配置 
 GET.users_GetNoId = select {fields} from {SHARDING_TABLES} where username= '{name}' 
 GET.users_GetNoId.js = function getTables() { return "testdb:user1;testdb:user2"; } 
 6,部署并启动服务(参考bs3_demo.jar中DalServer2.java) 
 BeansServer.main_start("com/umpay/v3/dal/beans2.properties"); 
 7,使用HTTP来测试 
 http://localhost:8081/dal/shard2/users_GetNoId.xml?fields=*&name=lius 
 http://localhost:8081/dal/sharding/users_GetById.txt?fields=*&SHARDING_KEY1=1 
 http://localhost:8081/dal/sharding/users_GetById.txt?fields=*&SHARDING_KEY1=102 
 http://localhost:8081/dal/sharding/users_GetById.txt?fields=*&SHARDING_KEY1=2&SHARDING_K 
EY2=101
28 
5 例1:单列分片普通SQL全局查询 
 0,场景 
 根据单一字段进行分片,不区分分片字段类型。 
 客户端未提交SHARDING_KEY1和SHARDING_KEY2参数值。 
 5,配置(dal_sharding文件) 
 GET.users_GetNoId3= select username,other from {SHARDING_TABLES} where 
username like '{name}%' 
 GET.users_GetNoId3.js= function getTables() { return “testdb:user1;testdb:user2; 
twitter:user_1;twitter:user_2”; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/users_GetNoId.txt?name=lius 【返回】 
{"twitter:user_2":[],"twitter:user_1":[{"username":"lius_1","other":"刘胜 
_1"}],"testdb:user2":[{"username":"lius2","other":"刘胜 
"}],"testdb:user1":[{"username":"lius","other":"刘胜"}]} 
 其他: 
 容错:自动处理js脚本返回中包含的空格。
29 
5 例2:单列分片普通SQL单表查询(单条) 
 0,场景 
 根据单一字段进行分片,不区分分片字段类型。 
 客户端只提交SHARDING_KEY1参数值,无SHARDING_KEY2参数值。 
 5,配置(dal_sharding文件) 
 GET.users_GetById3= select username,other from {SHARDING_TABLES} where 
id='{SHARDING_KEY1}' 
 GET.users_GetById3.js= function getTables(id) { if (id<100) return "testdb:user1"; else 
return "testdb:user2"; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/users_GetById.txt?SHARDING_KEY1=1 【返回】 
{"testdb:user1":[{"username":"lius","other":"刘胜"}]} 
 http://localhost:8081/dal/shard2/users_GetById.txt?SHARDING_KEY1=101 【返回】 
{“testdb:user2”:[{“username”:“lius2”,“other”:“刘胜”}]}
30 
5 例3:单列分片普通SQL多表查询(范围) 
 0,场景 
 根据单一字段进行分片,不区分分片字段类型。 
 客户端同时提交SHARDING_KEY1和SHARDING_KEY2参数值。 
 5,配置(dal_sharding文件) 
 GET.psql_GetByRange3 = select id,other from {SHARDING_TABLES} where 
id>{SHARDING_KEY1} AND id<{SHARDING_KEY2} 
 GET.psql_GetByRange3.js = function getTables(id1,id2) { if (id1>=100) return 
"testdb:user2"; else if (id2<=100) return "testdb:user1"; else return 
"testdb:user1;testdb:user2"; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/users_GetByRange3.txt?SHARDING_KEY1=2&SHARD 
ING_KEY2=102 【返回】{"testdb:user2":[{"id":101,"other":"刘胜 
"}],"testdb:user1":[{"id":4,"other":"赵军"}]} 
 http://localhost:8081/dal/shard2/users_GetByRange3.txt?SHARDING_KEY1=3&SHARD 
ING_KEY2=99 【返回】{"testdb:user1":[{"id":4,"other":"赵军"}]} 
 http://localhost:8081/dal/shard2/users_GetByRange3.txt?SHARDING_KEY1=100&SHA 
RDING_KEY2=102 【返回】{“testdb:user2”:[{“id”:101,“other”:“刘胜”}]}
31 
5 例4:多列分片普通SQL查询 
 0,场景 
 根据多个字段混合计算的值进行分片,不区分分片字段类型。 
 客户端根据分片策略,使用多个字段的值计算生成SHARDING_KEY1/2等参数值。 
 服务端配置的SQL语句中无字段直接对应SHARDING_KEY1/2参数。而由JS脚本识别处理 
SHARDING_KEY1/2参数。 
 5,配置(dal_sharding文件) 
 GET.user_GetById3x= select username,create_at from {SHARDING_TABLES} where 
username like '%{name}%' 
 GET.user_GetById3x.js= function getTables(dt6) { if (dt6>"201300") return 
"testdb:user2"; else return "testdb:user1"; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/user_GetById3x.txt?SHARDING_KEY1=201001&name 
=liu 【返回】{"testdb:user1":[{"id":1,"username":"lius"}]} 
 http://localhost:8081/dal/shard2/user_GetById3x.txt?SHARDING_KEY1=201301&name 
=liu 【返回】{"testdb:user2":[{"id":101,"username":"lius2"}]}
32 
5 例5:单列分片预编译单表查询(字符串) 
 0,场景 
 按单一字段进行分片,不区分分片字段类型。但是,其他预编译参数需要区分类型。 
 客户端只提交SHARDING_KEY1参数值,无SHARDING_KEY2参数值。 
 5,配置(dal_sharding文件) 
 GET.psql_GetById3int = select id,username from {SHARDING_TABLES} where 
id={SHARDING_KEY1} OR username={name.string} 
 GET.psql_GetById3int.js = function getTables(id) { if (id<100) return "testdb:user1"; 
else return "testdb:user2"; } 
 GET.psql_GetById3str = select id,username from {SHARDING_TABLES} where 
username='{SHARDING_KEY1}' 
 GET.psql_GetById3str.js = function getTables(str) { if (str.indexOf("2")==-1) return 
"testdb:user1"; else return "testdb:user2"; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/psql_GetById3str.txt?SHARDING_KEY1=lius 【返回】 
{"testdb:user1":[{"id":1,"username":"lius"}]} 
 http://localhost:8081/dal/shard2/psql_GetById3str.txt?SHARDING_KEY1=lius2 【返回】 
{"testdb:user2":[{"id":101,"username":"lius2"}]} 
 http://localhost:8081/dal/shard2/psql_GetById3int.txt?SHARDING_KEY1=2&name.string=lius 
 http://localhost:8081/dal/shard2/psql_GetById3int.txt?SHARDING_KEY1=102&name.string=lius2 
 问题(见例6)
33 
5 例6:单列分片预编译多表查询(整型) 
 0,场景 
 按单一字段进行分片,分片字段不区分int和String类型,其他字段需要区分。 
 客户端同时提交SHARDING_KEY1和SHARDING_KEY2参数值。 
 5,配置(dal_sharding文件) 
 GET.psql_ByRange3int = select id,other from {SHARDING_TABLES} where 
id>{SHARDING_KEY1} AND id<{SHARDING_KEY2} 
 GET.psql_ByRange3int.js = function getTables(id1,id2) { if (id1>=100) return 
"testdb:user2"; else if (id2<=100) return "testdb:user1"; else return 
"testdb:user1;testdb:user2"; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/users_ByRange3int.txt?SHARDING_KEY1=2&SHARDI 
NG_KEY2=102 【返回】{"testdb:user2":[{"id":101,"other":"刘胜 
"}],"testdb:user1":[{"id":4,"other":"赵军"}]} 
 http://localhost:8081/dal/shard2/users_ByRange3int.txt?SHARDING_KEY1=3&SHARDI 
NG_KEY2=99 【返回】{"testdb:user1":[{"id":4,"other":"赵军"}]} 
 http://localhost:8081/dal/shard2/users_ByRange3int.txt?SHARDING_KEY1=100&SHAR 
DING_KEY2=102 【返回】{“testdb:user2”:[{“id”:101,“other”:“刘胜”}]} 
 问题: 
 有没有发现上面实现上的小花招?是否需要改进?
34 
5 例7:读写分离(不同库不同表) 
 0,场景 
 按单一字段进行分片,分片字段不区分int和String类型,其他字段需要区分。 
 允许多个写库以及不同表名。允许多个读库以及不同表名。 
 5,配置(dal_sharding文件) 
 GET.rwsplit_GetNoId3 = select username,other from {SHARDING_TABLES} 
where username like '{name}%' 
 GET.rwsplit_GetNoId3.js = function getTables() { return 
"testdb:user1;testdb:user2"; } 
 PUT.rwsplit_GetNoId3 = update {SHARDING_TABLES} set username=username 
where username like '{name}%' 
 PUT.rwsplit_GetNoId3.js = function getTables() { return 
"twitter:user_1;twitter:user_2"; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/rwsplit_GetNoId3.txt?name=lius 【返回】 
{"testdb:user2":[{"username":"lius2","other":"刘胜 
2"}],"testdb:user1":[{"username":"lius","other":"刘胜"}]} 
 http://localhost:8081/dal/shard2/rwsplit_GetNoId3.txt?name=lius&METHOD=PUT 【 
返回】{"twitter:user_2":0,"twitter:user_1":1}
35 
5 例8:交易表按七日翻滚 
 0,场景 
 交易表按照交易时间(日期)进行分片,不同日期的交易数据,记录在不同库不同表中。 
 每周一/二/三/…/日的交易数据,分别记录在日表trans_1/trans_2/…/trans_7中。 
 客户端根据需要提交SHARDING_KEY1和SHARDING_KEY2参数值。 
 5,配置(dal_sharding文件) 
 GET.trans7_GetNoId3 = select * from {SHARDING_TABLES} where id>0 
 GET.trans7_GetNoId3.js = function getTables() { var day = new Date().getDay(); return 
"testdb:trans_"+day; } 
 PUT.trans7_PutNoId3 = insert into {SHARDING_TABLES} (`id`, `dtime`, `content`) VALUES ('{id}', 
'{dtime}', '{content}‘) 
 PUT.trans7_PutNoId3.js = function getTables() { var day = new Date().getDay(); return 
"testdb:trans_"+day; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/trans7_GetNoId3.txt 【返回】 
{"testdb:trans_4":[{"id":1,"content":"交易信息4- 
1","dtime":"20130404000001"},{"id":2,"content":"交易信息4- 
2","dtime":"20130404000002"}]} 
 http://localhost:8081/dal/shard2/trans7_PutNoId3.txt&id=99&dtime=20130606000099&content=Ne 
wTrans99
36 
5 测试:功能测试(黄进) 
 参考:20130618分片测试(黄进).xlsx 
 共23个测试用例 
 1 同库不同表查询所有字段值,有数据情况 
 2 同库不同表查询所有字段值,无数据情况 
 3 同表不同库查询所有字段值,有数据 
 4 同表不同库查询所有字段值,无数据 
 5 不传SHARDING_KEY1和SHARDING_KEY2参数值有数据 
 6 不传SHARDING_KEY1和SHARDING_KEY2参数值无数据 
 7/8/9 同库不同表只传SHARDING_KEY1参数(STRING) 
 10/11/12 同库不同表只传SHARDING_KEY1参数(int) 
 13 同表不同库只传SHARDING_KEY1参数(int) 
 14 传SHARDING_KEY1和SHARDING_KEY2参数(string) 
 15 传SHARDING_KEY1和SHARDING_KEY2参数(int) 
 16 多片普通sql 
 17 单列分片预编译单表查询(int) 
 18 单列分片预编译单表查询(str) 
 19 单列分片预编译多表查询 
 20 读写分离 
 21/22/23 日切表查询测试/日切表新增记录/日切表修改记录
37 
5 测试:性能自测(殷舒) 
 测试:引入脚本语言对性能的影响 
 环境:CPU:Intel-Xeon-1.86GHz *4,MEM:4G, 10.10.38.135 
 工具:Apache/ab 
 命令:ab -c 100 -n 100000 -k 
 主机:http://10.10.38.135:8082/ 
 测试地址和结果: 【req/sec】 
1. /dal/psqlid/testdb-db2/sql_select.txt 【1419】 
2. /dal/psqlid/testdb-db2/psql_select.txt 【1589】 
3. /dal/shard2/users_GetById16.txt?SHARDING_KEY1=1 【1388】 
4. /dal/shard2/psql_GetById16.txt?SHARDING_KEY1=1 【1398 】 
 结论:引入脚本语言对性能影响不大 
 (1589-1419)/1589=10.7%,(1398-1388)/1398= 0.7% 
 ==》改用预编译方式,性能提高1~10%左右 
 (1589-1398)/1589=12.0%,(1419-1388)/1419= 2.2% 
 ==》引入脚本语言后,性能降低2~12%左右
38 
6 总结:需求满足度和示例 
 后端访问方式: 【基于JDBC的访问】 
 普通SQL语句【例1/2/3】 
 预编译SQL语句【例5/6】 
 数据分片策略: 
 按读写操作的类型:读写分离(不同表名|多写表) 【例7】 
 按分片数据库库名:相同库相同表,相同库不同表【例1/2/3/5/6】 
 按分片数据库表名:不同库相同表,不同库不同表【例1/2/3/5/6】 
 按单个分片字段的类型(整型,字符串,…) 【例5/6】 
 按单个分片字段值个数 
 没有分片字段值(多表全局查询) 【例1】 
 单个分片字段值(单表单条查询) 【例2,例5】 
 两个分片字段值(多表范围查询) 【例3,例6】 
 多个字段混合计算的结果进行分片【例4】 
 客户端:根据多个字段,计算产生单一分片关键字,转化为单字段分片。 
 服务端:根据分片关键字的值,确定访问的库和表。 
 新特性:优先查询【例9】 
 新特性:复杂分片策略(多行js脚本)
40 
6 小结:新需求-结果合并 
 局部有序,局部最优 
 无聚合字段 
 二路归并 
 LIMIT运算【多表分页】 
 有聚合字段 
 聚合规则处理【SUM,MAX,MIN,DISTINCT】 
 表达式求值 
 堆排序【合并排序】 
 LIMIT运算【多表分页】
41 
5+例9:普通SQL单列分片全局优先查询 
 0,场景 
 (同例1)区别在于,客户端提交SHARDING_FIRST参数值为true,按顺序依次查询各表;有 
结果就返回。且返回的Map中最多只有一项。 
 5,配置(dal_sharding文件) 
 GET.users_GetNoId3= select username,other from {SHARDING_TABLES} where 
username like '{name}%' 
 GET.users_GetNoId3.js= function getTables() { return “testdb:user1;testdb:user2; 
twitter:user_1;twitter:user_2”; } 
 7,测试地址和返回值 
 http://localhost:8081/dal/shard2/users_GetNoId.txt?name=lius&SHARDING_FIRST=tr 
ue 
 【true时】{"twitter:user_1":[{"username":"lius_1","other":"刘胜_1"}]} 
 【false时】{"twitter:user_2":[],"twitter:user_1":[{"username":"lius_1","other":"刘胜 
_1"}],"testdb:user2":[{"username":"lius2","other":"刘胜 
"}],"testdb:user1":[{"username":"lius","other":"刘胜"}]}
42 
5+例10-0:多表分页查询的参数 
 新增通信接口的参数 
字段说明描述 
PAGESIZE 分页大小大于0的整数 
PAGENUM 分页页码大于0的整数 
SHARDING_C 
分页计算 
OOKIES 
中间结果 
每次查询将各分片结果集大小响应给请求方。当以相 
同条件查询不同分页时,可将上次的 
sharding_cookies值提交服务端,提高服务端效率。 
 配置SQL语句的参数 
字段说明针对数据库例 
SHARDING_TAB 
LES 
分片表的表名(可以含 
Schema信息) 
根据JS返回值自动填充,格 
式:db1:s.tbl11,s.tbl12 
LIMIT_OFFSET 起始位:偏移值或行序号DB2、Oracle、MySql >=0 
LIMIT_OFFSET2 终止位:偏移值或行序号DB2、Oracle <5 
LIMIT_ROWS 记录数: MySql =5
43 
5+例10-0:多表分页查询的示例(殷舒) 
分片1 分片2 分片3 
PAGESIZE=3 
第一页第二页第三页第四页
44 
5+例10-0:多表分页查询的测试(刘胜) 
 测试环境: 
 共4个分表,各分表记录条数分别为4,4,2,2(范围0..11/共12条) 
 共4个分页,每页大小3。 
 测试结果: 
 http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=1 
 第1页:{"testdb:user1":[{"id":1},{"id":2},{"id":3}]} 
 第2页:{"testdb:user2":[{"id":101},{"id":102}],"testdb:user1":[{"id":4}]} 
 第3页:{"twitter:user_1":[{"id":1}],"testdb:user2":[{"id":103},{"id":104}]} 
 第4页:{"twitter:user_2":[{"id":2},{"id":4}],"twitter:user_1":[{"id":3}]} 
表范围/条第1页第2页第3页第4页 
全局行序号0..11/12 0..3/3 3..5/3 6..8/3 9..11/3 
testdb:user1 0..3/4 0..2/3 3..3/1 
testdb:user2 4..7/4 0..1/2 2..3/2 
twitter:user_1 8..9/2 0..0/1 1..1/1 
twitter:user_2 10..11/2 0..1/2 
各分页总记录数共3条共3条共3条共3条
45 
5+例10-1:多表分页(MySql) 
 0,场景 
 按多表的总记录数,计算各分表的分页参数;分页和排序字段一致,不做全局排序。 
 3,确定uri对应的Dalet类,修改dal_rest配置 
 /dal/shard3/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding4Page 
 4,确定Dalet4ShardingPaged类对应的配置,修改beans2配置 
 com.bs3.app.dal.engine.Dalet4Sharding4Page = com/umpay/v3/dal/dal_sharding.properties 
 5,定义sqlid对应的sql和js语句,修改dal_sharding配置 
 GET.MySqlPage = select id from {SHARDING_TABLES} where 1=1 limit {LIMIT_OFFSET},{LIMIT_ROWS} 
 GET.MySqlPage.js = function getTables() { return "testdb:user1;testdb:user2;twitter:user_1;twitter:user_2"; } 
 GET.MySqlPage.count = select COUNT(*) from {SHARDING_TABLES} where 1=1 
 7,使用HTTP来测试 
 http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=1 
 第1页:{"testdb:user1":[{"id":1},{"id":2},{"id":3}]} 
 http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=2 
 第2页:{"testdb:user2":[{"id":101},{"id":102}],"testdb:user1":[{"id":4}]} 
 http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=3 
 第3页:{"twitter:user_1":[{"id":1}],"testdb:user2":[{"id":103},{"id":104}]} 
 http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=4&SHARDING_COOKIE 
S=4_4_2_2 【注:缓存每个分片记录数】 
 第4页:{"twitter:user_2":[{"id":2},{"id":4}],"twitter:user_1":[{"id":3}]}
46 
5+例10-2:多表分页(MySql多预编译) 
 0,场景 
 按多表的总记录数,计算各分表的分页参数;分页和排序字段一致,不做全局排序。 
 3,确定uri对应的Dalet类,修改dal_rest配置 
 /dal/shard3/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding4Page 
 4,确定Dalet4ShardingPaged类对应的配置,修改beans2配置 
 com.bs3.app.dal.engine.Dalet4Sharding4Page = com/umpay/v3/dal/dal_sharding.properties 
 5,定义sqlid对应的sql和js语句,修改dal_sharding配置 
 GET.psql_MySqlPage = select id from {SHARDING_TABLES} where 1=1 limit {LIMIT_OFFSET},{LIMIT_ROWS} 
 GET.psql_MySqlPage.js = function getTables() { return "testdb:user1;testdb:user2;twitter:user_1;twitter:user_2"; } 
 GET.psql_MySqlPage.count = select count(*) from {SHARDING_TABLES} where 1=1 
 7,使用HTTP来测试 
 http://localhost:8081/dal/shard3/psql_MySqlPage.txt?PAGESIZE=5&PAGENUM=1 
 {"testdb:user2":[{"id":101}],"testdb:user1":[{"id":1},{"id":2},{"id":3},{"id":4}]} 
 http://localhost:8081/dal/shard3/psql_MySqlPage.txt?PAGESIZE=5&PAGENUM=2 
 {"twitter:user_2":[],"twitter:user_1":[{"id":1},{"id":3}],"testdb:user2":[{"id":102},{"id":103},{"id":104}]} 
 http://localhost:8081/dal/shard3/psql_MySqlPage.txt?PAGESIZE=5&PAGENUM=3&SHARDING_CO 
OKIES=4_4_2_2 
 {"twitter:user_2":[{"id":2},{"id":4}]}
47 
5+例10-3:多表分页(DB2) 
 0,场景 
 按多个表的总记录数,计算每个分表的分页参数; 
 分页和排序字段一致,无需重新做全局排序。 
 3,确定uri对应的Dalet类,修改dal_rest配置 
 /dal/shard3/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding4Page 
 4,确定Dalet4ShardingPaged类对应的配置,修改beans2配置 
 com.bs3.app.dal.engine.Dalet4Sharding4Page = com/umpay/v3/dal/dal_sharding.properties 
 5,定义sqlid对应的sql和js语句,修改dal_sharding配置 
 GET.Db2Page = SELECT * FROM (Select id,rownumber() over(ORDER BY id ASC) AS rn from {SHARDING_TABLES}) 
AS a WHERE a.rn >{LIMIT_OFFSET} AND a.rn<={LIMIT_OFFSET2} 
 GET.Db2Page.js = function getTables() { return "testdb:user1;testdb:user2;twitter:user_1;twitter:user_2"; } 
 GET.Db2Page.count = select COUNT(*) from {SHARDING_TABLES} 
 7,使用HTTP来测试 
 http://localhost:8081/dal/shard3/Db2Page.txt?PAGESIZE=3&PAGENUM=1 
 http://localhost:8081/dal/shard3/Db2Page.txt?PAGESIZE=3&PAGENUM=2 
 http://localhost:8081/dal/shard3/Db2Page.txt?PAGESIZE=3&PAGENUM=3
48 
6+小节:新需求(20131016) 
 需求1:结果集合并【不做排序】 
 需求2:动态SQL语句【不限于where】 
 方式1:通过缺省参数。【不能覆盖所有场景】 
 方式2:将where整体作为一个参数【部分支持】 
 特定模式下的格式约束——预编译/存储过程/分片模式 
 注:动态拼接sql,只能是普通SQL,不能支持预编译? 
 需求3:支持多表关联查询(隐含join操作) 
 场景1:只一个表是分片的。【完全支持】 
 场景2:多个表都是分片的,但都在同一个库【修改支持】 
 要求:分片字段相同,分片规则一致,相同库中保存。 
 select * from s.table11,s.table12 where … 
 select * from s.table11 AS T1,s.table12 AS T2 where 
T1.id=T2.id AND … 
 场景3:多个表都是分片的,且不在同一个库【不支持】
51 
9 总结:相关培训 
 20081113大型分布式系统架构技术简介(刘胜)中级.pdf 
 20081211大型软件系统开发综述(刘胜)中级.pdf /.mm/.png 
 20090219网络信息安全和认证技术(刘胜)中级.pdf 
 20090625服务器框架的概念和实践(刘胜)20121204.ppt 
 20090625分布式认证和授权技术(刘胜)初级.ppt 
 20090914系统架构师2009大会归来(刘胜)暨头脑风暴.ppt 
 20091224数据访问层DAL概念和实践1(刘胜).ppt 
 20100326数据访问层DAL概念和实践2(刘胜).ppt 
 20100902数据访问层DAL架构和实践3(刘胜).ppt 
 20101217软件技术大会和JavaOne大会归来(刘胜).ppt 
 20110223高可用可扩展的负载均衡层(卢翔,刘胜).ppt 
 20110801应用监控系统架构实践(刘胜).ppt 
 20120613数据访问层DAL架构和实践4(刘胜).pptx 
 20121012联动优势技术大会(刘胜)三年技术规划v1.3.4.pptx 
 20121013联动技术框架能力发布-BS3&DAL(殷舒).pptx 
 20121218联动优势技术平台介绍(for漫道科技)v5.pptx 
 20130326号码集合的存储和访问方法兼谈专利申请(刘胜).pptx 
 20130521联动优势企业架构介绍(刘胜).pptx 
 20130325理解REST设计模式和反模式(刘胜)草案.pptx
52 
Q9& 结A语:Q&A 
 现场问题——? 
 1,? 
 2,? 
 3,? 
 4,? 
 5,? 
 思考题—— 
 1,? 
 2,? 
 3,?
53 
附:JPA事务类型与EntityManager 
 表11-2 事务类型与EntityManager 
运行环境J2EE/EJB容器J2EE/Web容器J2SE 
应用托管的 
EntityManager 
 对比 
RESOURCE_LOC 
AL或JTA 
RESOURCE_LOC 
AL或JTA 
RESOURCE_LOC 
AL 
容器托管的 
EntityManager 
JTA 不支持不支持 
环境/容器/托管RESOURCE_LOCAL JTA 
J2EE/EJB/应用OK OK 
J2EE/EJB/容器OK OK 
J2EE/WEB/应用OK OK 
J2EE/WEB/容器OK 不支持 
J2SE OK 不支持 
集中式事务OK OK 
分布式事务不支持支持
54 
附:解析中间层技术架构(Java) 
1 
2 
3 
4 
5 
 模块组成 
 网络:JDBC协议解析(MySQL) 
 解析:{SqlParser | Cobar} 
 路由:基于{表名+字段值| SQLID} 
 执行:{SQL | PSQL | CALL} 
 结果集合并& 排序 
 访问方式 
 方案1:SQL@JDBC 
 使用MySql的JDBC驱动,使用ORM方式不变 
 方案2:SQLID@JDBC 
 使用自定义JDBC驱动,使用ORM方式不变 
 方案3:SQLID@{HTTP|CM20} 
 使用自定义通信协议,限制前端接口。
55 
附:使用DAL方式 
 使用DAL方式 
 方案1:直接使用BS3中API接口。 
 方案2:自定义封装CM20协议访问。 
 方案3:自定义封装HTTP协议访问。 
 方案4:封装伪JDBC驱动,访问固定sqlid地址 
 客户端:已有应用系统不用改动。 
 层次多:Args -> SQL -> URI -> 解析SQL -> 解析表和字段 
 陷阱多:特殊SQL、大数据集、多表JOIN、多表分片、
56 
6+小节:新需求(20140106) 
 需求1:同一库两表关联,关联字段不相同。 
 场景:表order和trans关联,都分片,且分片字段不同。 
 Order表按orderdate分片 
 Trans表按intime分片 
 解决:使用字段别名【需要验证】 
 需求2:基于{表名+字段值} 分片 
 场景:封装伪JDBC驱动统一访问DAL服务。 
 需求:统一URI,根据sql解析结果映射得到分片规则 
 需求3:跨库的两表关联查询 
 场景:交易表关联退费交易表,退费数据跨年。 
 方案:部分数据冗余,单个库创建13个月表。【只读库】
57 
爬虫技术 
 需求: 
 (赵晓庆)给出手机号码,从互联网上把个人基本信息补全,比如身份证号 
码、家庭住址、教育程度。能不能通过技术手段拿到微博、人人网、校友录 
等的用户资料不,还有婚庆网站、电商网站。 
 (李亚光)跟几次张总交流过程中,张都提到了抓取外部数据的事情,你看 
这个事有啥快速出成果的技术方案没?或者给指出个技术方向。现在要有这 
么一个框架,java最好能够通过指定url分层获取页面数据,并将数据静态 
化成文件。 
 方案: 
 获取页面信息——爬虫 
 根据语言:java,python,Go 
 根据页面:静态HTML,动态JS脚本 
 信息类型:非敏感信息,敏感信息 
 收集方式:匿名主动获取(爬虫),商户合作获取(购买),用户主动提交。 
 挖掘页面信息——半格式化文本挖掘。 
 半格式化的,文本挖掘。因为输入不规范,所以也没有什么稳定可靠的算法,需 
要根据输入随时调整。

20130626联动优势数据访问层DAL架构和实践5(刘胜)数据分片和分页

  • 1.
    数据访问层DAL架构和实践(5)数据分片 Version 5.8.20140312 刘胜liusheng@umpay.com 联动优势B办公区 北京市西城区安德路甲104号证通商务楼F6层(100120)
  • 2.
    2 0 版本历史  关于DAL的历史版本  20091224数据访问层DAL概念和实践1(刘胜).ppt 【概念/设计/原型】  20100330数据访问层DAL概念和实践2(刘胜).ppt 【实现/统一/远景】  20100902数据访问层DAL架构和实践3(刘胜).ppt 【接口/本地/模板】  专利201010506543.2 数据访问方法及装置  20120613数据访问层DAL架构和实践4(刘胜).pptx 【新特性….】  20130606数据访问层DAL架构和实践5(刘胜).pptx 【数据分片】  最近培训材料  20120613数据访问层DAL架构和实践4新的特性(刘胜).pptx  20121012联动优势技术大会(刘胜)三年技术规划v1.3.4.pptx  20121208联动优势2012年度述职报告(刘胜)1206正式版.pptx  20130325理解REST设计模式和反模式(刘胜)草案.pptx  20130326号码集合的存储和访问方法兼谈专利申请(刘胜).pptx  20130604代码质量和单元测试(刘胜).pptx  20130604联动优势企业架构介绍(刘胜).pptx  20130606数据访问层DAL架构和实践5数据分片(刘胜).pptx
  • 3.
    3 0*内容大纲(耗时1H+) 1 概念  数据访问层DAL  数据库分区与分片  高可用数据存储架构  2 背景  问题,需求,典型场景  3 现状(DAL实现)  关键技术选择,演化路线,专利产品,服务平台,在系统中的地位  4 设计和实现  设计方案要点,实现方案权衡,脚本语言选择,相关约定  5 使用和测试  用例:环境准备和测试步骤,例1/2/3/…/8,例9  测试:功能测试,性能自测  6 总结  需求满足度,新需求,相关培训,发布地址,参考索引, Q&A
  • 4.
    4 1*概念:高可用数据存储架构(问) Share-Anything架构  案例:Oracle RAC共享缓存和存储  缺点:扩展能力有限(<10)  Share-Storage主备架构  案例:DB2双机主备方案  缺点:存储是单点  Share-Nothing分区架构  案例:DB2v9分区方案  缺点:管理节点是单点?  Share-Nothing主从架构(问:区别?)  案例:MySQL主从单向复制  缺点:写库是单点,也是瓶颈  Share-Nothing主主架构  案例:MySQL主从双向复制  缺点:未读写分离  Share-Nothing双主多从  优点:写库非单点,第一写库可停机  缺点:写库是瓶颈,第二写库不能停机 DAL 代理?
  • 5.
    5 1 概念:数据库分区与分片  分片vs. 分区 分片(Sharding) 分区(Partition) 技术支持中间件开发商(多库) 数据库厂商 存储依赖可跨DB类型,可跨物理主机不能跨DB类型,可跨表空间 数据划分根据ID,时间,范围等根据ID,时间,范围,Hash,混合分区等 存储方式分布式集中式/分布式 扩展性横向扩展(Scale-Out) 纵向扩展(Scale-Up) 可用性无单点存在单点(DB管理节点) 价格低廉适中(DAS直连式存储),昂贵(NAS或SAN) 应用场景常见于Web2.0网站多数传统应用  开源软件:  1,Hibernate Shards  2,HiveDB/Hibernate shards:横向切分,可跨服务器数据,但不能做order/join  3,MySql/Proxy + Hscal插件,不能跨服务器的  4,Spock Proxy:是3的分支:a)消除LUA脚本。b)客户端登录验证。c) 动态连接池。  5,PL/Proxy:针对PL语言,其理念是代理远程函数调用同样标签创建的函数。  6,PyShards:基于Python的Sharding方案,仅支持MySQL库。
  • 6.
    6 1+概念:数据分片Shard 实现shard 需要改变思维(DB设计,App使用)  尽量避免join - 关联查询  数据冗余/ 反范式  例:数据冗余for shard  shard before – comment(id, blog_id, content)  shard after – comment(id, blog_id, content, user_id)  常见shard 策略  垂直分片  按功能分【如:论坛,博客】  水平分片  2 * N 【如定单,购买者与网店各一份】  N / n 【按日期或ID 范围分区】  Hash(N) % n 【按hash 分】  分区表查找法
  • 7.
    7 A P P User1 User2 DAL Proxy 1*概念:数据访问层DAL实现  实现方式  进程内DAL API 接口组件  Java: Hibernate Shard,HiveDB, …  Python: Pyshards  进程外DAL Proxy 服务器  MySQL:MySql/Proxy,Amoeba,Cobar  PgSQL: PL/Proxy (Skype), PgPool-II  考虑因素:  业务场景:业务应用多少?——有很多  功能需求:需求是否固定?——不固定  分片规则:是否一成不变?——不确定 User1 User2 DAL API A P P
  • 8.
    8 1*概念:数据访问层DAL/Proxy 数据访问层DAL的定义  Data Access Layer(Layer=Services)  DAL是一系列服务的集合,是DAO的自然延伸,是 SOA的具体实现。  数据访问层DAL的好处  简化客户端接口,规范数据访问操作  保持客户端接口,动态升级服务端实现  支持更多客户端:Java/C/PHP/…  共享数据库连接,减少总数据连接数  更细粒度访问控制  方便实施服务治理:权限/管理/监控/分析/优化/…  方便实施数据库的迁移/升级/重构/…  方便实施读写分离,支持高并发访问  方便实施数据分片,支持海量数据库  。。。
  • 9.
    9 1+背景:已有的DAL Proxy方案  方案1:Mysql-Proxy@MySQL官方  不支持分库分表,且性能较差。  方案2:Atlas@Qihoo360(王超)  基于mysql-proxy-0.8.2版  方案3:Amoeba@盛大(陈思儒)  for {MySQL | Aladdin | MongoDB}  不支持事务/存储过程/分库分表/输出大结果集。  方案4:Cobar@AliBaBa(贺贤懋)  基于amoeba-0.34版  开源版只支持MySQL,不支持读写分离。  方案5:TDDL@Taobao  复杂度相对较高,文档较少。  只开源动态数据源,不开源分库分表。  方案6:S3@Amazon  基于标准REST和SOAP接口的超级SAN存储
  • 10.
    10 2*背景:问题(简) 问题1:核心数据库不堪重负(201106)  数据量大  用户表:tuser + tuserBank  交易表:transAll  七天翻滚表  数据冗余 要么分区,要么分片。 分区成本高,依赖厂商技术。 分片阻力大,应用端改动多。  数据表多个版本混存,每个用户记录两次,每条交易记录两次。【双写】  个别应用过度依赖于TransAll表,未做日表翻滚。【分片】  系统迁移不彻底,有残留数据。(用户管理系统:望京丰台) 【多查】  读写不均【读写分离】  淘宝读写比测算:10:1(来自iDataForum)  联动读写比估算:用户表(20:1),交易表(2:1)  连接池问题: 【连接共享】  应用太多太分散,容易使数据库连接总数超限,导致DB中断  问题2:针对复杂的数据库部署,应用开发困难  同时连多个数据库 对于简单分片, 也能凑活实现。  依次查多个数据库表:丰台-望京;七天翻滚表;在线-离线库
  • 11.
    11 2*背景:需求 最终目的:替核心数据库减负,同时降低开发难度  数据冗余:根据读写操作,使用不同的库。【Full-DB】  横向拆分:根据应用拆分多个库。【ShardDB】  纵向拆分:根据数据拆分多个库和多个表。【ShardDB】  需求细分: 【 实际场景千变万化]  后端访问方式: 【暂不考虑NOSQL存储】  基于JDBC的访问【普通SQL/预编译SQL/存储过程】  数据分片策略:  按读写操作的类型【读写分离】  按分片数据库库名【相同库相同表,相同库不同表】  按分片数据库表名【不同库相同表,不同库不同表】 以不变应万变  按单个分片字段的类型【整数ID值,字符串DT时间】  按单个分片字段值个数  没有分片字段值(全局) 【全局查询】  单个分片字段值(单条) 【单表查询】  两个分片字段值(范围) 【多表查询】 如何  按多个分片字段值分片,通过计算产生单一分片关键字【如hash】
  • 12.
    12 2*背景:典型应用场景 场景1:客服系统/海量短信日志查询  说明:将短信日志归档到多个MySQL库的多张日表。  查询:按日期查单个表/按日期范围查多个表/按手机号询全部表  场景2:通信账户/海量支付交易日志查询  说明:将系统业务日志归档到多个MySQL库的多张日表。  查询: (同场景1)  场景3:用户管理/数据迁移后的冗余查询  说明:系统从望京迁移到丰台,但有部分遗留数据未作迁移。  查询:先查丰台数据库,如未找到,则再次查望京数据库。  场景4:用户管理/用户信息分表查询  说明:将用户表按手机号拆分到多个库的多张表中,并提供查询。  查询:按手机号查询单张表/按其他字段查询全部表。  场景5:交易表按7日翻滚  说明:根据交易日期所在的星期几,选择对应的表进行操作。  查询:按交易日期查单张表/按日期范围查多张表/按其他字段查全部表
  • 13.
    13 3 现状:DAL关键技术的选择(简)  1,服务组织风格【RESTful】  2,中间层编程语言【Java vs C/C++】  3,后端存储方式【NO-RDB】  4,前端访问接口【NO-JDBC,NO-SQL】  NO-SQL 【用sqlid/callid替代sql语句】  抛弃复杂的sql语句,解析更容易,执行更安全。  NO-JDBC 【可支持NOSQL存储】  抛弃复杂的jdbc,服务端更容易解析  抛弃复杂的jdbc,客户端更容易使用  还可支持更多的存储方式:1)NOSQL库;2)服务(如Tuxedo)  5,交互协议(传输协议+数据协议)  数据传输协议【HTTP/CM20/…】  序列化技术【java+gz/hessian2+gz/xml/json/…】  结果集表示【List<Map<String,Object>>】  6,负载均衡: 【避免单点】 NO=Not Only
  • 14.
    14 3 现状:DAL专利产品(简) 1 应用逻辑层2 数据访问层(DAL) 1 应用系统 21 NIO网络 协议适配器 22 Dalet引 擎 211 消息头 解析器 212 消息体 解析器 221 资源 分发器 222 操作 映射器 23 Dalet容器 ● Dalet1 ● Dalet2 … http 11表现层 12逻辑层 13持久层 11表现层 12逻辑层 13持久层 http Method+URI 3 数据资源层 2 数据访问服务(1) 1应用系统 2 数据访问服务(2) 21 22 23 HTTP/CM20 JDBC Cache Hadoop Call DB2 MySQL Search APIs APIs 专利201010506543.2数据访问方法及装置
  • 15.
    15 3 现状:DAL服务平台(简)  服务平台= LBL + DAL + IoC + API DAL Remote API 连接池管理序列化和压缩… HTTP 客户端序列化 BSL基础服务平台 Mina2框架 IoService 过 滤 器 链 DB2 BS3框架 —— IoC AOP JMX Log REST AUTH FSM XUMP … Oracle Mysql 过滤器 IoHandler DAL服务器 NIO协议解析器 http/cm20/um32/… 数据访问引擎 DalEngine Dalet扩展 —— Auth PSqlid MCache MCacheAW MCacheNWR … 序列化和压缩 java/hessian/json/…/gz DB连接池管理 c3p0/dbcp/bonecp/… 线程池 协议解析器 … DAL Local API DalEngine Dalets … DB连接池管理 中间件 —— Tuxedo CICS
  • 16.
    16 3 现状:DAL在技术规划中的地位(简) 负载均衡层 CDN 区域负载 (可选) F5/LVS +Nginx/ Haproxy Web前置集群综合前置FSL 异 步 订阅 同 步 服务层缓存层持久层 综合前置 FSL 通知和反馈SMS,Email,IM,… Memcached + Membase 二级缓存 应用切分 水平切分 垂直切分 读写分离 双主多从 F5/LVS +Nginx/ Haproxy 主备机制 JMS Camel 通过DAL访问 NOSQL DB Hadoop / HBase … NameNode2 复制 Master DB (行式)在线交易库 Master / Slave,分片,集群 DataNodes 同NameNode1 步 异 步 页面缓存 Squid Varnish OSCache 静态资源 合作机构 银行 移动 商户 分布式服务集群 应用服务:ASL 基础服务:BSL 数据访问:DAL 监控服务定时服务:TSL 集群 … 读 (列式)离线分析库 写 读… 复 制 Slave DB
  • 17.
    17 3 现状:DAL演化路线(第5阶段)  第一阶段目标:访问代理  平台中立【DAL服务端,基于Java开发,可部署在多个平台上】  语言中立【DAL客户端,支持多种语言访问(JAVA/PHP/C/Ruby/…)】  厂商中立【支持多个DB厂商;支持多个DB版本;支持NOSQL存储;】  第二阶段目标:服务治理  资源共享【多种连接池,共享连接池;共享缓存(MCached/EhCached)】  权限控制【身份认证,连接管理,访问控制】  访问日志【业务日志,SqlLog,BackLog】  监控优化【性能瓶颈分析和优化】  第三阶段目标:读写分离  读写分离【主从结构,需要额外的DB复制技术】  访问集群【负载均衡:HaProxy】  第四阶段目标:非结构化存储【针对RO/CO/KV/DO的访问】  参考:三备份/NWR模型/多版本/… 【无需额外的复制,使用NWR模型】  支持:Mcache/MongoDB/…  支持:MCache高可用模式【永远可写,永远可读】  第五阶段目标:数据分片  多种执行方式:普通SQL,预编译SQL,存储过程。  多种分片策略:单列|多列,无值|单值|多值,多库&多表  多种扩展方式:Java类扩展,脚本语言扩展。
  • 18.
    18 3*现状:多库同表的读写分离(问) 类名:Dalet4PSqlid  测试:DalClientsTest2.test_sqlrw_ok()  配置:  在dal_vrdb中配置虚拟数据库(第一为写库,其他为读库)  vdb.testdb = NWR111,testdb,testdb1,testdb2  在dal_psqlid中配置,以REST风格根据GET/PUT操作来区分读写。  GET.sql_select_user1 = SELECT id,other FROM user1 WHERE username like '%lius%'  PUT.sql_update_user1= UPDATE user1 SET `id`=id WHERE username like '%lius%’  PUT.sql_select_user1 = SELECT id,other FROM user1 WHERE username like '%lius%  PUT.psql_select_user1= SELECT id,other FROM user1 WHERE username like '%lius%  测试(使用vdb.testdb而不是testdb)  http://localhost:8081/dal/psqlid2/vdb.testdb/sql_select_user1.txt 读库读(testdb1,testdb2)  http://localhost:8081/dal/psqlid2/vdb.testdb/sql_select_user1.txt?METHOD=PUT 写库写操作  http://localhost:8081/dal/psqlid2/vdb.testdb/sql_update_user1.txt?METHOD=PUT 写库读操作  http://localhost:8081/dal/psqlid2/testdb/sql_select_user1.txt 直接操作写库  http://localhost:8081/dal/psqlid2/testdb1/sql_select_user1.txt 直接操作读库  http://localhost:8081/dal/psqlid2/vdb.testdb/psql_select_user1.txt?METHOD=PUT 预编译  问题:如表名也不同?插入后查询操作间隔小于复制延迟?
  • 19.
    19 3 现状:同库多表的查询(问)  类名:Dalet4PSqlid  配置:在dal_psqlid中配置语句。  GET.user_cross0 = select * from user1,user2;  GET.user_union0 = select * from user1 union all select * from user2;  GET.user_cross = select user1.other,user2.other from user1,user2;  GET.user_union =select other from user1 union all select other from user2;  测试:  http://localhost:8081/dal/psqlid2/testdb/user_cross.txt  http://localhost:8081/dal/psqlid2/testdb/user_union.txt  问题:  上面两个查询语句的区别?
  • 20.
    20 4 设计和实现(简)  设计方案的要点  设计思路和原则  设计方案  设计要点  实现方案的权衡  多种执行方式:SQL,预编译SQL,存储过程  多种分片策略:。。。  多种扩展方式:Java扩展,JS扩展  脚本语言的选择:  新旧方式的对比:  其他相关约定项:命名规范优于配置。
  • 21.
    21 4*实现:设计方案的要点(问) 设计思路和原则  约定优于配置。  诚实原则:展现真实,不粉饰太平。 LESS, BUT BETTER. LESS IS MORE. &  少即是多:用较少的类支持较多的分片策略(Do more with less)  二八原则:不过度设计。  做好20%最常用功能特性【有80%使用者将会用到】  覆盖80%大部分应用场景【剩下20%实现成本太高】  放弃部分功能特性【多线程并发/数据合并/二次排序/…】  设计方案  基于已有DAL架构平台,定义新的Dalet来访问分片数据。  实现方案  复用已有的Dalet4PSqlid类来扩展,尽量重用原有模式。  引入动态脚本语言来支持各种千变万化的分片策略。  问题  实现多线程并发查询?新建线程还是线程池?——优点?缺点?  实现跨库或跨表的查询结果合并,二次排序?——优点?缺点?
  • 22.
    22 4+实现:实现方案的权衡(问) 多种执行方式  支持: 普通SQL,预编译SQL  放弃存储过程【问:为什么放弃?】  多种分片策略  分片字段单列,多列【问:如何支持多列?】  字段的值无值,单值,多值【问:对应的场景?】  分片数据多库& 多表【问:需命名限定?】  多种扩展方式 Java接口扩展脚本语言扩展 定制方式基于接口实现基于脚本函数 定制内容实现一个Java文件,并配置配置一行函数定义的脚本语言 运行环境JAVA5+ JAVA6+ 部署方式字节码或类库更新配置文件 支持分片策略较少很多,基本覆盖所有场景 支持动态加载N/A N/A 混合多种分片多个类单个类:Dalet4Sharding
  • 23.
    23 4+实现:脚本语言的选择(问) Java环境:  Java5:独立库,如Jython/JRuby/Jacl/BeanShell/JudoScript/OGNL/Rhino  Java6:全面支持JSR-223规范(Scripting for the Java Platform),内嵌 Rhino解释器。对PHP、Groovy、BeanShell等语言的支持也正在进行中。  脚本语言的选择 脚本语言/引擎Jre5 Jre6 jsr 运行效率熟悉程度其他 Java 内嵌内嵌★★★★★ ★★★★★ 问:如何脚本化? Ruby/JRuby 库库jsr292 ★★★★ ★★☆ Python/Jython 库库jsr223 ★★★ ★★☆ 不支持单行配置 JavaScript/Rhino 库内嵌jsr223 ★★☆ ★★★★ Groovy 库库jsr241 ★☆ ★★★ 兼容Java语法 BeanShell 库库jsr274 ★ ★★★ 兼容Java语法 Tcl/Jacl 库库BSF ☆ ★ Scala 库库支持N/A ★ 函数式 PHP 库库jsr292 N/A ★★★★ Clojure 库库jsr331 N/A ★ 类Erlang
  • 24.
    24 4+实现:新旧方式的对比 方式一(旧)方式二(新) 类实现类Dalet4PSqlid Dalet4Sharding 功 能 特 性 防止SQL注入Dalet4PSqlidSafe 支持 SQL和预编译SQL 支持支持 存储过程支持不支持 读写分离/同表名支持支持 读写分离/不同表名不支持支持 读写分离/单写表支持支持 读写分离/多写表不支持支持 同库多表查询简单支持(union all)完全支持 多库多表查询不支持支持 配 置 项 文件格式Properties Properties 脚本函数无需xxx.js = getTables() {…} xxx.js = getTables(id) {…} xxx.js = getTables(id1,id2) {…} 全局定义IMPORT.FILE.JS=外部js函数库
  • 25.
    25 4*实现:相关约定项(问) 约定(Dalet4Sharding类)  分片策略:按照单一字段分片为主(将多字段分片转化为单字段分片)  分片参数  分片表名{SHARDING_TABLES}  分片字段值{SHARDING_KEY1},{ SHARDING_KEY2}  查询方式{SHARDING_FIRST}…只查第一个有效数据集  JS函数定义  getTables() 无SHARDING_KEY1和2参数  getTables(id) 仅有SHARDING_KEY1参数  getTables(id1,id2) 有SHARDING_KEY1和2参数  JS函数返回格式db1:s.tbl11,s.tbl12;db2:s2.tbl21,s2.tbl22;…  服务返回值类型Map<K,T> 分隔符:逗号改分号?!  其中K=String/库名:表名,T={Exception|Integer|List<ROW>}  其中ROW= {JavaBean | Map<String,Object>}  其中Object = {String | Integer | Long | Double | …}  问题:  返回类型中ROW是否需要转JavaBean对象?有几种分隔符?
  • 26.
    26 5 使用和测试  环境准备和测试步骤  需求和示例(例1..例9)  功能测试(黄进)  性能自测(殷舒)
  • 27.
    27 5 使用:环境准备和测试步骤  1,准备数据  在testdb库中建user1和user2表,在twitter库中建user_1和user_2表。  2,配置testdb库参数,修改dal_vrdb配置  3,确定uri对应的Dalet类,修改dal_rest配置  /dal/shard2/ = com.bs3.app.dal.engine.Dalet4Sharding  /dal/shard2/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding  4,确定Dalet4Sharding类对应的配置,修改beans2配置  com.bs3.app.dal.engine.Dalet4Sharding = com/umpay/v3/dal/dal_sharding.properties  5,定义sqlid对应的sql和js语句,修改dal_sharding配置  GET.users_GetNoId = select {fields} from {SHARDING_TABLES} where username= '{name}'  GET.users_GetNoId.js = function getTables() { return "testdb:user1;testdb:user2"; }  6,部署并启动服务(参考bs3_demo.jar中DalServer2.java)  BeansServer.main_start("com/umpay/v3/dal/beans2.properties");  7,使用HTTP来测试  http://localhost:8081/dal/shard2/users_GetNoId.xml?fields=*&name=lius  http://localhost:8081/dal/sharding/users_GetById.txt?fields=*&SHARDING_KEY1=1  http://localhost:8081/dal/sharding/users_GetById.txt?fields=*&SHARDING_KEY1=102  http://localhost:8081/dal/sharding/users_GetById.txt?fields=*&SHARDING_KEY1=2&SHARDING_K EY2=101
  • 28.
    28 5 例1:单列分片普通SQL全局查询  0,场景  根据单一字段进行分片,不区分分片字段类型。  客户端未提交SHARDING_KEY1和SHARDING_KEY2参数值。  5,配置(dal_sharding文件)  GET.users_GetNoId3= select username,other from {SHARDING_TABLES} where username like '{name}%'  GET.users_GetNoId3.js= function getTables() { return “testdb:user1;testdb:user2; twitter:user_1;twitter:user_2”; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/users_GetNoId.txt?name=lius 【返回】 {"twitter:user_2":[],"twitter:user_1":[{"username":"lius_1","other":"刘胜 _1"}],"testdb:user2":[{"username":"lius2","other":"刘胜 "}],"testdb:user1":[{"username":"lius","other":"刘胜"}]}  其他:  容错:自动处理js脚本返回中包含的空格。
  • 29.
    29 5 例2:单列分片普通SQL单表查询(单条)  0,场景  根据单一字段进行分片,不区分分片字段类型。  客户端只提交SHARDING_KEY1参数值,无SHARDING_KEY2参数值。  5,配置(dal_sharding文件)  GET.users_GetById3= select username,other from {SHARDING_TABLES} where id='{SHARDING_KEY1}'  GET.users_GetById3.js= function getTables(id) { if (id<100) return "testdb:user1"; else return "testdb:user2"; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/users_GetById.txt?SHARDING_KEY1=1 【返回】 {"testdb:user1":[{"username":"lius","other":"刘胜"}]}  http://localhost:8081/dal/shard2/users_GetById.txt?SHARDING_KEY1=101 【返回】 {“testdb:user2”:[{“username”:“lius2”,“other”:“刘胜”}]}
  • 30.
    30 5 例3:单列分片普通SQL多表查询(范围)  0,场景  根据单一字段进行分片,不区分分片字段类型。  客户端同时提交SHARDING_KEY1和SHARDING_KEY2参数值。  5,配置(dal_sharding文件)  GET.psql_GetByRange3 = select id,other from {SHARDING_TABLES} where id>{SHARDING_KEY1} AND id<{SHARDING_KEY2}  GET.psql_GetByRange3.js = function getTables(id1,id2) { if (id1>=100) return "testdb:user2"; else if (id2<=100) return "testdb:user1"; else return "testdb:user1;testdb:user2"; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/users_GetByRange3.txt?SHARDING_KEY1=2&SHARD ING_KEY2=102 【返回】{"testdb:user2":[{"id":101,"other":"刘胜 "}],"testdb:user1":[{"id":4,"other":"赵军"}]}  http://localhost:8081/dal/shard2/users_GetByRange3.txt?SHARDING_KEY1=3&SHARD ING_KEY2=99 【返回】{"testdb:user1":[{"id":4,"other":"赵军"}]}  http://localhost:8081/dal/shard2/users_GetByRange3.txt?SHARDING_KEY1=100&SHA RDING_KEY2=102 【返回】{“testdb:user2”:[{“id”:101,“other”:“刘胜”}]}
  • 31.
    31 5 例4:多列分片普通SQL查询  0,场景  根据多个字段混合计算的值进行分片,不区分分片字段类型。  客户端根据分片策略,使用多个字段的值计算生成SHARDING_KEY1/2等参数值。  服务端配置的SQL语句中无字段直接对应SHARDING_KEY1/2参数。而由JS脚本识别处理 SHARDING_KEY1/2参数。  5,配置(dal_sharding文件)  GET.user_GetById3x= select username,create_at from {SHARDING_TABLES} where username like '%{name}%'  GET.user_GetById3x.js= function getTables(dt6) { if (dt6>"201300") return "testdb:user2"; else return "testdb:user1"; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/user_GetById3x.txt?SHARDING_KEY1=201001&name =liu 【返回】{"testdb:user1":[{"id":1,"username":"lius"}]}  http://localhost:8081/dal/shard2/user_GetById3x.txt?SHARDING_KEY1=201301&name =liu 【返回】{"testdb:user2":[{"id":101,"username":"lius2"}]}
  • 32.
    32 5 例5:单列分片预编译单表查询(字符串)  0,场景  按单一字段进行分片,不区分分片字段类型。但是,其他预编译参数需要区分类型。  客户端只提交SHARDING_KEY1参数值,无SHARDING_KEY2参数值。  5,配置(dal_sharding文件)  GET.psql_GetById3int = select id,username from {SHARDING_TABLES} where id={SHARDING_KEY1} OR username={name.string}  GET.psql_GetById3int.js = function getTables(id) { if (id<100) return "testdb:user1"; else return "testdb:user2"; }  GET.psql_GetById3str = select id,username from {SHARDING_TABLES} where username='{SHARDING_KEY1}'  GET.psql_GetById3str.js = function getTables(str) { if (str.indexOf("2")==-1) return "testdb:user1"; else return "testdb:user2"; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/psql_GetById3str.txt?SHARDING_KEY1=lius 【返回】 {"testdb:user1":[{"id":1,"username":"lius"}]}  http://localhost:8081/dal/shard2/psql_GetById3str.txt?SHARDING_KEY1=lius2 【返回】 {"testdb:user2":[{"id":101,"username":"lius2"}]}  http://localhost:8081/dal/shard2/psql_GetById3int.txt?SHARDING_KEY1=2&name.string=lius  http://localhost:8081/dal/shard2/psql_GetById3int.txt?SHARDING_KEY1=102&name.string=lius2  问题(见例6)
  • 33.
    33 5 例6:单列分片预编译多表查询(整型)  0,场景  按单一字段进行分片,分片字段不区分int和String类型,其他字段需要区分。  客户端同时提交SHARDING_KEY1和SHARDING_KEY2参数值。  5,配置(dal_sharding文件)  GET.psql_ByRange3int = select id,other from {SHARDING_TABLES} where id>{SHARDING_KEY1} AND id<{SHARDING_KEY2}  GET.psql_ByRange3int.js = function getTables(id1,id2) { if (id1>=100) return "testdb:user2"; else if (id2<=100) return "testdb:user1"; else return "testdb:user1;testdb:user2"; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/users_ByRange3int.txt?SHARDING_KEY1=2&SHARDI NG_KEY2=102 【返回】{"testdb:user2":[{"id":101,"other":"刘胜 "}],"testdb:user1":[{"id":4,"other":"赵军"}]}  http://localhost:8081/dal/shard2/users_ByRange3int.txt?SHARDING_KEY1=3&SHARDI NG_KEY2=99 【返回】{"testdb:user1":[{"id":4,"other":"赵军"}]}  http://localhost:8081/dal/shard2/users_ByRange3int.txt?SHARDING_KEY1=100&SHAR DING_KEY2=102 【返回】{“testdb:user2”:[{“id”:101,“other”:“刘胜”}]}  问题:  有没有发现上面实现上的小花招?是否需要改进?
  • 34.
    34 5 例7:读写分离(不同库不同表)  0,场景  按单一字段进行分片,分片字段不区分int和String类型,其他字段需要区分。  允许多个写库以及不同表名。允许多个读库以及不同表名。  5,配置(dal_sharding文件)  GET.rwsplit_GetNoId3 = select username,other from {SHARDING_TABLES} where username like '{name}%'  GET.rwsplit_GetNoId3.js = function getTables() { return "testdb:user1;testdb:user2"; }  PUT.rwsplit_GetNoId3 = update {SHARDING_TABLES} set username=username where username like '{name}%'  PUT.rwsplit_GetNoId3.js = function getTables() { return "twitter:user_1;twitter:user_2"; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/rwsplit_GetNoId3.txt?name=lius 【返回】 {"testdb:user2":[{"username":"lius2","other":"刘胜 2"}],"testdb:user1":[{"username":"lius","other":"刘胜"}]}  http://localhost:8081/dal/shard2/rwsplit_GetNoId3.txt?name=lius&METHOD=PUT 【 返回】{"twitter:user_2":0,"twitter:user_1":1}
  • 35.
    35 5 例8:交易表按七日翻滚  0,场景  交易表按照交易时间(日期)进行分片,不同日期的交易数据,记录在不同库不同表中。  每周一/二/三/…/日的交易数据,分别记录在日表trans_1/trans_2/…/trans_7中。  客户端根据需要提交SHARDING_KEY1和SHARDING_KEY2参数值。  5,配置(dal_sharding文件)  GET.trans7_GetNoId3 = select * from {SHARDING_TABLES} where id>0  GET.trans7_GetNoId3.js = function getTables() { var day = new Date().getDay(); return "testdb:trans_"+day; }  PUT.trans7_PutNoId3 = insert into {SHARDING_TABLES} (`id`, `dtime`, `content`) VALUES ('{id}', '{dtime}', '{content}‘)  PUT.trans7_PutNoId3.js = function getTables() { var day = new Date().getDay(); return "testdb:trans_"+day; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/trans7_GetNoId3.txt 【返回】 {"testdb:trans_4":[{"id":1,"content":"交易信息4- 1","dtime":"20130404000001"},{"id":2,"content":"交易信息4- 2","dtime":"20130404000002"}]}  http://localhost:8081/dal/shard2/trans7_PutNoId3.txt&id=99&dtime=20130606000099&content=Ne wTrans99
  • 36.
    36 5 测试:功能测试(黄进)  参考:20130618分片测试(黄进).xlsx  共23个测试用例  1 同库不同表查询所有字段值,有数据情况  2 同库不同表查询所有字段值,无数据情况  3 同表不同库查询所有字段值,有数据  4 同表不同库查询所有字段值,无数据  5 不传SHARDING_KEY1和SHARDING_KEY2参数值有数据  6 不传SHARDING_KEY1和SHARDING_KEY2参数值无数据  7/8/9 同库不同表只传SHARDING_KEY1参数(STRING)  10/11/12 同库不同表只传SHARDING_KEY1参数(int)  13 同表不同库只传SHARDING_KEY1参数(int)  14 传SHARDING_KEY1和SHARDING_KEY2参数(string)  15 传SHARDING_KEY1和SHARDING_KEY2参数(int)  16 多片普通sql  17 单列分片预编译单表查询(int)  18 单列分片预编译单表查询(str)  19 单列分片预编译多表查询  20 读写分离  21/22/23 日切表查询测试/日切表新增记录/日切表修改记录
  • 37.
    37 5 测试:性能自测(殷舒)  测试:引入脚本语言对性能的影响  环境:CPU:Intel-Xeon-1.86GHz *4,MEM:4G, 10.10.38.135  工具:Apache/ab  命令:ab -c 100 -n 100000 -k  主机:http://10.10.38.135:8082/  测试地址和结果: 【req/sec】 1. /dal/psqlid/testdb-db2/sql_select.txt 【1419】 2. /dal/psqlid/testdb-db2/psql_select.txt 【1589】 3. /dal/shard2/users_GetById16.txt?SHARDING_KEY1=1 【1388】 4. /dal/shard2/psql_GetById16.txt?SHARDING_KEY1=1 【1398 】  结论:引入脚本语言对性能影响不大  (1589-1419)/1589=10.7%,(1398-1388)/1398= 0.7%  ==》改用预编译方式,性能提高1~10%左右  (1589-1398)/1589=12.0%,(1419-1388)/1419= 2.2%  ==》引入脚本语言后,性能降低2~12%左右
  • 38.
    38 6 总结:需求满足度和示例  后端访问方式: 【基于JDBC的访问】  普通SQL语句【例1/2/3】  预编译SQL语句【例5/6】  数据分片策略:  按读写操作的类型:读写分离(不同表名|多写表) 【例7】  按分片数据库库名:相同库相同表,相同库不同表【例1/2/3/5/6】  按分片数据库表名:不同库相同表,不同库不同表【例1/2/3/5/6】  按单个分片字段的类型(整型,字符串,…) 【例5/6】  按单个分片字段值个数  没有分片字段值(多表全局查询) 【例1】  单个分片字段值(单表单条查询) 【例2,例5】  两个分片字段值(多表范围查询) 【例3,例6】  多个字段混合计算的结果进行分片【例4】  客户端:根据多个字段,计算产生单一分片关键字,转化为单字段分片。  服务端:根据分片关键字的值,确定访问的库和表。  新特性:优先查询【例9】  新特性:复杂分片策略(多行js脚本)
  • 39.
    40 6 小结:新需求-结果合并  局部有序,局部最优  无聚合字段  二路归并  LIMIT运算【多表分页】  有聚合字段  聚合规则处理【SUM,MAX,MIN,DISTINCT】  表达式求值  堆排序【合并排序】  LIMIT运算【多表分页】
  • 40.
    41 5+例9:普通SQL单列分片全局优先查询 0,场景  (同例1)区别在于,客户端提交SHARDING_FIRST参数值为true,按顺序依次查询各表;有 结果就返回。且返回的Map中最多只有一项。  5,配置(dal_sharding文件)  GET.users_GetNoId3= select username,other from {SHARDING_TABLES} where username like '{name}%'  GET.users_GetNoId3.js= function getTables() { return “testdb:user1;testdb:user2; twitter:user_1;twitter:user_2”; }  7,测试地址和返回值  http://localhost:8081/dal/shard2/users_GetNoId.txt?name=lius&SHARDING_FIRST=tr ue  【true时】{"twitter:user_1":[{"username":"lius_1","other":"刘胜_1"}]}  【false时】{"twitter:user_2":[],"twitter:user_1":[{"username":"lius_1","other":"刘胜 _1"}],"testdb:user2":[{"username":"lius2","other":"刘胜 "}],"testdb:user1":[{"username":"lius","other":"刘胜"}]}
  • 41.
    42 5+例10-0:多表分页查询的参数 新增通信接口的参数 字段说明描述 PAGESIZE 分页大小大于0的整数 PAGENUM 分页页码大于0的整数 SHARDING_C 分页计算 OOKIES 中间结果 每次查询将各分片结果集大小响应给请求方。当以相 同条件查询不同分页时,可将上次的 sharding_cookies值提交服务端,提高服务端效率。  配置SQL语句的参数 字段说明针对数据库例 SHARDING_TAB LES 分片表的表名(可以含 Schema信息) 根据JS返回值自动填充,格 式:db1:s.tbl11,s.tbl12 LIMIT_OFFSET 起始位:偏移值或行序号DB2、Oracle、MySql >=0 LIMIT_OFFSET2 终止位:偏移值或行序号DB2、Oracle <5 LIMIT_ROWS 记录数: MySql =5
  • 42.
    43 5+例10-0:多表分页查询的示例(殷舒) 分片1分片2 分片3 PAGESIZE=3 第一页第二页第三页第四页
  • 43.
    44 5+例10-0:多表分页查询的测试(刘胜) 测试环境:  共4个分表,各分表记录条数分别为4,4,2,2(范围0..11/共12条)  共4个分页,每页大小3。  测试结果:  http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=1  第1页:{"testdb:user1":[{"id":1},{"id":2},{"id":3}]}  第2页:{"testdb:user2":[{"id":101},{"id":102}],"testdb:user1":[{"id":4}]}  第3页:{"twitter:user_1":[{"id":1}],"testdb:user2":[{"id":103},{"id":104}]}  第4页:{"twitter:user_2":[{"id":2},{"id":4}],"twitter:user_1":[{"id":3}]} 表范围/条第1页第2页第3页第4页 全局行序号0..11/12 0..3/3 3..5/3 6..8/3 9..11/3 testdb:user1 0..3/4 0..2/3 3..3/1 testdb:user2 4..7/4 0..1/2 2..3/2 twitter:user_1 8..9/2 0..0/1 1..1/1 twitter:user_2 10..11/2 0..1/2 各分页总记录数共3条共3条共3条共3条
  • 44.
    45 5+例10-1:多表分页(MySql) 0,场景  按多表的总记录数,计算各分表的分页参数;分页和排序字段一致,不做全局排序。  3,确定uri对应的Dalet类,修改dal_rest配置  /dal/shard3/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding4Page  4,确定Dalet4ShardingPaged类对应的配置,修改beans2配置  com.bs3.app.dal.engine.Dalet4Sharding4Page = com/umpay/v3/dal/dal_sharding.properties  5,定义sqlid对应的sql和js语句,修改dal_sharding配置  GET.MySqlPage = select id from {SHARDING_TABLES} where 1=1 limit {LIMIT_OFFSET},{LIMIT_ROWS}  GET.MySqlPage.js = function getTables() { return "testdb:user1;testdb:user2;twitter:user_1;twitter:user_2"; }  GET.MySqlPage.count = select COUNT(*) from {SHARDING_TABLES} where 1=1  7,使用HTTP来测试  http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=1  第1页:{"testdb:user1":[{"id":1},{"id":2},{"id":3}]}  http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=2  第2页:{"testdb:user2":[{"id":101},{"id":102}],"testdb:user1":[{"id":4}]}  http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=3  第3页:{"twitter:user_1":[{"id":1}],"testdb:user2":[{"id":103},{"id":104}]}  http://localhost:8081/dal/shard3/MySqlPage.txt?PAGESIZE=3&PAGENUM=4&SHARDING_COOKIE S=4_4_2_2 【注:缓存每个分片记录数】  第4页:{"twitter:user_2":[{"id":2},{"id":4}],"twitter:user_1":[{"id":3}]}
  • 45.
    46 5+例10-2:多表分页(MySql多预编译) 0,场景  按多表的总记录数,计算各分表的分页参数;分页和排序字段一致,不做全局排序。  3,确定uri对应的Dalet类,修改dal_rest配置  /dal/shard3/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding4Page  4,确定Dalet4ShardingPaged类对应的配置,修改beans2配置  com.bs3.app.dal.engine.Dalet4Sharding4Page = com/umpay/v3/dal/dal_sharding.properties  5,定义sqlid对应的sql和js语句,修改dal_sharding配置  GET.psql_MySqlPage = select id from {SHARDING_TABLES} where 1=1 limit {LIMIT_OFFSET},{LIMIT_ROWS}  GET.psql_MySqlPage.js = function getTables() { return "testdb:user1;testdb:user2;twitter:user_1;twitter:user_2"; }  GET.psql_MySqlPage.count = select count(*) from {SHARDING_TABLES} where 1=1  7,使用HTTP来测试  http://localhost:8081/dal/shard3/psql_MySqlPage.txt?PAGESIZE=5&PAGENUM=1  {"testdb:user2":[{"id":101}],"testdb:user1":[{"id":1},{"id":2},{"id":3},{"id":4}]}  http://localhost:8081/dal/shard3/psql_MySqlPage.txt?PAGESIZE=5&PAGENUM=2  {"twitter:user_2":[],"twitter:user_1":[{"id":1},{"id":3}],"testdb:user2":[{"id":102},{"id":103},{"id":104}]}  http://localhost:8081/dal/shard3/psql_MySqlPage.txt?PAGESIZE=5&PAGENUM=3&SHARDING_CO OKIES=4_4_2_2  {"twitter:user_2":[{"id":2},{"id":4}]}
  • 46.
    47 5+例10-3:多表分页(DB2) 0,场景  按多个表的总记录数,计算每个分表的分页参数;  分页和排序字段一致,无需重新做全局排序。  3,确定uri对应的Dalet类,修改dal_rest配置  /dal/shard3/{sqlid} = com.bs3.app.dal.engine.Dalet4Sharding4Page  4,确定Dalet4ShardingPaged类对应的配置,修改beans2配置  com.bs3.app.dal.engine.Dalet4Sharding4Page = com/umpay/v3/dal/dal_sharding.properties  5,定义sqlid对应的sql和js语句,修改dal_sharding配置  GET.Db2Page = SELECT * FROM (Select id,rownumber() over(ORDER BY id ASC) AS rn from {SHARDING_TABLES}) AS a WHERE a.rn >{LIMIT_OFFSET} AND a.rn<={LIMIT_OFFSET2}  GET.Db2Page.js = function getTables() { return "testdb:user1;testdb:user2;twitter:user_1;twitter:user_2"; }  GET.Db2Page.count = select COUNT(*) from {SHARDING_TABLES}  7,使用HTTP来测试  http://localhost:8081/dal/shard3/Db2Page.txt?PAGESIZE=3&PAGENUM=1  http://localhost:8081/dal/shard3/Db2Page.txt?PAGESIZE=3&PAGENUM=2  http://localhost:8081/dal/shard3/Db2Page.txt?PAGESIZE=3&PAGENUM=3
  • 47.
    48 6+小节:新需求(20131016) 需求1:结果集合并【不做排序】  需求2:动态SQL语句【不限于where】  方式1:通过缺省参数。【不能覆盖所有场景】  方式2:将where整体作为一个参数【部分支持】  特定模式下的格式约束——预编译/存储过程/分片模式  注:动态拼接sql,只能是普通SQL,不能支持预编译?  需求3:支持多表关联查询(隐含join操作)  场景1:只一个表是分片的。【完全支持】  场景2:多个表都是分片的,但都在同一个库【修改支持】  要求:分片字段相同,分片规则一致,相同库中保存。  select * from s.table11,s.table12 where …  select * from s.table11 AS T1,s.table12 AS T2 where T1.id=T2.id AND …  场景3:多个表都是分片的,且不在同一个库【不支持】
  • 48.
    51 9 总结:相关培训  20081113大型分布式系统架构技术简介(刘胜)中级.pdf  20081211大型软件系统开发综述(刘胜)中级.pdf /.mm/.png  20090219网络信息安全和认证技术(刘胜)中级.pdf  20090625服务器框架的概念和实践(刘胜)20121204.ppt  20090625分布式认证和授权技术(刘胜)初级.ppt  20090914系统架构师2009大会归来(刘胜)暨头脑风暴.ppt  20091224数据访问层DAL概念和实践1(刘胜).ppt  20100326数据访问层DAL概念和实践2(刘胜).ppt  20100902数据访问层DAL架构和实践3(刘胜).ppt  20101217软件技术大会和JavaOne大会归来(刘胜).ppt  20110223高可用可扩展的负载均衡层(卢翔,刘胜).ppt  20110801应用监控系统架构实践(刘胜).ppt  20120613数据访问层DAL架构和实践4(刘胜).pptx  20121012联动优势技术大会(刘胜)三年技术规划v1.3.4.pptx  20121013联动技术框架能力发布-BS3&DAL(殷舒).pptx  20121218联动优势技术平台介绍(for漫道科技)v5.pptx  20130326号码集合的存储和访问方法兼谈专利申请(刘胜).pptx  20130521联动优势企业架构介绍(刘胜).pptx  20130325理解REST设计模式和反模式(刘胜)草案.pptx
  • 49.
    52 Q9& 结A语:Q&A  现场问题——?  1,?  2,?  3,?  4,?  5,?  思考题——  1,?  2,?  3,?
  • 50.
    53 附:JPA事务类型与EntityManager 表11-2 事务类型与EntityManager 运行环境J2EE/EJB容器J2EE/Web容器J2SE 应用托管的 EntityManager  对比 RESOURCE_LOC AL或JTA RESOURCE_LOC AL或JTA RESOURCE_LOC AL 容器托管的 EntityManager JTA 不支持不支持 环境/容器/托管RESOURCE_LOCAL JTA J2EE/EJB/应用OK OK J2EE/EJB/容器OK OK J2EE/WEB/应用OK OK J2EE/WEB/容器OK 不支持 J2SE OK 不支持 集中式事务OK OK 分布式事务不支持支持
  • 51.
    54 附:解析中间层技术架构(Java) 1 2 3 4 5  模块组成  网络:JDBC协议解析(MySQL)  解析:{SqlParser | Cobar}  路由:基于{表名+字段值| SQLID}  执行:{SQL | PSQL | CALL}  结果集合并& 排序  访问方式  方案1:SQL@JDBC  使用MySql的JDBC驱动,使用ORM方式不变  方案2:SQLID@JDBC  使用自定义JDBC驱动,使用ORM方式不变  方案3:SQLID@{HTTP|CM20}  使用自定义通信协议,限制前端接口。
  • 52.
    55 附:使用DAL方式 使用DAL方式  方案1:直接使用BS3中API接口。  方案2:自定义封装CM20协议访问。  方案3:自定义封装HTTP协议访问。  方案4:封装伪JDBC驱动,访问固定sqlid地址  客户端:已有应用系统不用改动。  层次多:Args -> SQL -> URI -> 解析SQL -> 解析表和字段  陷阱多:特殊SQL、大数据集、多表JOIN、多表分片、
  • 53.
    56 6+小节:新需求(20140106) 需求1:同一库两表关联,关联字段不相同。  场景:表order和trans关联,都分片,且分片字段不同。  Order表按orderdate分片  Trans表按intime分片  解决:使用字段别名【需要验证】  需求2:基于{表名+字段值} 分片  场景:封装伪JDBC驱动统一访问DAL服务。  需求:统一URI,根据sql解析结果映射得到分片规则  需求3:跨库的两表关联查询  场景:交易表关联退费交易表,退费数据跨年。  方案:部分数据冗余,单个库创建13个月表。【只读库】
  • 54.
    57 爬虫技术 需求:  (赵晓庆)给出手机号码,从互联网上把个人基本信息补全,比如身份证号 码、家庭住址、教育程度。能不能通过技术手段拿到微博、人人网、校友录 等的用户资料不,还有婚庆网站、电商网站。  (李亚光)跟几次张总交流过程中,张都提到了抓取外部数据的事情,你看 这个事有啥快速出成果的技术方案没?或者给指出个技术方向。现在要有这 么一个框架,java最好能够通过指定url分层获取页面数据,并将数据静态 化成文件。  方案:  获取页面信息——爬虫  根据语言:java,python,Go  根据页面:静态HTML,动态JS脚本  信息类型:非敏感信息,敏感信息  收集方式:匿名主动获取(爬虫),商户合作获取(购买),用户主动提交。  挖掘页面信息——半格式化文本挖掘。  半格式化的,文本挖掘。因为输入不规范,所以也没有什么稳定可靠的算法,需 要根据输入随时调整。

Editor's Notes

  • #2 【修改历史】 Version 5.0.20130606 初期版本。 Version 5.1.20130608 +写库读操作修改测试验证。+脚本语言选择。*设计原则。 Version 5.2.20130619 +性能测试,+用例和测试 Version 5.2.20130620 +其它新需求,+功能测试(黄进),*性能自试(殷舒),*设计方案,+实现方案(比较),+发布地址 Version 5.3.20130621 *其它新需求(TODO) Version 5.3.20130624 *相关约定(+SHARDING_FIRST), Version 5.3.20130626 +例9单列分片普通SQL全局优先查询,+实现:新旧读写分离方式对比,*背景问题,*背景需求, Version 5.3.20130626 *需求满足度(改为:需求和示例),个别细节修改(分片类型,新旧方式的对比) Version 5.4.20130626 评审。隐藏部分内容(高可用数据存储架构,DAL技术选择,DAL服务平台,DAL地位,现状多表分离和多表查) Version 5.5.20130628 发布。*新需求(陈瑛琦)支持DB2导出的归档数据文件;*相关约定:分隔符逗号改分号?! Version 5.5.20130709 +新需求-结果合并 Version 5.6.20130802 +例10-x:多表分页 Version 5.6.20130814 *其他:框架拆分 Version 5.7.20131016 +例10-0:多表分页查询的参数 Version 5.7.20131016 +小节:新需求(TODO-1016) Version 5.8.20140312 +数据分片,DAL实现,已有DAL方案 -------- 分享:http://www.slideshare.net/nikeliu/20130626dal51-h 标题:联动优势数据访问层DAL架构和实践之五:访问分片数据 Architecture and Practice for DAL (5) Data Sharding Architecture and Practice for Data Access Layer (5) Data Sharding 联动优势数据访问层DAL架构和实践之五:分片数据分片 说明: How to implement a dalet to access sharding databases. 按照许超前的说法(见http://www.slideshare.net/xcq/ss-3629618),其实现的DAL与memcache比较,其性能差异主要在协议解析和查询分析上。 和已有DAL软件(如许超前DAL手机之家、陈思儒Amoeba/贺贤懋Cobar等)不一样,在前端访问方式的选择上,抛弃JDBC方式,而是为同一个dalet数据服务,同时提供自定义TCP长连接和HTTP长连接两种接口。 因而通过抛弃JDBC可以获得多方面的好处—— 1)可减少S端协议解析和查询分析的开销; 2)也简化C端编程。 3)后端存储就不再限于RDB了,而可以是任意NOSQL、文件、缓存、甚至是Tuxedo等在线服务。 4)可以实现无状态了,更容易横向扩展。 5)从接口上就可消除join等关键字的误用,避免引起服务端负担过重。 --------
  • #3 代码质量@刘胜20130426.mm 确定“优秀代码评选标准”时的一些总结。思维导图格式。主要是大原则,没写具体实例。可以一起讨论交流一下。 20130326号码集合的存储和访问方法兼谈专利申请(刘胜).pptx 黑名单存储,以及专利知识。之前这边和金泽分别讲过一次。如还有人有兴趣,可以再讲一次。 20130521联动优势企业架构介绍(刘胜).pptx 按部门培训计划来的命题作文,原计划是5.21在金泽讲的,后因搬家改期。如有兴趣可先讲。主要是综合了服务器框架、技术规划等相关内容。 20130325理解REST设计模式和反模式(刘胜)草案.pptx 初稿,总结了三个REST模式和反模式,附录还有更多的反模式。
  • #5 问题:分区和主从的区别?
  • #6 开放系统的存储: 》内置存储 》外挂存储【按连接的方式分】 》》直连式存储(Direct-Attached Storage,简称DAS) 》》网络化存储(Fabric-Attached Storage,简称FAS)按传输协议分 》》》网络接入存储(Network-Attached Storage,简称NAS) 》》》存储区域网络(Storage Area Network,简称SAN) 分片模式: 1.(垂直切分)按功能划分 2.(水平切分)按某字段值的范围划分 3.基于HASH的切分 4.基于路由表的切分
  • #8 http://www.pgpool.net/mediawiki/index.php/Main_Page
  • #9 不同厂商的数据库 各种NOSQL数据存储 在线服务 概念:数据访问层DAL 【Layer=Services】 是一系列服务的集合,是DAO的自然延伸,是SOA的具体实现。 好处: 简化客户端接口,规范数据操作 【=>RESTful DB】 保持客户端接口,动态升级服务端实现 【SOA】 支持对巨量数据的访问 【缓存;读写分离;分片(分表/分库)】 服务的治理 【认证/管理/权限/监控/分析/优化】 功能需求: 访问代理 【Proxy】 读写分离 【RAIDb-1镜像,每个库都是全库】 数据分片 【RAIDb-0分区,每个库都是子库】 高可用性 【支持三备份和NWR模型】 特性需求: 厂商中立 【1)多种数据库类型;2)多个数据库版本?】 语言中立 【支持Java、C/C++、PHP、Flex等多种客户端接口】 资源共享 【共享DataSource连接池,共享Cache】 访问日志 【提供SqlLog和BackLog,可用于性能瓶颈分析和数据恢复】 访问控制 【用户认证/连接管理/权限控制】 读写分离 【FullDB】 数据分片 【分表,分库】 访问集群 【HA-Proxy、JGroup】
  • #10 方案1:Mysql-Proxy@MySQL官方 特点:官方提供,可实现负载平衡/读写分离/failover等,但不支持大数据量的分库分表且性能较差。 参考:http://dev.mysql.com/downloads/mysql-proxy/ 参考:https://github.com/drmingdrmer/mysql-proxy-xp/blob/master/lib/rw-splitting.lua 方案2:Atlas@Qihoo360/王超 特点:在mysql-proxy-0.8.2版基础上优化,增加若干新特性。 源码:https://github.com/Qihoo360/Atlas 方案3:Amoeba@/陈思儒 版本:Amoeba for MySQL/Aladdin/MongoDB 特点:不支持事务/存储过程/分库分表/输出大结果集。 缺点:不支持事务/存储过程/输出大数据量结果集/分库分表。目前只做到分DB实例,每个节点需要保持库表结构一致。 源码:http://sourceforge.net/projects/amoeba/ 文档:http://docs.hexnova.com/amoeba/ 方案4:Cobar@AliBaBa/贺贤懋 缺点:基于amoeba 0.34,开源版只支持mysql,并不支持读写分离。 源码:http://code.alibabatech.com/wiki/display/cobar/Home 方案5:TDDL@Taobao 特点:基于集中式配置的Jdbc数据源实现,具有主备/读写分离/动态数据库配置等功能。 缺点:复杂度相对较高,文档较少,只开源动态数据源,分表分库部分还未开源,还需要依赖diamond,不推荐使用。 源码:https://github.com/alibaba/tb_tddl 方案6:S3@Amazon(Simple Storage Service) 特点:基于标准REST和SOAP接口的超级SAN存储 -------- 参考:http://bbs.chinaunix.net/thread-1341765-1-1.html 再提mysql-proxy读写分离脚本(rw-splitting.lua)BUG问题 参考:http://www.open-open.com/doc/view/f8d0b2d9898d4c898fe53bcdaddca2a3 Cobar -- 分布式数据库Proxy方案.pptx Cobar 前身之前版本为amoeba 0.34已经在生产环境中投入使用,目前开发的新版本更名为cobar 。Cobar 应用领域在分布式数据库领域中致力于解决数据切分、数据分发、数据合并、数据返回等功能。为前端应用提供一个透明的、单一的数据访问服务。屏蔽后端分布式数据库的复杂逻辑。
  • #11 数据量大 【陈瑛绮】 联动用户表:老表userinf有1.1亿,mobileBankInf有4000万。 【新表tuser+tuserBank】 联动交易表:老表transAll有1.2亿, 【从2010.10.1-2011.6,日均50万】 数据冗余: 【陈瑛绮】 数据表多个版本混存,每个用户记录两次,每条交易记录两次。 【双写,效率低,不一致】 个别应用过度依赖于TransAll表,未做日表翻滚。 【小额年底改造/空充无人维护/个别电渠】 读写不均 淘宝读写比测算:10:1(来自iDataForum) 联动读写比估算:用户表(20:1),交易表(2:1) 连接池问题:应用太多太分散,导致数据库连接数超限。 【未有效共享连接池】
  • #16 BSL平台(=LBL+DAL+IoC)
  • #19 GET.user_cross = select other from user1, user2; GET.user_union = select other from user1 union all select other from user2; # GET http://localhost:8081/dal/psqlid2/testdb/user_cross.txt [{"other":"刘胜"},{"other":"吴锋海"},{"other":"彭飞"},{"other":"赵军"},{"other":"刘胜"},{"other":"吴锋海"},{"other":"彭 飞"},{"other":"赵军"},{"other":"刘胜"},{"other":"吴锋海"},{"other":"彭飞"},{"other":"赵军"},{"other":"刘胜"},{"other":"吴锋海"},{"other":"彭飞"},{"other":"赵军"}] # GET http://localhost:8081/dal/psqlid2/testdb/user_union.txt [{"other":"刘胜"},{"other":"吴锋海"},{"other":"彭飞"},{"other":"赵军"},{"other":"刘胜"},{"other":"吴锋海"},{"other":"彭飞"},{"other":"赵军"}]
  • #21 Groovy、BeanShell,Jython,Jruby,Rhino,PHP,Jacl,JudoScript,OGNL,…
  • #24 JSR-223: Scripting for the Java Platform JSR-241: Groovy – A New Standard Programming Language for the Java Platform JSR-274: Standardizing BeanShell JSR 292: Supporting Dynamically Typed Languages on the Java Platform JSR-331: Java Constraint Programming API https://code.google.com/p/red5/source/browse/java/scripting/branches/paulg_0.6/doc/engines.txt?spec=svn1276&r=1276 This is JSR-223 script engine for the Groovy language. Groovy is available for download at http://groovy.codehaus.org/. We have built and tested Groovy version 1.0 JSR-06. This is JSR-223 script engine for JRuby - Java implementation of Ruby language. JRuby is available for download at http://jruby.sourceforge.net/. We have built and tested with JRuby version 0.9.0. This is JSR-223 script engine for the Jacl language. Jacl is Java implementation of Tcl (Tool Command Language). This is available for download at http://tcljava.sourceforge.net/. We have built and tested with Jacl version 1.3.3. This is JSR-223 script engine for the JudoScript language. JudoScript is available for download at http://www.judoscript.com/. We have built and tested with Judo version 0.9. This is JSR-223 script engine for Jython - Java implementation of Python. Jython is available for download at http://www.jython.org/. We have built and tested with Jython version 2.1. This is JSR-223 script engine for OGNL - Object Graph Navigation Language. OGNL is available for download at http://www.ognl.org/. We have built and tested with OGNL 2.6.9. This is JSR-223 script engine for the Rhino / Javascript / ECMA language. Rhino is available for download at http://rhino.mozilla.org/. We have built and tested Groovy version 1.6 R2.
  • #25 读写分离 问题:不支持存储过程:1)不跨库;2)不支持不同表名的处理。
  • #26 5+例10-0:多表
  • #28 http://localhost:8081/dal/sharding/users_GetNoId.xml?fields=*&name=lius http://localhost:8081/dal/sharding/users_GetById.xml?fields=*&SHARDING_KEY1=1 http://localhost:8081/dal/sharding/users_GetById.xml?fields=*&SHARDING_KEY1=102 http://localhost:8081/dal/sharding/users_GetById.xml?fields=*&SHARDING_KEY1=2& SHARDING_KEY1=101
  • #30 GET.users_GetById3= select username,other from {SHARDING_TABLES} where id='{SHARDING_KEY1}' GET.users_GetById3.js= function getTables(id) { if (id<100) return "testdb:user"; else return "testdb:user2"; }
  • #32 GET.user_GetById3x= select username,create_at from {SHARDING_TABLES} where username like '%{name}%' GET.user_GetById3x.js= function getTables(dt6) { if (dt6>"201300") return "testdb:user2"; else return "testdb:user1"; } ####################################################### http://localhost:8081/dal/shard2/user_GetById3x.txt?SHARDING_KEY1=201001&name=liu http://localhost:8081/dal/shard2/user_GetById3x.txt?SHARDING_KEY1=201301&name=liu
  • #33 # http://localhost:8081/dal/shard2/psql_GetById3int.txt?SHARDING_KEY1=2&name.string=lius # http://localhost:8081/dal/shard2/psql_GetById3int.txt?SHARDING_KEY1=102&name.string=lius2
  • #34 SHARDING_TABLES
  • #36 PUT.trans7_PutNoId3 = insert into {SHARDING_TABLES} (`id`, `dtime`, `content`) VALUES ('{id}', '{dtime}', '{content}') PUT.trans7_PutNoId3.js= function getTables() { var day = new Date().getDay(); return "testdb:trans_"+day; } ####################################################### # http://localhost:8081/dal/shard2/trans7_GetNoId3.txt # http://localhost:8081/dal/shard2/trans7_PutNoId3.txt&id=99&dtime=20130606000099&content=NewTrans99
  • #38 CPU:Intel(R) Xeon(R) 1.86GHz *4 MEM:4G ############################################################################## Dalet4Sharding Server Software: Server Hostname: 10.10.38.135 Server Port: 8082 Document Path: /dal/shard2/users_GetById16.txt?SHARDING_KEY1=1 Document Length: 132 bytes Concurrency Level: 100 Time taken for tests: 9.997571 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Keep-Alive requests: 10000 Total transferred: 2740548 bytes HTML transferred: 1320264 bytes Requests per second: 1000.24 [#/sec] (mean) Time per request: 99.976 [ms] (mean) Time per request: 1.000 [ms] (mean, across all concurrent requests) Transfer rate: 267.67 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 3 Processing: 2 98 65.5 82 723 Waiting: 2 98 65.5 82 723 Total: 2 98 65.6 82 723 Percentage of the requests served within a certain time (ms) 50% 82 66% 97 75% 112 80% 124 90% 161 95% 203 98% 299 99% 398 100% 723 (longest request) ############################################################################## ############################################################################## Dalet4PSqlid Server Software: Server Hostname: 10.10.38.135 Server Port: 8082 Document Path: /dal/psqlid/testdb-db2/sql_select.txt?tables=sharding.t_test Document Length: 101 bytes Concurrency Level: 100 Time taken for tests: 10.467854 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Keep-Alive requests: 10000 Total transferred: 2430729 bytes HTML transferred: 1010303 bytes Requests per second: 955.31 [#/sec] (mean) Time per request: 104.679 [ms] (mean) Time per request: 1.047 [ms] (mean, across all concurrent requests) Transfer rate: 226.69 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.2 0 3 Processing: 2 103 60.5 87 593 Waiting: 2 103 60.5 87 593 Total: 2 103 60.6 87 596 Percentage of the requests served within a certain time (ms) 50% 87 66% 105 75% 119 80% 131 90% 171 95% 213 98% 296 99% 359 100% 596 (longest request) ##############################################################################
  • #40 1. TODO.DAL (刘畅) 先后查询,如果查询有结果,不对后续库做操作。 2. TODO.DAL (刘畅) rest、sqlid配置动态加载。 3. TODO.DAL (张文凌)js配置支持多行,单独引用.js文件?。 4. TODO.DAL (李升) 支持多表分页查询处理。(本消息通过Mac版飞信发送,点击即可体验)
  • #41 参考:http://www.slideshare.net/aleafs/myfox-on-nodejs Nodefox: 一个数据处理中间件,负责从一个MySQL集群中提取数据,计算,并输出统计结果。
  • #43 select id from {SHARDING_TABLES} where 1=1 limit {LIMIT_OFFSET},{LIMIT_ROWS} SELECT * FROM (Select id,rownumber() over(ORDER BY id ASC) AS rn from {SHARDING_TABLES}) AS a WHERE a.rn >{LIMIT_OFFSET} AND a.rn<={LIMIT_OFFSET2} JS函数返回格式 db1:s.tbl11,s.tbl12;db2:s2.tbl21,s2.tbl22;… db1:s.tbl11,s.tbl12;db2:s2.tbl21,s2.tbl22;…
  • #49 缺点:在预编译/存储过程/分片模式下,对sql有特定的格式约束。 建议彻底杜绝跨库的Join操作。(优化join的方法就是不要join ) 只要是多表关联查询本质上都是join操作。(可以支持,假设分片字段一致,分片规则一致。) SELECT * FROM A,B WHERE A.ID = B.ID; SELECT * FROM A JOIN B ON A.ID = B.ID; SELECT * FROM A JOIN B USING(ID); 查询两表数据,这三条sql有什么不同——用MySQL的EXPLAIN 检测发现性能没有区别,只是写法不同
  • #51  umpay_bs3_v4.1381.120301.jar
  • #54 http://www.360doc.com/content/10/0413/15/19525_22861156.shtml @TransactionAttribute(TransactionAttributeType.REQUIRED)
  • #56 丁喆 13:44:05 我们不修改ibatis,只是做了一个dal的驱动,封装成jdbc方式 丁喆 13:44:18 具体情况正在验证 丁喆 13:44:24 还没弄完 【虎.无名】 13:45:05 但是,jdbc是有连接状态的,而dal是无状态的。尤其对于大数据集的场景,用dal并不适合。 丁喆 13:47:56 jdbc是有连接状态的,我们可以不保存链接的方式 丁喆 13:48:39 每次都是获取新的链接,或者连接池中只保存connection对象 丁喆 13:49:24 你这里说的大数据集是否有界定?多少算大? 丁喆 13:52:34 这个倒是可以在客户端加以限制 【虎.无名】 13:52:50 在线操作,是不需要大数据集的。离线操作才需要。 丁喆 13:53:30 目前我们对于这种大数据集都是在应用层面做了限制 【虎.无名】 13:53:59 3)分页。2)自定义dalet在服务端生产所需文件,然后通过web获取。1)直接访问DB。
  • #57 20140106.2294 (丁喆)DAL新需求:1)同一库两表关联,关联字段不相同。2)统一访问uri,根据sql解析结果映射得到分片规则(即:根据表名确定分片)。3)退费数据跨年(13月)。 20140106.TODO(丁喆)TODO 同一库两表关联,关联字段不相同。例如:order表和trans表关联,order.orderdate<-->trans.intime。。。【使用字段别名,需要验证。】 20140106.TODO(丁喆)sql解析模式,统一访问uri,根据sql解析结果映射得到分片规则js。(所有实体表需要管理虚拟表名对应,谁来维护?)——【根据表名确定分片】 20140106.TODO(丁喆)跨库的两表关联查询,交易表关联退费交易表,退费数据跨年。(部分数据冗余,单个库创建13个月表)——【只读不写,无事务】
  • #58 技术总是在短期内被高估,但在长期内被低估 ----日期:2014-01-07   赵晓庆 17:29:22 研究过爬虫么 【虎.无名】 17:31:35 以前练习python时写过很小的分析失效链接。。。得看你做什么。 【虎.无名】 17:32:09 不同的用途,爬虫不一样,基本上,都是获取网页内容,然后分析,复杂一点的,就有登录操作什么 赵晓庆 17:32:46 我的需求是,给出手机号码,从互联网上把个人基本信息补全,比如身份证号码、家庭住址、教育程度 【虎.无名】 17:33:42 互联网那么多内容,你从哪获取? 【虎.无名】 17:34:03 你得有明确的数据来源。。。 【虎.无名】 17:35:17 比如,招聘网站,比如学校通信录网站。。。这些来源,你得逐步积累,没有一个单一来源是全量数据的。 【虎.无名】 17:35:41 这些都是隐私,用户都不会主动公开的。 ----日期:2014-01-08 赵晓庆 14:02:44 你能帮我看看,能不能通过技术手段拿到微博、人人网、校友录等的用户资料不,还有婚庆网站、电商网站。 【虎.无名】 14:06:27 这就得看这些网站的安全防护措施如何了,只要浏览器能看到,那么就可编写爬虫批量获取。。。否则就得用一些黑客手段了,这些没擅长。 ----日期:2014-01-13 李亚光  10:33:40 刘导,有个事需要你帮我们规划规划,跟几次张总交流过程中,张都提到了抓取外部数据的事情,你看这个事有啥快速出成果的技术方案没?或者给指出个技术方向(本消息通过Mac版飞信发送,点击即可体验) 【虎.无名】  10:35:04 赵晓庆问过一次类似问题。 李亚光  10:35:47 其实这个是两个层面的问题一部分是技术问题,一部分是业务问题 李亚光  10:35:59 技术问题你给定个方案 李亚光  10:36:18 业务问题我再找人想想,琢磨个需求出来 【虎.无名】  10:36:46 赵的问题是:通过手机号查询,实现个人信息补全,然后打分评级。。。 【虎.无名】  10:39:53 我思考过,有一定想法,但是对你说的“快速出成果”,毫无任何帮助。 李亚光  10:40:43 先给个技术抓取的框架啥的 【虎.无名】  10:41:04 重点不在框架,也没什么框架。 【虎.无名】  10:42:10 是一种半格式化的,文本挖掘。因为输入不规范,所以也没有什么稳定可靠的算法,需要根据输入随时调整。 李亚光  10:50:30 现在第一步要实现能够从什么样的数据源拿到什么样的数据,这个需要一个框架支持 李亚光  10:50:59 具体要拿哪些数据,做什么样的分析是接下来的事情 李亚光  10:51:09 其实就是第一步先做数据采集 【虎.无名】  10:56:23 任何一个,支持http的代码都行,获取页面信息即可。 【虎.无名】  10:57:12 不管是java,python都是可以的。 【虎.无名】  10:57:43 只要浏览器能访问的,代码肯定也可以。 李亚光  10:59:31 恩,现在要有这么一个框架,java最好能够通过指定url分层获取页面数据,并将数据静态化成文件。