Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

1,662 views

Published on

标题:
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.
和已有DAL软件(如许超前DAL手机之家、陈思儒Amoeba/贺贤懋Cobar等)不一样,在前端访问方式的选择上,抛弃JDBC方式,而是为同一个dalet数据服务,同时提供自定义TCP长连接和HTTP长连接两种接口。
因而通过抛弃JDBC可以获得多方面的好处——
1)可减少S端协议解析和查询分析的开销;
2)也简化C端编程。
3)后端存储就不再限于RDB了,而可以是任意NOSQL、文件、缓存、甚至是Tuxedo等在线服务。
4)可以实现无状态了,更容易横向扩展。
5)从接口上就可消除join等关键字的误用,避免引起服务端负担过重。

Published in: Design
  • 楼主能否提供下载地址
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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

  1. 1. 数据访问层DAL架构和实践(5)数据分片 Version 5.8.20140312 刘胜liusheng@umpay.com 联动优势B办公区 北京市西城区安德路甲104号证通商务楼F6层(100120)
  2. 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. 3 0*内容大纲(耗时1H+)  1 概念  数据访问层DAL  数据库分区与分片  高可用数据存储架构  2 背景  问题,需求,典型场景  3 现状(DAL实现)  关键技术选择,演化路线,专利产品,服务平台,在系统中的地位  4 设计和实现  设计方案要点,实现方案权衡,脚本语言选择,相关约定  5 使用和测试  用例:环境准备和测试步骤,例1/2/3/…/8,例9  测试:功能测试,性能自测  6 总结  需求满足度,新需求,相关培训,发布地址,参考索引, Q&A
  4. 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. 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. 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. 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. 8 1*概念:数据访问层DAL/Proxy  数据访问层DAL的定义  Data Access Layer(Layer=Services)  DAL是一系列服务的集合,是DAO的自然延伸,是 SOA的具体实现。  数据访问层DAL的好处  简化客户端接口,规范数据访问操作  保持客户端接口,动态升级服务端实现  支持更多客户端:Java/C/PHP/…  共享数据库连接,减少总数据连接数  更细粒度访问控制  方便实施服务治理:权限/管理/监控/分析/优化/…  方便实施数据库的迁移/升级/重构/…  方便实施读写分离,支持高并发访问  方便实施数据分片,支持海量数据库  。。。
  9. 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. 10 2*背景:问题(简)  问题1:核心数据库不堪重负(201106)  数据量大  用户表:tuser + tuserBank  交易表:transAll  七天翻滚表  数据冗余 要么分区,要么分片。 分区成本高,依赖厂商技术。 分片阻力大,应用端改动多。  数据表多个版本混存,每个用户记录两次,每条交易记录两次。【双写】  个别应用过度依赖于TransAll表,未做日表翻滚。【分片】  系统迁移不彻底,有残留数据。(用户管理系统:望京丰台) 【多查】  读写不均【读写分离】  淘宝读写比测算:10:1(来自iDataForum)  联动读写比估算:用户表(20:1),交易表(2:1)  连接池问题: 【连接共享】  应用太多太分散,容易使数据库连接总数超限,导致DB中断  问题2:针对复杂的数据库部署,应用开发困难  同时连多个数据库 对于简单分片, 也能凑活实现。  依次查多个数据库表:丰台-望京;七天翻滚表;在线-离线库
  11. 11. 11 2*背景:需求  最终目的:替核心数据库减负,同时降低开发难度  数据冗余:根据读写操作,使用不同的库。【Full-DB】  横向拆分:根据应用拆分多个库。【ShardDB】  纵向拆分:根据数据拆分多个库和多个表。【ShardDB】  需求细分: 【 实际场景千变万化]  后端访问方式: 【暂不考虑NOSQL存储】  基于JDBC的访问【普通SQL/预编译SQL/存储过程】  数据分片策略:  按读写操作的类型【读写分离】  按分片数据库库名【相同库相同表,相同库不同表】  按分片数据库表名【不同库相同表,不同库不同表】 以不变应万变  按单个分片字段的类型【整数ID值,字符串DT时间】  按单个分片字段值个数  没有分片字段值(全局) 【全局查询】  单个分片字段值(单条) 【单表查询】  两个分片字段值(范围) 【多表查询】 如何  按多个分片字段值分片,通过计算产生单一分片关键字【如hash】
  12. 12. 12 2*背景:典型应用场景  场景1:客服系统/海量短信日志查询  说明:将短信日志归档到多个MySQL库的多张日表。  查询:按日期查单个表/按日期范围查多个表/按手机号询全部表  场景2:通信账户/海量支付交易日志查询  说明:将系统业务日志归档到多个MySQL库的多张日表。  查询: (同场景1)  场景3:用户管理/数据迁移后的冗余查询  说明:系统从望京迁移到丰台,但有部分遗留数据未作迁移。  查询:先查丰台数据库,如未找到,则再次查望京数据库。  场景4:用户管理/用户信息分表查询  说明:将用户表按手机号拆分到多个库的多张表中,并提供查询。  查询:按手机号查询单张表/按其他字段查询全部表。  场景5:交易表按7日翻滚  说明:根据交易日期所在的星期几,选择对应的表进行操作。  查询:按交易日期查单张表/按日期范围查多张表/按其他字段查全部表
  13. 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. 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. 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. 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. 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. 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. 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. 20 4 设计和实现(简)  设计方案的要点  设计思路和原则  设计方案  设计要点  实现方案的权衡  多种执行方式:SQL,预编译SQL,存储过程  多种分片策略:。。。  多种扩展方式:Java扩展,JS扩展  脚本语言的选择:  新旧方式的对比:  其他相关约定项:命名规范优于配置。
  21. 21. 21 4*实现:设计方案的要点(问)  设计思路和原则  约定优于配置。  诚实原则:展现真实,不粉饰太平。 LESS, BUT BETTER. LESS IS MORE. &  少即是多:用较少的类支持较多的分片策略(Do more with less)  二八原则:不过度设计。  做好20%最常用功能特性【有80%使用者将会用到】  覆盖80%大部分应用场景【剩下20%实现成本太高】  放弃部分功能特性【多线程并发/数据合并/二次排序/…】  设计方案  基于已有DAL架构平台,定义新的Dalet来访问分片数据。  实现方案  复用已有的Dalet4PSqlid类来扩展,尽量重用原有模式。  引入动态脚本语言来支持各种千变万化的分片策略。  问题  实现多线程并发查询?新建线程还是线程池?——优点?缺点?  实现跨库或跨表的查询结果合并,二次排序?——优点?缺点?
  22. 22. 22 4+实现:实现方案的权衡(问)  多种执行方式  支持: 普通SQL,预编译SQL  放弃存储过程【问:为什么放弃?】  多种分片策略  分片字段单列,多列【问:如何支持多列?】  字段的值无值,单值,多值【问:对应的场景?】  分片数据多库& 多表【问:需命名限定?】  多种扩展方式 Java接口扩展脚本语言扩展 定制方式基于接口实现基于脚本函数 定制内容实现一个Java文件,并配置配置一行函数定义的脚本语言 运行环境JAVA5+ JAVA6+ 部署方式字节码或类库更新配置文件 支持分片策略较少很多,基本覆盖所有场景 支持动态加载N/A N/A 混合多种分片多个类单个类:Dalet4Sharding
  23. 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. 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. 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. 26 5 使用和测试  环境准备和测试步骤  需求和示例(例1..例9)  功能测试(黄进)  性能自测(殷舒)
  27. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 39. 40 6 小结:新需求-结果合并  局部有序,局部最优  无聚合字段  二路归并  LIMIT运算【多表分页】  有聚合字段  聚合规则处理【SUM,MAX,MIN,DISTINCT】  表达式求值  堆排序【合并排序】  LIMIT运算【多表分页】
  40. 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. 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. 42. 43 5+例10-0:多表分页查询的示例(殷舒) 分片1 分片2 分片3 PAGESIZE=3 第一页第二页第三页第四页
  43. 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. 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. 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. 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. 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. 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. 49. 52 Q9& 结A语:Q&A  现场问题——?  1,?  2,?  3,?  4,?  5,?  思考题——  1,?  2,?  3,?
  50. 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. 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. 52. 55 附:使用DAL方式  使用DAL方式  方案1:直接使用BS3中API接口。  方案2:自定义封装CM20协议访问。  方案3:自定义封装HTTP协议访问。  方案4:封装伪JDBC驱动,访问固定sqlid地址  客户端:已有应用系统不用改动。  层次多:Args -> SQL -> URI -> 解析SQL -> 解析表和字段  陷阱多:特殊SQL、大数据集、多表JOIN、多表分片、
  53. 53. 56 6+小节:新需求(20140106)  需求1:同一库两表关联,关联字段不相同。  场景:表order和trans关联,都分片,且分片字段不同。  Order表按orderdate分片  Trans表按intime分片  解决:使用字段别名【需要验证】  需求2:基于{表名+字段值} 分片  场景:封装伪JDBC驱动统一访问DAL服务。  需求:统一URI,根据sql解析结果映射得到分片规则  需求3:跨库的两表关联查询  场景:交易表关联退费交易表,退费数据跨年。  方案:部分数据冗余,单个库创建13个月表。【只读库】
  54. 54. 57 爬虫技术  需求:  (赵晓庆)给出手机号码,从互联网上把个人基本信息补全,比如身份证号 码、家庭住址、教育程度。能不能通过技术手段拿到微博、人人网、校友录 等的用户资料不,还有婚庆网站、电商网站。  (李亚光)跟几次张总交流过程中,张都提到了抓取外部数据的事情,你看 这个事有啥快速出成果的技术方案没?或者给指出个技术方向。现在要有这 么一个框架,java最好能够通过指定url分层获取页面数据,并将数据静态 化成文件。  方案:  获取页面信息——爬虫  根据语言:java,python,Go  根据页面:静态HTML,动态JS脚本  信息类型:非敏感信息,敏感信息  收集方式:匿名主动获取(爬虫),商户合作获取(购买),用户主动提交。  挖掘页面信息——半格式化文本挖掘。  半格式化的,文本挖掘。因为输入不规范,所以也没有什么稳定可靠的算法,需 要根据输入随时调整。

×