Wednesday, March 30, 2011

(ZT)Page.ClientScript.RegisterStartupScript()

 

使用类型、键、脚本文本和指示是否添加脚本标记的布尔值向 Page 对象注册启动脚本。

参数

type

要注册的启动脚本的类型。

key

要注册的启动脚本的键。

script

要注册的启动脚本文本。

addScriptTags

指示是否添加脚本标记的布尔值.

备注:

启动脚本由它的键和类型唯一标识。具有相同的键和类型的脚本被视为重复脚本。只有使用给定的类型和键对的脚本才能使用该页面进行注册。试图注册一个已经注册的脚本不会创建重复的脚本。

调用 IsStartupScriptRegistered 方法以确定具有给定的键和类型对的启动脚本是否已经注册,从而避免不必要的添加脚本尝试。

RegisterStartupScript 方法的此重载中,使用 addScriptTags 参数可指示 script 参数中提供的脚本是否包装在 <script> 元素块中。将 addScriptTags 设置为 true 指示脚本标记将自动添加。

RegisterStartupScript 方法添加的脚本块在页面加载完成但页面的 OnLoad 事件引发之前执行。

示例

<%@ Page Language="C#"%>
<script runat="server">
public void Page_Load(Object sender, EventArgs e)
{
// Define the name and type of the client scripts on the page.
String csname1 = "PopupScript";
String csname2 = "ButtonClickScript";
Type cstype = this.GetType();
// Get a ClientScriptManager reference from the Page class.
ClientScriptManager cs = Page.ClientScript;
// Check to see if the startup script is already registered.
if (!cs.IsStartupScriptRegistered(cstype, csname1))
{
String cstext1 = "alert('Hello World');";
cs.RegisterStartupScript(cstype, csname1, cstext1, true);
}
// Check to see if the client script is already registered.
if (!cs.IsClientScriptBlockRegistered(cstype, csname2))
{
StringBuilder cstext2 = new StringBuilder();
cstext2.Append("<script type=text/javascript> function DoClick() {");
cstext2.Append("Form1.Message.value='Text from client script.'} </");
cstext2.Append("script>");
cs.RegisterClientScriptBlock(cstype, csname2, cstext2.ToString(), false);
}
}
</script>
<html>
<head>
<title>ClientScriptManager Example</title>
</head>
<body>
<form id="Form1"
runat="server">
<input type="text" id="Message"> <input type="button" value="ClickMe" onclick="DoClick()">
</form>
</body>
</html>

Monday, March 28, 2011

Gridview Paging Query With Row_Number()

 

eg.

 

get records between 201 and 300

select * from (

SELECT row_number() over (order by column1, column2 as rowindex) as rowindex , * from BOM_Lists_Transfer_Test )as tablewithrowindex where  (rowindex between 201 and 300 ) and …..

SP 2010 New Feature

  • Language Integrated Query (LINQ)
  • List Enhancements
  • Business Connectivity Services(Former Business Data Catalog)
  • Silverlight Integration
  • Client OM
  • Some are considered “ Web 2.0 ” protocols, such as Representational State Transfer (REST), Asynchronous JavaScript + XML (AJAX), JavaScript Object Notation (JSON) and
    ATOMSub/Pub
  • Sandbox Solutions and Resource Governors
  • Searching Ability Enhancement
  • ECM Enhancement
    • Document
    • Workflow
    • Records

Wednesday, March 23, 2011

软件架构师(ZT)

开会谈到个人职业规划与发展问题,大部分人都想从技术到管理华丽转身,可又不知道如何做管理。其实一个最好的过度角色便是架构师。请看我下面的总结与建议:

软件架构师工作职责是在一个软件项目开发过程中,将客户的需求转换为规范的开发计划及文本,并制定这个项目的总体架构,指导整个开发团队完成这个计划。

一般来讲,软件架构师主要分成:

1:前端架构师: 前端架构师主要负责软件产品的前端呈现,必须具有以下能力:       

  • 可用性设计
  • 呈现层设计
  • 需求分析能力
  • 沟通能力
  • 执行能力
  • 推动能力

2:平台架构师:平台架构师主要参与产品的长期规划及其推进,因此其需具有一下能力:

  • 技术能力(技术比较广泛而且也比较熟练)
  • 长期规划能力及推进力
  • 需求分析能力
  • 沟通能力
  • 执行能力
  • 推动能力

3:解决方案架构师:解决方案架构师主要为特定的解决方案提供一个一栈式的架构。其能力包括:

  • 技术能力(技术比较广泛而且也比较熟练)
  • 整合能力
  • 行业知识
  • 需求分析能力
  • 沟通能力
  • 执行能力
  • 推动能力

再来看看,软件架构师的核心地位:

  1. 在团队外部,架构师,需要与项目经理沟通,通过系统分析师理解需求,让测试分析师制定测试与开发的同步计划,架构好软件产品后,还要告之配置经理进行系统配置实施。
  2. 在团队内部,架构师,要让设计员根据需求设计好软件,并让用户界面设计员做好界面设计,同时还会设计到数据库设计、系统集成、实施等相关任务工作分解。

从能力层级上看,平台架构师比前端架构师要求知识高一些,而解决方案架构师则是更加具备综合能力的高端人才。

如Tristan,如果想在UI设计上有所发展,可以往前端架构师发展,并在UI用户体验上特别花功夫,好的UI设计与用户体验对于客户是非常重要的。

如当前的我,目前也就偏向于平台架构师,但这是一个对技术有强依赖性,对一些技术架构要非常清楚。

当然,我们最好大家都能朝着解决方案架构师发展,它有一些类似产品经理(如Leo要即将发展的方向)这样的感觉,但又不全是,解决方案架构师,眼光会放得更远,有一种战略性的架构,与软件市场很密切。所以必须具有一定的行业知识。

UI Automation组内其他人也可以根据自身的特点,来发展规划自己的未来。上图中几乎是全部软件业人才角色分布图,供作参考。

但是,我们注意的是,"沟通能力、执行能力、推动能力"是上面三种架构师的基本能力,架构师没有35岁底线之说,它是技术与管理的衔接层,所以非常适合有着技术背景,但想往管理发展的你们。如果未来,从图中,你不难看出,在管理团队、分析团队、测试团队以及实施团队,你都可以拿下。当然大部分都会走向最上面的管理团队,这时你再来个转身,绝对华丽,将你所有的精力花在软件项目或软件企业管理上,哪怕是CEO,总裁级别,经过你这样的修炼,你将无可替代!!!加油!VMM UI Automation team!!

Pingpong Terms

Aarons, Hughes Ruth || (USA) 1936 and 1937 World Champion (Women’s single)
accuracy | 准确(性)| n.
ace | 发球得分 | n.
adjust | 调整 | v.
advantage | 优势 | n.
aggressive | 具有进攻性的 | a.
agile | 灵活的 | a.
agility | 灵活(性) | n.
all-out | 全攻型的 | a. an all-out hitter
all-round | 全能的,技术全面的 | a. It’s good to have an all-round game and
play all the strokes properly. ||
American grip (See Seemiller grip) np.
An | 安宰亨 (韩国选手) | (KOR) ?
angle (closed angle; open angle) | 角度(合角/朝下,仰角/朝上) | n.
anti- (anti-topspin; anti-loop) | 防 (上旋,弧旋,旋转) | pref.
anti-doping | 反兴奋剂的 | a. the World Anti-Doping Agency (WADA) | 世界反兴
奋组织 | Anti-doping code | 反兴奋剂条例 |
antispin | 防转胶皮 | n. an inverted rubber sheet that’s very slick, so
spin doesn’t take on it. It usually has a dead sponge underneath. It’s
mostly used for defensive shots. Also known as anti.
arc | 弧线 | n.
arm | 手臂 | n. upper arm | 大/上臂 |
assessment | 评估 | n.
attack | 进攻 | v. a devastating ~ (?)
attack after serve | 发抢(发球抢攻的缩略)|
attack on serve | 接发抢 (接发球抢攻的缩略)|
attacker | 攻击型选手, 进攻方 | n.
AUT | 奥地利 | Austria
B English-Chinese Table Tennis Dictionary
back | 背 | n.
back | 后面的 | a.
backhand | 反手 | n. A shot done with the racket to the left of the left
elbow for a right-hander, the reverse for a left-hander.
Backhand backside drive / loop
backing | 抵, 顶 | n.
backspin | 下旋, 下转 | n. A type of spin used mostly on defensive shots.
When you chop the ball, you produce backspin. The bottom of the ball will
move away from you. This is also called chop or underspin.
backward | 向后 | adv.
badge | 胸徽,袖徽等标牌 | n.
balance | 平衡 | n. a good ~ of speed and spin
ball | 球 | n.
band | 带子,套子 | n. head ~ 额带,wrist ~ 护腕
Barna, Victor || (HUN) 1930, 1932, 1933, 1933 and 1935 World Champion (Men’
s single)
barrier | 球挡 | n.
bat | 球拍 | n.
bat | 球拍 | n.
be down | 落后 | v.
beat | 打败 | v.
BEL | 比利时 | Belgium
Bengtsson (Stellan Bengtsson) | 本格森 (瑞典) | (SWE)1971 World Champion
bent | 弯曲 | v.
Bergmann, Richard || (AUT) 1937, (ENG) 1939, 1948 and 1950 World Champion (
Men’s single)
best four (of seven) | 七局四胜制 | np.
best three (of five) | 五局三胜制 | np.
best two (of three) | 三局两胜制 | np.
blade | (乒乓球)底板 | n. The racket, usually without covering. | 无覆盖物的
球拍 | shakehand ~ | 横握式底板 | penhold ~ | 直握式底板 | Japanese penhold
~ | 日本式直握底板 | Japanese hinoki single ply ~ | 日本式桧木地板 | Carbon
~ | 碳精/素底板 | arylate ~ | 芳基纤维底板 | titantium ~ | 钛底板 |
offensive ~ | 进攻型底板 | defensive ~ | 防守型底板 | allround ~ | 全面型底
板 |
block | 挡球, 推挡 | v. n. A quick, off the bounce return of an aggressive
drive done by holding the racket in the ball’s path.
blocker | 推挡型选手 |
BLR | 白俄罗斯 | Byelorussia
Boll | 波尔 (德国选手) | (GER)
bounce | 弹,跳 | v. n.
break | 打破,破坏 | v. to break the looper’s rhythm ||
brush | 摩擦 | v. n.
bucket | 球筐 | n. a ~ of balls for multi-ball practice
C English-Chinese Table Tennis Dictionary
calves | |
Cao Yanhua | 曹燕华(中国选手)| (CHN) 1983 and 1985 World Champion (Women’
s single)
captain | 队长 | n.
center line | 中线 | np.
change line | 变线 |
cheerleader | 啦啦队长 | n.
chest | 胸 | n.
Chiang Peng Lung | 蒋澎龙 (台北选手) | (TPE)
Chiu Chung-hui | 邱钟惠(中国选手)| (CHN) 1961 World Champion (Women’s
single)
CHN | 中国 | China
chop | 削球 | v. n. A defensive return of a drive with backspin, usually
done from well away from the table. (see backspin)
chop block | 下旋推挡 | np. A block where the racket is chopped down at
contact to create backspin.
chopper | 削球选手 | n. A  of play where chopping is the primary shot.
Chuang Tse-tung | 庄则栋 (中国选手)| (CHN) 1961, 1963 and 1965 World
Champion (Men’s single)
closed | (球拍)前倾, (球拍)下压 | a.
closed racket | 前倾的/下压的球拍,压拍型 | np. Racket position in which the
hitting surface is aimed downward, with the top edge leaning away from you.
[参看open racket |亮拍型|]
coach | 教练 | n.
comeback | 追上, 扳回 | n.
concentration | 注意力集中 | n.
confidence | 自信 | n.
consistency | 稳定(性)| n.
consistent | 始终的 | a.
consistently | 始终地 | a.
consolation singles | 安慰赛单打| np.
constantly | 不停地 | a.
contact | 接触 | v. n.
control | 控制 | v. n. receive control (接发球控制)
Corbillon (Cup) | 考比伦 (杯) (女子团体冠军)| n.
corner | 角落 | n.
counter-attack | 反攻 | n.
counterdrive | 反攻, 对攻 | v. n A drive made against a drive. Some players
specialize in counterdriving.
counter-loop | 反拉弧旋,反拉 | v. n. To loop a loop. (see loop)
countersmash | 反攻, 对攻 | v. n. To smash a smash. (see smash)
court | 比赛赛区 | n.
CRE | 希腊 | Greece
CRO | 克罗地亚 | Croatia
crosscourt | 球台对角的 | a. A ball that is hit diagonally from corner to
corner. ||
crossover | 交叉步 | n. A  of footwork for covering the wide forehand.
crucial | 关键的 | a.
cut | 削球 | v. n.
D English-Chinese Table Tennis Dictionary
Dawei ITTF Pro Tour Grand Finals 2001 or 2001 Dawei ITTF Pro Tour Grand
Finals | 大维2001 国际乒联职业巡回总决赛 |
dead | 不转球? | n. A ball with no spin.
deep (See long) | 长球 | a. 1. A ball that bounces on the opponent’s side
of the table very close to the endline. 2. A serve or push that would not
bounce twice on the opponent’s side of the table (if given the chance)
default | 失去比赛资格 | Being disqualified from a match for any reason.
defeat | 打败 | v.
defender | 防守方, 防守型运动员 | n.
defensive | 防守的 | a.
delegation | 代表团 | n.
Deng Yaping | 邓亚萍(中国选手)| (CHN) 1991, 1995 and 1997 World Champion (
Women’s single)
Depetrisova, Vlasta || (TCH) 1939 World Champion (Women’s single)
deuce | 局点前的最后平局,如20平,10平 | n.
diagonal | 斜线的 | a.
diagonally | 斜线地 | adv.
die out | (旋转)消失 | vp.
direction | 方向 | n.
disguise | 装作,假装 | v.n.
disputed edge ball, a | 有争议的擦边球 | np.
double | 连击 | n.
double bounce | 两跳 | np. A ball that hits the same side of the table twice
. The person on that side loses the point.
doubles | 双打 | n.
down | 下, 落后 | adv. prep. a.
down the line | 直线 | np. A ball that is hit along one side of the table.
draw | 抽签 | n.
drive | 攻球 | v. n.
drop shot | 吊球,近网短球 | np. Putting the ball so short that the opponent
has trouble reaching it. Done when the opponent is away from the table.
dump shot | 勉强的球 | np. All hitters must accept the fact that sometimes
you must go for a dump shot to make sure that you don’t miss an opportunity
to smash. (Larry, 1993: 119)
E English-Chinese Table Tennis Dictionary
early (stage) | 上升期 | a. n.
Eguchi, Fujie || (JPN) 1957 World Champion (Women’s single)
elbow | 肘部 | n.
endline | 底线 | n.
ENG | 英格兰 | England
erratic | 无规律的 | a. an ~ shot || However, his backhand seemed more
erratic, and he began to make mistake from both wings.
error | 错误 | n.
even up | 打平 | vp.
exert the authority | 执法, 行使职权 | vp.
expedite system / rule | 超时轮换发球制 / 规则 | np. If a game has continued
for 15 minutes without the game ending, the expedite rule takes effect. A
point is awarded to the receiver who returns 13 consecutive shots after
expedite has been called. Players alternate serves after expedite has been
called.
explosive | 爆发的 | a. ~ power ||
Adelyn 发表于 2005-10-14 14:03
F English-Chinese Table Tennis Dictionary
Farkas, Gizella || (HUN) 1947, 1948 abd 1949 World Champion (Women’s single)
fast | 快 | a.
fast-paced | 节奏快的 | a.
feed | 喂球 | v.
feeder | 喂球者,陪练者 | n.
final | 决赛 | n.
finalist | 参加决赛的选手 | n.
fish | 放高球 | v (See lobber)
flat | 平球,不转的球 | n. A ball that has no spin, usually traveling fast.
The ball hits the racket straight on, at a perpendicular angle, e.g. a flat
shot ||
flexibility | 灵活(性)| n,
flight | (球的)飞行 | n.
flip | (台内)挑攻,旋转球拍 | v. n. An aggressive topspin return of a ball
that lands near the net (a short ball). To move in twists and turns
float | 漂,浮 | a. backspin is usually float
follow-through | 手臂击球后的惯性动作 | n. It is the natural progression of
the racket forward and up after a stroke
follow-up | 跟进球 | n.
foot | 脚 | n.
footwork (forward, backward, side-to-side) | 步法 | n. How a person moves to
make a shot.
forearm | 前臂 | n.
forehand | 正手 | n. Any shot done with the racket to the right of the elbow
for a right-hander, to the left for a left-hander.
forward | 向前 | adv.
FRA || France
free hand | 非执拍手 | np. The hand not holding the racket.
front | 前面的 | a.
Fukazu, Naoko || (JPN) 1965 World Champion (Women’s single)
G English-Chinese Table Tennis Dictionary
Gaiten, Jean-Philippe | 盖亭 (法国选手) | (FRA) 1993 World Champion
game | (比赛) 局 | n.
game point | 局点 | np.
Ge Xinai | 葛新爱(中国选手)| (CHN) 1979 World Champion (Women’s single)
GER | 德国 | Germany
get in | 上前, 上手 | v.
glue | 胶水 | n.
graze | 摩擦 | v.
grazing | 摩擦的 | a.
grip | 执拍法 | n. pen grip ||, pendulum serve grip || (Larry, 1993: 109)
grippy | 粘球的 | a. grippy inverted rubber
groove | 适应,顺手 | n. fig. If a hitter gets into a groove, the game’s
over.
Guo Yuehua | 郭跃华(中国选手)| (CHN) 1981 and 1983 World Champion (Men’s
single)
gym | 体育馆 | n.
H English-Chinese Table Tennis Dictionary
hand (right hand; left hand) | 手 | n.
handicap events | 残疾人比赛 / 赛事? | np. A tournament event where points
are spotted to make the match even.
handle | 球拍的柄 | n. straight (ST) | 直柄 | flare (FL) | 收腰柄 | anatomic
(AN) | 葫芦柄 | conical | 锥形柄 |
hanger | ? | n.
hard rubber | 光胶皮球拍 | np. A type of racket covering with pips-out
rubber but no sponge underneath. It was the most common covering for many
years until the development of sponge rubber but is now rarely used.
Hasegawa, Nobuhiko | (日本选手) | (JPN) 1967 World Champion (Men’s single)
He Zhili | 何智丽(中国选手)| (CHN) 1987 World Champion (Women’s single)
Hidden serve | 遮掩式发球 | n
high | 高的 | a.
high kicking loop | 高吊弧旋 | np.
high toss serve | 高抛发球 | n. A serve where the ball is thrown high into
the air. This increases both spin and deception.
hip | 臀 | n.
hip | 臀部 | n.
His En-ting | 郗恩廷(中国选手)| (CHN) 1975 World Champion (Men’s single)
hit | 击球 | v.
hitter | 击球方,攻击型选手 | n. A  of play where hitting is the primary
shot.
Hu Yu-lan | 胡玉兰(中国选手)| (CHN) 1973 World Champion (Women’s single)
HUN | 匈牙利 | Hungary
Hyun Jung hwa || (KOR) 1993 World Champion (Women’s single)
I English-Chinese Table Tennis Dictionary
incoming | 来/到的 | a.
index finger | 食指 | np.
intimidated | 战战兢兢的,畏畏缩缩的 | a.
inverted racket | 反胶球拍 | np.
inverted sponge | 反胶 | np. The most common racket covering. It consists of
a sheet of pimpled rubber on top of a layer of sponge. The pips point
inward, toward the sponge, so the surface is smooth. This is the opposite of
pips-out sponge, where the pips point outward, away from the sponge.
ISR | 以色列 | Israel
ITA | 意大利 | Italia
Itoh (Shigeo Itoh) | 伊藤 (日本选手)| 1969 World Champion
ITTF (International Table Tennis Federal) | 国际乒联 (国际乒乓球联合会) |
The governing body for world table tennis.
J English-Chinese Table Tennis Dictionary
Jacobi, Roland || (HUN) 1926 World Champion (Men’s single)
Jiang Jialiang | 江嘉良 (中国选手)| (CHN)1985 and 1987 World Champion
Jonyer (Istvan Jonyer) | 约尼尔(瑞典选手)| (SWE) 1975 World Champion
Joo Se-Hyok | 朱思赫 (韩国选手) | (KOR)
JPN | 日本 | Japan
judge | 判断 | v.
judgment | 判断 | n.
Jung Kuo-tuan | 容国团(中国选手)| (CHN) 1959 World Champion (Men’s single)
junk player | 怪球手 | np. A player who uses an unusual racket covering,
usually long pips or antispin.
K English-Chinese Table Tennis Dictionary
Karlsson | 卡尔松 (瑞典选手) | (SWE)
Keen Trinto | 凯恩 (荷兰选手) | (NED)
keep | 保持 | v.
Kettnerova, Marie || (TCH) 1933 and 1935 World Champion (Women’s single)
kill off | 扣失 | But Kim Taek Soo kills Liu Guozheng’s serve off. | 但是金
泽洙扣杀刘国正的发球失误 |
kill shot | 扣杀球 | np. (see smash)
Kim Hyon Hui | 金英姬 (朝鲜选手)| (PRK)
Kim Hyong Mi | 金香美 (朝鲜选手)| (PRK)
Kim Taek Soo | 金泽洙 (韩国选手) | (KOR)
Klampar (Tibor Klampar) | 克兰帕尔(波兰选手) | (POL)
knee | 膝 | n.
knife grip (See western grip) | 横拍握法 | np.
knock acock | 打败 | vp.
Kohno, Mitsuru | (日本选手) | (JPN) 1977 World Champion (Men’s single)
Kolar, Stanislav || (TCH) 1936 World Champion (Men’s single)
Kong Linghui || (CHN) gold titleholder of the Olympics, the World
Championships, the World Cup.
KOR | 韩国 | Korea
Korbel | 科贝尔 (德国选手) |
Kowada, Toshiko || (JPN) 1969 World Champion (Women’s single)
Adelyn 发表于 2005-10-14 14:04
L English-Chinese Table Tennis Dictionary
late (stage) | 下降期 | a. n.
Leach, Johnny || (ENG) 1949 and 1951 World Champion (Men’s single)
Lead | 领先 | v. a. In the second game, Persson again has a big ~, this time
17-8 and ties it up at 17 all! Nine in a row! But Persson pulls it out, 22-
20.
Lee Eun Sil | 李恩实 (韩国选手) | (KOR)
left-handed | 左手的 | a.
lefthander | 左手运动员 | n.
lefty | 左手选手| n.
leg | 腿 | n.
let | 重发,无效,不算,重新来过 | n. If play is interrupted for any reason
during a rally, a let is called and the point does not count.
let serve | 擦网重发 | np. The most common type of let when a serve nicks
the net. As with other lets, the serve is taken over again.
light | 轻的,轻微的 | a. light spin
Lin Hui-ching | 林慧卿(中国选手)| (CHN) 1971 World Champion (Women’s
single)
line (side line, endline) | 台边 (边线,底线)| n.
lineman | 底线司线员 | n.
little (spin) | 轻微的(旋转)| a.
Liu Guoliang || gold titleholder of the Olympics, the World Championships,
the World Cup
loaded | 加转的 | a. A ball with a great deal of spin.
lob | (放/打) 高球 | v. n. A high defensive return of a smash. Usually done
with topspin or sidespin.
lobber | 放高球的选手 | n.
long | 长的 | a. See deep.
long pips | 长胶 | np. A type of pips-out rubber where the pips are long and
thin and bend on contact with the ball. It returns the ball with whatever
spin was on it at contact and is very difficult to play against if you aren
’t used to it.
loop (fast loop; slow loop) | (拉)弧旋球(前冲弧旋,高吊弧旋)| v.n. A
heavy topspin shot, usually considered the most important shot in the game.
Many players either specialize in looping or in handling the loop. The back-
of-racket penhold backhand loop (直板反手反面拉,即为直板横拉)
looper | 弧旋球选手 | n. A  of play where the primary shot is the loop.
loose | 松的 | a.
lose | 失利, 输 | v.
low | 低的 | a.
low fast loop | 前冲弧旋 | np.
LUX | 卢森堡 | Luxemburg
M English-Chinese Table Tennis Dictionary
match | 比赛 | n. A two or three or three out of five games contest.
match point | 赛点 | np.
Matsuzaki, Kimiyo || (JPN) 1959 and 1963 World Champion (Women’s single)
Mechlovits, Zoltan || (HUN) 1928 World Champion (Men’s single)
medium (pace) | 中等节奏 | a.
Mednyanszky, Maria || (HUN) 1926, 1928, 1929, 1930 and 1931 World Champion (
Women’s single)
middle game | 过渡球 |
miss | 漏掉,错过 | v.
mixed doubles | 混合双打 | np.
more (spin) | 加转 | a.
Morisawa, Sachiko || (JPN) 1967 World Champion (Women’s single)
move | 移动,使…移动 | v. n. Move your opponent in and out. ||
multi-ball | 多球 | n.
N English-Chinese Table Tennis Dictionary
neck | 颈 | n.
NED | 荷兰 | Netherlands
net | 球网 | n. the ~ assembly | 网具 | np.
net measurer | 量网器 | np.
nick | 擦,触 | v.
NOR | 挪威 | Norway
no-spin | 不转 | a.
O English-Chinese Table Tennis Dictionary
official | 官员 | n.
offside | 出边线的 | a.
off-the-table | 离台的 | a.
Ogimura, Ichiro || (JPN) 1954 and 1956 World Champion (Men’s single)
Oh Sang Eun | 吴尚垠 (韩国选手) | (KOR)
Okawa, Tomie || (JPN) 1956 World Champion (Women’s single)
oncoming | 过来的(球)| a.
one-sided | 单面的 | a. A one-sided looper rushes all over the court trying
to use a forehand. ||
Ono, Seiji | (日本选手) | (JPN) 1979 World Champion (Men’s single)
open | 向上的 | a.
open racket | 拍面向上的球拍,亮拍型 | np. Racket position in which the
hitting surface is aimed upward, with the top edge learning toward you. [参
看closed racket |压拍型|]
opening | 空档 | n.
opponent | 对手 | n.
P English-Chinese Table Tennis Dictionary
pace | 节奏 | n. slow / medium / fast ~
Pak, Yung sung | 朴英顺? | (PRK) 1977 World Champion (Women’s single)
palm | 掌心 | n.
Park Sang Jun | 朴相俊 (韩国选手) | (KOR)
partner | 双打搭档 |
passive | 消极的 | a.
passively | 消极地 | a.
penhold backside hit | 直板横打 | penhold backside drive / loop / smash etc.
Pen-hold  grip | 直握法 | np.
penholder | 直拍选手 | n. A type of grip used mostly by Asians. It gives the
best possible forehand but the most awkward backhand of the conventional
grips.
Perry, Fred || (ENG) 1929 World Champion (Men’s single)
Persson, Jorgen | 佩尔森 (瑞典选手) | (SWE) 1991 World Champion
pick hitting | 防守中的突击 |
pick-hitting | 挑攻,台内攻球 ?| n.
picky | 挑剔的,讲究的? | a.
pips | 胶粒 | n. The small conical bits of rubber that cover a sheet of
table tennis rubber.
pips-out | 正胶 胶粒向外 | a. A type of racket covering. It consists of a
sheet of pips-out rubber on top of a layer of sponge. The pips point outward
, the opposite of inverted.
placement | 落点 | n. the placement of loops / smash / forehand drive
play | 打,比赛 | n.v. in ~ | 正在比赛中 | play sloppily | 打得稀松 |
player (professional; amateur; Chinese, Swedish, ..;) | 选手(专业的,业余的
,中国的,瑞典的)| n. a hot player | 兴奋起来的,状态好的选手 | np. A hot/
cold ~ | 冷热型选手 | a ‘big match’ player | 大赛型选手 |
playing area, the | 赛区 | np.
playing bag | 运动包 | np.
playing surface, the | 球台台面 | np. The top of the table, including the
upper edges.
point | 得分 | n.
POL | 波兰 | Poland
pop up | (球)高了 | vp. to pop a drop shot up | 短球放高了 |
position | 位置 | n. a ready ~ | 准备位置,预备位置 |
power | 力量 | n. Players are often using the whole of the arm plus the body
and a twist of the hips to get power. ||
powerful (loop) | 强力弧旋球 | a.
press area | 新闻报道区 | np.
Primorac | 普里莫拉茨(克罗地亚选手)| (CRO)
Pritzi, Gertrude || (AUT) 1937 (co-) and 1938 World Champion (Women’s
single)
PRK | 朝鲜 | the Peoples Republic of Korea
pull ahead | 领先 | v.
pull away | 把比分拉开 | v.
push | 搓球 | v. n. A backspin return. Usually defensive. A sidespin ~ | 侧
(旋)搓,侧挤 |
pusher | 搓球方,搓球者 | n.
put away | 扣杀 | vp. See smash.
put-away shot | 扣杀球 | np. See smash.
Punch-block | 加力推档 |
Q English-Chinese Table Tennis Dictionary
Qiao Hong | 乔红(中国选手)| (CHN) 1989 World Champion (Women’s single)
R English-Chinese Table Tennis Dictionary
racket | 球拍 | n. What you hit the ball with. The blade plus covering. A
combination racket | 两面性能不同的球拍 |
racket hand | 执拍手 | np. The hand that holds the racket.
racket holder | 球拍袋/包 | np.
rally | 来回球, 相持球 | n. The hitting of the ball back and forth,
commencing with the serve and ending when a point is won. It is the period
of during which the ball is in play (See play).
rating events | 排名赛 | np. A tournament event that requires players to be
rated under a specified amount.
reaction time | 反应时间 | np.
read | 识别 (旋转)| v. the spin
ready position/home position | 预备位置 | np.
receive | 接发球 | v. n. The return of a serve.
receiver | 接发球方,接发球的选手 | n.
reckless | 粗心的 | a.
recover | 还原 | v.
relax | 放松 | v.
return | 回球 | v.
rhythm | 节奏 | n. to break the looper’s rhythm ||
right-handed | 右手执拍的 | a.
righty | 右手选手 | n
rip | 积极进攻,(侧身)抢攻 | v Liu Guozheng serves and rips a winner. |刘
国正发球抢攻得手| to rip a loop winner | 抢拉赢得一分 |
ROM | 罗马尼亚 | Romania
Rosskopf | 罗斯可普夫 (德国选手) | (GER)
rotate | 转圈 | v.
Rozeanu, Angelica || (ROM) 1950, 1951, 1952, 1953, 1954 and 1955 World
Champion (Women’s single)
rubber (backside) | 胶皮 | n. The racket covering. Sometimes refers only to
the rubber on top of a sponge base. || pips out ~ | 正胶胶皮 | inverted ~ |
反胶胶皮 | long pips ~ | 长胶胶皮 | semi long pips ~ | 半长胶胶皮 | anti-
spin/loop ~ | 防弧胶胶皮 |
rubber clearner | 胶皮清洁擦 | np. Used to keep the surface of inverted
rubber clean.
runner-up | 亚军 | n.
Rye Ji Hye | 柳智慧 (韩国选手)| (KOR)
Ryu Seung Min | 柳承敏 (韩国选手) | (KOR)
Adelyn 发表于 2005-10-14 14:06
S English-Chinese Table Tennis Dictionary
Saive | (大) 塞夫 (比利时选手) | (BEL)
Saive | (小) 塞夫 (比利时选手) | (BEL)
Samsorov | 萨姆索洛夫 (白俄罗斯选手) | (BLR) 2001 World Champion (Men’s
single)
sandwich rubber | 套胶 | np. A sponge base covered by a sheet of rubber with
pips that point either in or out. If pointed in, it is inverted sponge. If
pointed out, it is pips-out sponge.
Satoh, Hiroji || (JPN) 1952 World Champion (Men’s single)
Scharlger, W. ?| 施拉格 (奥地利选手) | (AUT)
score | 计分,得分 | n.
scorer | 计分器 | n.
scoring system | 记分制 | 11-point ~ | 11分制 | 21-point ~ | 21 分制 |
second response | 二次反应 | n.
Seemiller grip | 西弥勒握拍法,又称美国式握拍法 | np. A grip that is often
used in the United States, named after five-time U.S. National Champion Dan
Seemiller, who developed it. Many coaches consider it an inferior grip and,
outside the U.S. it is almost unheard of. Also known as the American grip.
semi-final | 半决赛 | n.
semifinalist | 参加半决赛的选手 | n.
serve (non-spin; backspin / underspin; side-spin; topspin one; toss; squad;
two bounce serve) | 发球 | v. n. The first shot, done by the server. It
begins with the ball being tossed from the palm of the hand and struck by
the racket. || In the first, Bratanov was leading 19-16, but Placemtini won
all five on his ~ to win 21-19. ||
serve off | 发球 | v.
server | 发球方 | n.
service | 发球 | n. ~ change ||
set | 局? | n.
set point | 局点?| np.
severe | 强烈的 | a. ~ spin ||
shadow | 徒手的 | a. a shadow-stroke | 徒手击球 | a ~ practice | 徒手训练 |
You can ~ the various shots and techniques without a ball. ||
shakehands grip | 横握法 | np. The most popular grip. It gives the best
balance of forehand and backhand.
shift | 移动 (重心)| v.
short | 短的,短球 | a. n. A ball that would bounce twice on the opponent’s
side of the table if given the chance.
shot | 好球,击球 | n. a high-risk shot ||
shoulder | 肩 | n.
sidespin | 侧旋 | n. A type of spin most effective on serves. When you use
sidespin, that ball spins like a record on a record player.
sidespin loop | 侧旋弧旋 | np.
Sido, Ferenc | 西多 | (HUN) 1953 World Champion (Men’s single)
SIN | 新加坡 | Singapore
single | 单打的 | a.
singles | 单打 | n.
Sipos, Anna | ? | (HUN) 1932 and 1933 World Champion (Women’s single)
slow | 慢节奏 | a. ~ pace
smash | 扣杀 | v. n. a smash is a very powerful forehand or backhand attack,
also called kill shot or a put-away shot. / Ball is hit with enough speed
so opponent cannot make a return. Also called a kill shot or a put-away shot.
smother kill | 近网吊球 | n. a short return, usually used against a lob. /
to smash right off the bounce. Usually done against lob.
snap | 挑?| v. a wrist ~ | 翻腕挑(尤指反手的台内挑打)|
souvenir | 礼品 | n.
spare | 备用的 | a.
specialize in | 擅长(训练的结果)| vp.
spectator | 观众 | n.
speed | 速度 | n.
speed glue | 速干胶 | n. A type of glue that can be put under a sheet of
table tennis sponge to make it faster and spinier.
spin (topspin; backspin / underspin; sidespin; non-spin; spin combination,
crazy spin) | 旋转 | v. n. The rotation of the ball.
sponge | 海绵 | n. The bouncy rubber material used in sandwich covering
under a sheet of rubber with pips. It revolutionized the game and ended the
hard rubber age in the 1950s.
stable | 稳定的 | a.
stage (early, peak, late, very late) | 击球时期 (上升期, 高点, 下降期, 下降
末期) | n.
stamina | 抗击/对抗能力 | n.
stance | 站位 | n. ready stance | 预备站位 |
stand | 观众席, 比赛观看台 | n.
standing | 名次 | n.
step | 移动步伐 | v. Watch to see if they step around the backhand corner
too much. If so, return the fast serve wide to the forehand with a quick
drive or block. (Larry, 1993: 119)
step around | 侧身 | v.
Stipancic (Anton Stipancic) | 斯蒂潘尼契(南斯拉夫选手)| (YUG)
strategy | 策略 |
strength | 力量,优势 | n. Everyone has his or her strengths and weaknesses.
|| But the second wasn’t exactly close at first ---- Boll led 17-7, 19-2,
and 20-16, mostly on the ~ of his attack.
stretch | 伸展 | v. n.
stroke (preparation; contact; follow-through) | 击球 | n. v. Any shot used
in the game, including the serve.
Surbek, Dragutin | 舒贝克 (南斯拉夫选手)| (YUG)
swat | 劈杀 | v.
Swaythling (Cup) | 斯韦思林 (杯) (男子团体冠军)| n.
SWD | 瑞典 | Sweden
swing | 摆动 | v.
Szabados, Miklos || (HUN) 1931 World Champion (Men’s single)
T English-Chinese Table Tennis Dictionary
table | 球台 | n.
tactics | 战术,策略 | n.
Tanaka, Toshiaki || (JPN) 1955 and 1957 World Champion (Men’s single)
TCH | 捷克 |
team | 球队 | n. the national ~ | 国家队 |
tentative | 暂时的 | a.
The World Table Tennis Championships (The World Championships) | 世界乒乓球
锦标赛 (世锦赛) | np.
threat | 威胁 | v. n.
thumb | 大拇指 | n.
tie | 比赛, 对局, 对阵 | n.
tight | 紧的, 紧张的 | a.
timeout | 暂停 | n.
title | 头衔,赛事冠军 | n.
Tong Ling | 童玲(中国选手)| (CHN) 1981 World Champion (Women’s single)
topspin | 上旋球 | n. A type of spin used on most aggressive shots. When you
topspin the ball, the top of the ball moves away from you.
toss | 抛起 | v.
tournament | 锦标赛 | n. A tournament is a competition in which players who
win a match continue to play further matches until just one person or team
is left to win the champion.
TPE | 中国台北 | China Taiwan
trade off | 各胜(多少) | v.
training | 训练 | n.
trampoline | 弹,崩 | v. Hold the racket with a relaxed grip and let the
ball sink into the sponge and trampoline back, usually with a light topspin.
(Larry, 1993: 63)
transfer | 转移 | v. He transfers some of the weight on his left leg to his
right leg. ||
transition | 转换 | n. a smooth, flowing ~ from one shot to the next ||
truck | 躯干 | n.
Twirl | 球拍旋转法 | n. Turning the racket, usually in the middle of a serve
or while the ball is in play, to confuse the opponent as to which type of
rubber is being used at a specific time. Not as deceptive as it once was,
because of the ‘two color rubber’ law. (San Diego Table Tennis Association
Website )
twist | 扭转 | v.
Two color rubber law | 两面胶皮两种颜色条例 | np. A table tennis law put
into effect, namely using red rubber on one side of the racket and black
rubber on the other side, so opponents will not be so confused and deceived.
(San Diego Table Tennis Association Website )
two-step footwork | 跨步 | np. The most popular  of footwork where the
player starts with a short step with the foot on the side he or she is
moving to. Then, the other foot follows as both feet move together.
U English-Chinese Table Tennis Dictionary
umpire | 裁判 | n. The official who keeps score and enforces rules during a
match.
underspin (See backspin) | 下旋 | n. See backspin.
up | 上 |
upward | 向上 | adv.
USA | 美国 | the United States of America
USTTA (United States Table Tennis Association) | 美国乒乓协会 | The
governing body for table tennis in the United States.
V English-Chinese Table Tennis Dictionary
Vana, Bohumil || (TCH) 1938 and 1947 World Champion (Men’s single)
versatility | 技术多样,战术多变 | n. The key to beating a hitter is
versatility. (Larry, 1993” 119)
very late (stage) | (球的) 下降后期 | ap.
vicious | 凌厉的 | a. a ~ loop ||
volley | 台内阻挡 | v. n. To hit the ball before it bounces on your side of
the table, which results in an immediate loss of the point for you.
volley block | ?|
W English-Chinese Table Tennis Dictionary
waist | 腰 | n.
Waldner | 瓦尔德内尔 (瑞典选手) | (SWE) 1997 World Champion (Men’s single)
Wang Nan | 王楠 (中国选手)| (CHN) 2000 and 2001 World Champion (Women’s
single)
warm up | 准备活动,热身 | v. n.
watt | 亚光(的),不反光的 | n.a.
weakness | 弱点,劣势 | n.
weight | 重心,重量 | n.
western  grip | 横握 (球拍) | np.
whiff | ?| v.
whiplash | 鞭击似的击球 | n. to give the ball ~ ||
whip-through | 鞭击似的抽球 | n. The whip-through after contact is really
quick.
wide forehand | 正手空档 | np. Go to the wide forehand, then come back to
the backhand.
wild card | 外卡 | n
win | 胜利, 嬴 | v.
wing (one-winged; two-winged) | 面 (单面;两面) | n.
winner | 胜方,得分 | n.
wrist | 手腕 | n. a ~ snap
X English-Chinese Table Tennis Dictionary
Y English-Chinese Table Tennis Dictionary
Yoo Nam Kyu | 刘南奎 (韩国选手) | (KOR) 1988 Olympic Champion (Men’s single)
YUG | 南斯拉夫 | Yugoslavia
Yugoslavia | 南斯拉夫 | (YUG)

Thursday, March 10, 2011

【什么是Bit-map】(ZT)

 

所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。

如果说了这么多还没明白什么是Bit-map,那么我们来看一个具体的例子,假设我们要对0-7内的5个元素(4,7,2,5,3)排序(这里假设这些元素没有重复)。那么我们就可以采用Bit-map的方法来达到排序的目的。要表示8个数,我们就只需要8个Bit(1Bytes),首先我们开辟 1Byte的空间,将这些空间的所有Bit位都置为0(如下图:)

image

然后遍历这5个元素,首先第一个元素是4,那么就把4对应的位置为1(可以这样操作 p+(i/8)|(0x01<<(i%8)) 当然了这里的操作涉及到Big-ending和Little-ending的情况,这里默认为Big-ending),因为是从零开始的,所以要把第五位置为一(如下图):

image

然后再处理第二个元素7,将第八位置为1,,接着再处理第三个元素,一直到最后处理完所有的元素,将相应的位置为1,这时候的内存的Bit位的状态如下:

image

然后我们现在遍历一遍Bit区域,将该位是一的位的编号输出(2,3,4,5,7),这样就达到了排序的目的。下面的代码给出了一个BitMap的用法:排序。

//定义每个Byte中有8个Bit位

#include <memory.h>

#define BYTESIZE 8

void SetBit(char *p, int posi)

{

for(int i=0; i < (posi/BYTESIZE); i++)

{

p++;

}

*p = *p|(0x01<<(posi%BYTESIZE));//将该Bit位赋值1

return;

}

void BitMapSortDemo()

{

//为了简单起见,我们不考虑负数

int num[] = {3,5,2,10,6,12,8,14,9};

//BufferLen这个值是根据待排序的数据中最大值确定的

//待排序中的最大值是14,因此只需要2个Bytes(16个Bit)

//就可以了。

const int BufferLen = 2;

char *pBuffer = new char[BufferLen];

//要将所有的Bit位置为0,否则结果不可预知。

memset(pBuffer,0,BufferLen);

for(int i=0;i<9;i++)

{

//首先将相应Bit位上置为1

SetBit(pBuffer,num[i]);

}

//输出排序结果

for(int i=0;i<BufferLen;i++)//每次处理一个字节(Byte)

{

for(int j=0;j<BYTESIZE;j++)//处理该字节中的每个Bit位

{

//判断该位上是否是1,进行输出,这里的判断比较笨。

//首先得到该第j位的掩码(0x01<<j),将内存区中的

//位和此掩码作与操作。最后判断掩码是否和处理后的

//结果相同

if((*pBuffer&(0x01<<j)) == (0x01<<j))

{

printf("%d ",i*BYTESIZE + j);

}

}

pBuffer++;

}

}

int _tmain(int argc, _TCHAR* argv[])

{

BitMapSortDemo();

return 0;

}

【适用范围】

可进行数据的快速查找,判重,删除,一般来说数据范围是int的10倍以下

【基本原理及要点】

使用bit数组来表示某些元素是否存在,比如8位电话号码

【扩展】

Bloom filter可以看做是对bit-map的扩展

【问题实例】

1)已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。

8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的电话)

2)2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上,在遍历这些数的时候,如果对应位置的值是0,则将其置为1;如果是1,将其置为2;如果是2,则保持不变。或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个 2bit-map,都是一样的道理。

Wednesday, March 09, 2011

TreeView Inside UpdatePanel of ASP.Net

If you have a TreeView inside an UpdatePanel, it is better to have the following properties set as such PopulateNodesFromClient="false" and EnableClientScript="false". Hope this will solve at least some random problems when clicking/expanding/collapsing a treenode.

Saturday, March 05, 2011

数据切分 of Database(ZT)

 

可能很多读者朋友在网上或杂志上都已经多次见到关于数据切分的相关文章了,只不过在有些文章中称之为数据的 Sharding。其实无论称之为数据的 Sharding 还是数据的切分,其实质都是一样的。简单来说,就是指通过某种特定的条件,将存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。数据的切分同时还可以提高系统的总体可用性,因为单台设备 Crash 之后,只有总体数据的某部分不可用,而不是所有的数据。

数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。一种是按照不同的表(或者 Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分;另外一种则是根据表中数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上,这种切分称之为数据的水平(横向)切分。

垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低、相互影响很小、业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。

水平切分与垂直切分相比,稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更复杂。

当某个(或者某些)表的数据量和访问量特别大,通过垂直切分将其放在独立的设备上后仍然无法满足性能要求时,就必须将垂直切分和水平切分相结合,先垂直切分,然后再水平切分,这样才能解决这种超大型表的性能问题。

下面就针对垂直、水平及组合切分这三种数据切分方式的架构实现及切分后数据的整合进行相应的分析。

 

数据的垂直切分

我们先来看一下,数据的垂直切分到底是如何切分的。数据的垂直切分,也可以称为纵向切分。将数据库想象成由很多个一大块一大块的"数据块"(表)组成,垂直地将这些"数据块"切开,然后把它们分散到多台数据库主机上面。这样的切分方法就是垂直(纵向)的数据切分。

一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的数据对应到数据库中就是一个或多个表。而在架构设计中,各个功能模块相互之间的交互点越统一、越少,系统的耦合度就越低,系统各个模块的维护性及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。

功能模块越清晰,耦合度越低,数据垂直切分的规则定义也就越容易。完全可以根据功能模块来进行数据的切分,不同功能模块的数据存放于不同的数据库主机中,可以很容易就避免跨数据库的 Join 存在,同时系统架构也非常清晰。

当然,很难有系统能够做到所有功能模块使用的表完全独立,根本不须要访问对方的表,或者须要将两个模块的表进行 Join 操作。这种情况下,就必须根据实际的应用场景进行评估权衡。决定是迁就应用程序将需要 Join 的表的相关模块都存放在同一个数据库中,还是让应用程序做更多的事情--完全通过模块接口取得不同数据库中的数据,然后在程序中完成 Join 操作。

一般来说,如果是一个负载相对不大,而且表关联又非常频繁的系统,那可能数据库让步,将几个相关模块合并在一起,减少应用程序工作的方案可以减少较多的工作量,是一个可行的方案。

当然,通过数据库的让步,让多个模块集中共用数据源,实际上也是间接默许了各模块架构耦合度增大的发展,可能会恶化以后的架构。尤其是当发展到一定阶段,发现数据库实在无法承担这些表所带来的压力,不得不面临再次切分时,所带来的架构改造成本可能远远大于最初就使用切分的架构设计。

所以,在数据库进行垂直切分的时候,如何切分、切分到什么样的程度,是一个比较考验人的难题。这只能在实际的应用场景中通过平衡各方面的成本和收益,才能分析出一个真正适合自己的拆分方案。

比如在本书所使用的示例系统的 example 数据库中,我们简单分析一下,然后设计一个简单的切分规则,进行一次垂直拆分。

系统功能基本可以分为4个功能模块:用户、群组消息、相册以及事件,分别对应为如下这些表:

(1)用户模块表:user,user_profile,user_group,user_photo_album

(2)群组讨论表:groups,group_message,group_message_content,top_message

(3)相册相关表:photo,photo_album,photo_album_relation,photo_comment

(4)事件信息表:event

初略一看,没有哪个模块可以脱离其他模块独立存在,模块与模块之间都存在着关系,莫非无法切分?

当然不是,再稍微深入分析一下,可以发现,虽然各个模块所使用的表之间都有关联,但是关联关系还算清晰,也比较简单。

群组讨论模块和用户模块之间主要存在通过用户或群组关系来进行关联。一般都会通过用户的 id 或 nick_name 及 group 的 id 来进行关联,通过模块之间的接口实现不会带来太多麻烦。

相册模块仅仅与用户模块存在用户的关联。这两个模块之间的关联基本只有通过用户 id 关联的内容,简单清晰,接口明确。

事件模块与各个模块可能都有关联,但是都只关注其各个模块中对象的ID信息,同样比较容易分拆。
所以,第一步可以将数据库按照功能模块相关的表进行一次垂直拆分,每个模块所涉及的表单独分到一个数据库中,模块与模块之间的表关联在应用系统端都通过接口来处理。如数据垂直切分示意图(图14-1)所示:

(点击查看大图)图14-1  垂直切分示意

通过这样的垂直切分之后,之前只能通过一个数据库来提供的服务,就被分拆成4个数据库来提供服务,服务能力自然是增加几倍了。

垂直切分的优点:

数据库的拆分简单明了,拆分规则明确;

应用程序模块清晰明确,整合容易;

数据维护方便易行,容易定位。

垂直切分的缺点:

部分表关联无法在数据库级别完成,要在程序中完成;

对于访问极其频繁且数据量超大的表仍然存在性能瓶颈,不一定能满足要求;

事务处理相对复杂;

切分达到一定程度之后,扩展性会受到限制;

过度切分可能会带来系统过于复杂而难以维护。

针对于垂直切分可能遇到数据切分及事务问题,在数据库层面实在是很难找到一个较好的处理方案。实际应用案例中,数据库的垂直切分大多是与应用系统的模块相对应的,同一个模块的数据源存放于同一个数据库中,可以解决模块内部的数据关联问题。而模块与模块之间,则通过应用程序以服务接口的方式来相互提供所需要的数据。虽然这样做在数据库的总体操作次数方面确实会有所增加,但是在系统整体扩展性及架构模块化方面,都是有益的。可能某些操作的单次响应的时间会稍有增加,但是系统的整体性能很可能反而会有一定的提升。而扩展瓶颈问题,就只能依靠下一节将要介绍的数据水平切分架构来解决了

数据的水平切分

上面一节分析介绍了数据的垂直切分,本节分析数据的水平切分。数据的垂直切分基本上可以简单地理解为按照表或模块来切分数据,而水平切分则不同。一般来说,简单的水平切分主要是将某个访问极其平凡的表再按照某个字段的某种规则分散到多个表中,每个表包含一部分数据。

简单来说,可以将数据的水平切分理解为按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中。当然,为了能够比较容易地判定各行数据被切分到哪个数据库中了,切分总是须要按照某种特定的规则来进行的:如根据某个数字类型字段基于特定数目取模,某个时间类型字段的范围,或者某个字符类型字段的 hash 值。如果整个系统中大部分核心表都可以通过某个字段来进行关联,那这个字段自然是一个进行水平分区的上上之选了,当然,非常特殊无法使用的情况除外。

一般来说,像现在非常火爆的 Web 2.0 类型网站,基本上大部分数据都能够通过会员用户信息关联上,可能很多核心表都非常适合通过会员 ID 来进行数据的水平切分。而像论坛社区讨论系统,就更容易切分了,可以按照论坛编号来进行水平切分。切分之后基本上不会出现各个库之间的交互。

如果示例系统的所有数据都是和用户关联的,那么就可以根据用户来进行水平拆分,将不同用户的数据切分到不同的数据库中。当然,唯一区别是用户模块中的 groups 表和用户没有直接关系,所以 groups 不能根据用户来进行水平拆分。对于这种特殊情况下的表,完全可以独立出来,放在一个独立的数据库中。其实这个做法可以说是利用了前面一节所介绍的"数据的垂直切分"方法,将在下一节中更为详细地介绍这种垂直切分与水平切分同时使用的联合切分方法。

所以,对于示例数据库来说,大部分的表都可以根据用户 ID 来进行水平切分。不同用户相关的数据进行切分之后存放在不同的数据库中。如将所有用户 ID 通过被2 取模然后分别存放于两个不同的数据库中。每个和用户 ID 关联上的表都可以这样切分。这样,基本上每个用户相关的数据,都在同一个数据库中,即使须要关联,也非常容易实现。

可以通过水平切分示意图(图14-2)更为直观地展示水平切分相关信息:

(点击查看大图)图14-2

水平切分的优点:

表关联基本能够在数据库端全部完成;

不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;

应用程序端整体架构改动相对较少;

事务处理相对简单;

只要切分规则能够定义好,基本上较难遇到扩展性限制。

水平切分的缺点:

切分规则相对复杂,很难抽象出一个能够满足整个数据库的切分规则;

后期数据的维护难度有所增加,人为手工定位数据更困难;

应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难。

垂直与水平联合切分的使用

前面两节内容中,分别了解了"垂直"和"水平"这两种切分方式的实现和切分之后的架构信息,以及两种架构各自的优缺点。但是在实际的应用场景中,除了那些负载并不是太大、业务逻辑也相对简单的系统可以通过上面两种切分方法之一来解决扩展性问题之外,恐怕其他大部分业务逻辑复杂、系统负载大的系统,都无法通过上面任何一种数据的切分方法来实现较好的扩展性,这就需要将上述两种切分方法结合使用,不同的场景使用不同的切分方法。

本节将结合垂直切分和水平切分各自的优缺点,进一步完善整体架构,并提高系统的扩展性。

一般来说,数据库中的所有表很难通过某一个(或少数几个)字段全部关联起来,所以仅仅通过数据的水平切分无法解决所有问题。而垂直切分也只能解决部分问题,对于那些负载非常高的系统,即使只是单个表都无法通过单台数据库主机来承担其负载。必须结合"垂直"和"水平"两种切分方式,充分利用两者的优点,避开其缺点。

每一个应用系统的负载都是一步一步增长上来的,在开始遇到性能瓶颈的时候,大多数架构师和DBA都会选择先进行数据的垂直拆分,因为这样的成本最低,最符合这个时期所追求的最大投入产出比。然而,随着业务的不断扩张,系统负载的持续增长,在系统稳定一段时期之后,经过了垂直拆分之后的数据库集群可能再次不堪重负,遇到了性能瓶颈。

此时该如何抉择?是再次进一步细分模块,还是寻求其他的解决办法?如果我们再像最开始那样继续细分模块,进行数据的垂直切分,那可能在不久的将来,又会遇到现在所面临的同样问题。而且随着模块的不断细化,应用系统的架构也会越来越复杂,整个系统很可能会出现失控的局面。

这时候就必须要利用数据水平切分的优势来解决遇到的问题。而且,完全不必在使用数据水平切分时,推倒之前进行数据垂直切分的成果,而是在其基础上利用水平切分的优势来避开垂直切分的弊端,解决系统复杂性不断扩大的问题。而水平拆分的弊端(规则难以统一)也已经被之前的垂直切分解决掉了,让水平切分可以进行得得心应手。

对于示例数据库,假设在最开始进行了数据的垂直切分,然而随着业务的不断增长,数据库系统遇到了瓶颈,我们选择重构数据库集群的架构。如何重构?考虑到之前已经做好了数据的垂直切分,而且模块结构清晰明确,而业务增长的势头越来越猛,即使现在再次拆分模块,也坚持不了太久。所以选择了在垂直切分的基础上再进行水平切分。

经历过垂直切分后的数据库集群中的各个数据库都只有一个功能模块,而每个功能模块中的所有表基本上都会与某个字段进行关联。如用户模块全部都可以通过用户 ID 进行切分,群组讨论模块则都通过群组 ID 来切分,相册模块则根据相册 ID 来进切分,最后的事件通知信息表考虑到数据的时限性(仅仅访问最近某个事件段的信息),则按时间来切分。

组合切分示意图(图14-3)展示了切分后的整个架构:

(点击查看大图)图14-3  组合切分示意图

实际上,在很多大型的应用系统中,垂直切分和水平切分基本上是并存的,而且经常在不断地交替进行,以增加系统的扩展能力。我们在应对不同的应用场景时,也须要充分考虑到这两种切分方法的局限及优势,在不同的时期(负载压力)使用不同的方式。

联合切分的优点:

可以充分利用垂直切分和水平切分各自的优势而避免各自的缺陷;

让系统扩展性得到最大化提升。

联合切分的缺点:

数据库系统架构比较复杂,维护难度更大;

应用程序架构也更复杂。

14.5  数据切分及整合方案(1)

通过前面的章节,已经清楚了通过数据库的数据切分可以极大地提高系统的扩展性。但是,数据库中的数据经过垂直和(或)水平切分被存放在不同的数据库主机之后,应用系统面临的最大问题就是如何让这些数据源得到较好的整合,可能这也是很多读者非常关心的一个问题。本节主要的内容就是分析各种可以帮助我们实现数据切分及数据整合的整体解决方案。

数据的整合很难依靠数据库本身来达到,虽然 MySQL 存在 Federated 存储引擎,可以解决部分类似的问题,但是在实际应用场景中却很难较好地运用。那该如何来整合这些分散在各个 MySQL 主机上的数据源呢?

总的来说,存在两种解决思路:

(1)在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库,在模块内完成数据的整合;

(2)通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明。

可能90%以上的人在面对这两种解决思路时都会倾向于选择第二种,尤其是系统不断庞大复杂的时候。确实,这是一个非常正确的选择,虽然短期内须要付出的成本可能会相对大一些,但对整个系统的扩展性来说,是非常有帮助的。

所以,对于第一种解决思路就不过多分析了,下面重点分析第二种思路中的一些解决方案。

1. 自行开发中间代理层

在决定选择通过数据库的中间代理层来解决数据源整合的架构方向之后,有不少公司(或者企业)自行开发了符合自身应用特定场景的代理层应用程序。

自行开发中间代理层可以最大程度地应对自身应用的特点,最大化定制个性化需求,在面对变化的时候也可以灵活应对。这应该是自行开发代理层最大的优势了。

当然,选择自行开发,享受个性化定制最大化乐趣的同时,自然也需要投入更多的成本来进行前期研发及后期的持续升级改进工作,而且本身的技术门槛可能也比简单的 Web 应用更高。所以,在决定选择自行开发之前,仍须要进行比较全面的评估。

由于自行开发更多时候考虑的是如何更好地适应自身应用系统,应对自身的业务场景,所以这里也不好分析太多。下面将主要分析当前比较流行的几种数据源整合解决方案。

2. 利用 MySQL Proxy 实现数据切分及整合

MySQL Proxy 是 MySQL 官方提供的一个数据库代理层产品,和 MySQL Server 一样,它也是一个基于 GPL 开源协议的开源产品。可用来监视、分析或传输它们之间的通讯信息。它的灵活性允许最大限度地使用它,目前具备的功能主要有连接路由、Query分析、 Query过滤和修改、负载均衡,以及基本的 HA 机制等。

实际上,MySQL Proxy 本身并不具有上述所有的功能,而是提供了实现上述功能的基础。要实现这些功能,还须要我们自行编写 LUA 脚本。

MySQL Proxy 实际上是在客户端请求与 MySQL Server 之间建立了一个连接池。所有客户端请求都发向 MySQL Proxy,然后经由 MySQL Proxy进行相应的分析,判断出是读操作还是写操作,分发至对应的 MySQL Server 上。对于多节点 Slave 集群,也可以起到负载均衡的效果。如MySQL Proxy 基本架构图(图14-4):

(点击查看大图)图14-4  MySQL Proxy 架构

通过上面的架构简图,可以清晰地看到 MySQL Proxy 在实际应用中所处的位置,以及能做的基本事情。MySQL Proxy 详细的实施细则在 MySQL 官方文档中有非常详细的介绍和示例,感兴趣的读者朋友可以直接从 MySQL 官方网站免费下载或者在线阅读,这里就不赘述。

数据切分及整合方案(2)

3. 利用 Amoeba 实现数据切分及整合

Amoeba 是一个基于 Java 开发的,专注于解决分布式数据库数据源整合 Proxy 程序的开源框架,基于 GPL3 开源协议。目前,Amoeba 已经具有 Query 路由、Query 过滤、读写分离、负载均衡及 HA 机制等相关内容,如图14-5所示。

Amoeba 主要解决以下几个问题:

(1)数据切分后复杂数据源整合;

(2)提供数据切分规则并降低数据切分规则给数据库带来的影响;

(3)降低数据库与客户端的连接数;

(4)读写分离路由。

(点击查看大图)图14-5  Amoeba For MySQL架构

可以看出,Amoeba 所做的事情,正好就是通过数据切分来提升数据库的扩展性所需要的。

Amoeba 并不是一个代理层的 Proxy 程序,而是一个开发数据库代理层 Proxy 程序的框架,目前基于 Amoeba 所开发的 Proxy 程序有 Amoeba For MySQL 和 Amoeba For Aladin 两个。

Amoeba For MySQL是专门针对 MySQL 数据库的解决方案,前端应用程序请求的协议及后端连接的数据源数据库都必须是 MySQL。对于客户端的任何应用程序来说,Amoeba For MySQL 和一个 MySQL 数据库没有什么区别,任何使用 MySQL 协议的客户端请求,都可以被 Amoeba For MySQL 解析并进行相应的处理。Amoeba For可以告诉我们 Amoeba For MySQL 的架构信息(出自 Amoeba 开发者博客):

Amoeba For Aladin 则是一个适用更为广泛、功能更为强大的 Proxy 程序。它可以同时连接不同数据库的数据源为前端应用程序提供服务,但是仅仅接受符合 MySQL 协议的客户端应用程序请求。也就是说,只要前端应用程序通过 MySQL 协议连接上来,Amoeba For Aladin 会自动分析 Query 语句,根据 Query 语句中所请求的数据来自动识别出该 Query 的数据源是在什么类型数据库的哪一个物理主机上。Amoeba For Aladdin 架构图(图14-6)展示了 Amoeba For Aladin 的架构细节(出自 Amoeba 开发者博客)。

乍一看,两者好像完全一样嘛。细看才会发现两者主要的区别仅在于通过 MySQL Protocal Adapter处理之后,根据分析结果判断出数据源数据库,然后选择特定的 JDBC驱动和相应协议连接后端数据库。

其实通过上面两个架构图大家可能已经发现了 Amoeba 的特点,它只是一个开发框架,我们除了选择它已经提供的 For MySQL 和 For Aladin 这两款产品之外,还可以基于自身的需求进行二次开发,得到更适合自己应用特点的 Proxy 程序。

但对于使用 MySQL 数据库来说,不论是 Amoeba For MySQL 还是 Amoeba For Aladin 都可以很好地使用。当然,考虑到任何一个系统越是复杂,其性能肯定就会有一定的损失,维护成本自然也会更高一些。所以,在仅仅须要使用 MySQL 数据库的时候,还是建议使用 Amoeba For MySQL。

Amoeba For MySQL 的使用非常简单,所有的配置文件都是标准的 XML 文件,总共有4个,分别如下:

amoeba.xml--主配置文件,配置所有数据源及Amoeba自身的参数;

rule.xml--配置所有 Query 路由规则的信息;

functionMap.xml--配置用于解析 Query 中的函数所对应的 Java 实现类;

rullFunctionMap.xml--配置路由规则中需要使用到的特定函数的实现类。

(点击查看大图)图14-6  Amoeba For Aladdin架构

如果您的规则不是太复杂,基本上仅使用上面4个配置文件中的前面两个就可完成所有工作。Proxy 程序常用的功能如读写分离、负载均衡等配置都在 amoeba.xml 中进行。此外,Amoeba 已经支持了实现数据的垂直切分和水平切分的自动路由,路由规则可以在 rule.xml 进行设置。

目前 Amoeba稍有欠缺的主要就是其在线管理功能及对事务的支持方面了,曾经在与相关开发者的沟通过程中提出过这方面的建议,希望能够提供一个可以进行在线维护管理的命令行管理工具,方便在线维护使用,得到的反馈是管理专门的管理模块已经纳入开发日程了。另外在事务支持方面Amoeba暂时还无法做到,即使客户端应用在提交给 Amoeba 的请求时包含事务信息的,Amoeba 也会忽略事务相关信息。当然,在经过不断完善之后,我相信事务支持肯定是Amoeba 重点考虑的功能。

关于Amoeba更为详细的使用方法读者可以通过Amoeba开发者博客(http://amoeba. sf.net)上面提供的使用手册获取,这里就不再细述了。

4. 利用 HiveDB 实现数据切分及整合

和前面的 MySQL Proxy 及 Amoeba 一样,HiveDB 同样是一个基于 Java 针对MySQL 数据库的提供数据切分及整合的开源框架,只是目前的 HiveDB 仅仅支持数据的水平切分。主要解决大数据量下数据库的扩展性及数据的高性能访问问题,同时支持数据的冗余及基本的 HA 机制。

HiveDB 的实现机制与 MySQL Proxy 和 Amoeba 有一定的差异,它并不是借助 MySQL 的 Replication 功能来实现数据的冗余,而是自行实现了数据冗余机制,而其底层主要是基于 Hibernate Shards 来实现数据切分工作。

在 HiveDB 中,通过用户自定义的各种 Partition keys(即制定数据切分规则),将数据分散到多个 MySQL Server 中。访问时运行 Query 请求,则会自动分析过滤条件,并行从多个 MySQL Server 中读取数据,并合并结果集返回给客户端应用程序。

单纯从功能方面来讲,HiveDB 可能并不如 MySQL Proxy 和 Amoeba 那样强大,但是其数据切分的思路与前面二者并无本质差异。此外,HiveDB 并不只是一个开源爱好者所共享的内容,而是存在商业公司支持的开源项目。

HiveDB 官方网站上的 HiveDB 架构示意图(图14-7),描述了 HiveDB 如何来组织数据的基本信息,虽然不能详细地表现出架构方面的信息,但是也基本可以展示其在数据切分上独特的一面了。

(点击查看大图)图14-7  HiveDB 架构示意

5. 其他实现数据切分及整合的解决方案

除了上面介绍的几个数据切分及整合的整体解决方案之外,还存在很多其他的解决方案、如在MySQL Proxy 的基础上做进一步扩展的 HSCALE,通过 Rails 构建的 Spock Proxy,以及基于 Pathon 的 Pyshards,等等。

不管大家选择使用哪一种解决方案,总体设计思路基本上都不应该有任何变化,即通过数据的垂直和水平切分,增强数据库的整体服务能力,让应用系统的整体扩展能力尽量得到提升,扩展方式尽可能便捷。

只要通过中间层 Proxy 应用程序较好地解决了数据切分和数据源整合问题,那么数据库的线性扩展能力将像应用程序一样方便:只要通过添加廉价的 PC Server 服务器,即可线性增加数据库集群的整体服务能力,让数据库不再轻易成为应用系统的性能瓶颈。

14.6  数据切分与整合中可能存在的问题

这里,大家应该对数据切分与整合的实施有一定的认识了,或许很多读者都已经根据各种解决方案的优劣基本选定了适合于自己应用场景的方案,后面的工作主要就是实施准备了。

在实施数据切分方案之前,仍要分析一些可能存在的问题。一般来说,可能遇到的问题主要有以下几点:

引入分布式事务的问题;

跨节点 Join 的问题;

跨节点合并排序分页问题。

1. 引入分布式事务的问题

一旦数据进行切分被分别存放在多个 MySQL Server 中,不管切分规则设计得多么完美(实际上并不存在完美的切分规则),都可能造成之前某些事务所涉及的数据已经不在同一个 MySQL Server 中了。

在这样的场景下,如果应用程序仍然按照老的方案,那么势必须要引入分布式事务来解决。而在 MySQL 各个版本中,只有从 MySQL 5.0 开始以后的各个版本才对分布式事务提供支持,而且目前仅有 Innodb 提供分布式事务支持。不过,即使我们刚好使用了支持分布式事务的 MySQL 版本,同时也使用 Innodb 存储引擎,分布式事务本身对于系统资源的消耗就很大,性能也并不太高,引入分布式事务在异常处理方面会带来很多比较难控制的问题。

怎么办?其实可以通过一个变通的方法来解决这种问题,首先须要考虑的是:数据库是否是唯一一个能够解决事务的地方?其实并不是这样的,完全可以结合数据库及应用程序来共同解决。各个数据库解决自身的事务,然后通过应用程序来控制多个数据库上的事务。

也就是说,只要我们愿意,完全可以将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上的小事务,并通过应用程序来总控各个小事务。当然,这样做要求应用程序必须要有足够的健壮性,当然也会给应用程序带来一些技术难度。

2. 跨节点 Join 的问题

上面介绍了可能引入分布式事务的问题,现在再看看需要跨节点 Join 的问题。数据切分之后,也许有些老的 Join 语句无法继续使用,因为 Join 使用的数据源可能被切分到多个 MySQL Server 中了。

怎么办?这个问题从 MySQL 数据库角度来看,如果非得在数据库端直接解决的话,恐怕只能通过 MySQL 一种特殊的存储引擎 Federated 处理了。Federated 存储引擎是 MySQL 解决类似于 Oracle 的 DB Link 之类问题的方案。和 Oracle DB Link 的主要区别在于,Federated 会保存一份远端表结构的定义信息在本地。乍一看,Federated 确实是解决跨节点 Join 非常好的方案。但是我们还应该清楚一点,那就是如果远端的表结构发生了变更,本地的表定义信息是不会跟着发生变化的。如果在更新远端表结构的时候并没有更新本地的 Federated 表定义信息,Query 运行很可能出错,无法得到正确的结果。

对待这类问题,还是推荐通过应用程序来处理,先在驱动表所在的 MySQL Server 中取出驱动结果集,然后根据驱动结果集再到被驱动表所在的 MySQL Server 中取出相应的数据。可能很多读者朋友会认为这样做将对性能产生一定的影响,是的,确实会有一定的负面影响,但除此之外,基本上没有太多其他更好的解决办法了。而且,由于数据库通过较好的扩展之后,每台 MySQL Server 的负载就可以得到较好的控制,单纯针对单条 Query 来说,其响应时间可能比不切分之前要提高一些,所以性能方面带来的负面影响也并不是太大。更何况,类似于这种跨节点 Join 的需求也并不是太多,相对于总体性能而言,可能也只是很小一部分而已。所以为了整体性能,偶尔牺牲一点点,其实是值得的,毕竟系统优化本身就是很多取舍和平衡的过程。

3. 跨节点合并排序分页问题

一旦进行了数据的水平切分之后,可能就并不只有跨节点 Join 无法正常运行,有些排序分页的 Query 语句的数据源可能也会被切分到多个节点,其直接后果就是这些排序分页 Query 无法继续正常运行。其实这和跨节点 Join 是一个道理,数据源存在于多个节点上,要通过一个 Query 来解决,就是一个跨节点 Join 操作。同样 Federated 也可以部分解决,但存在的风险也一样。但是有一点不同:Join 很多时候都有一个驱动与被驱动的关系,所以它涉及的多个表之间的数据读取一般会存在一个顺序关系。但是排序分页就不同了,排序分页的数据源基本上可以说是一个表(或者一个结果集),并不存在顺序关系,所以从多个数据源取数据的过程是完全可以并行的。这样,排序分页数据的取数效率可以比跨库 Join 更高,所以带来的性能损失相对要小,在有些情况下可能比在原来未进行数据切分的数据库中效率更高了。当然,不论是跨节点 Join 还是跨节点排序分页,都会使应用服务器消耗更多的资源,尤其是内存资源,因为在读取访问及合并结果集的这个过程须要比不处理合并处理更多的数据。

分析到这里,可能很多读者朋友会发现,上面所有的这些问题,我的建议基本上都是通过应用程序来解决的。大家可能心里开始犯嘀咕了,是不是因为我是 DBA,所以就把很多事情都扔给应用架构师和开发人员了?

其实完全不是这样,首先应用程序由于其特殊性,可以非常容易做到很好的扩展性,但是数据库就不一样,必须借助很多其他的方式才能做到扩展,而且在扩展过程中,很难避免带来有些原来在集中式数据库中可以解决但被切分开成一个数据库集群之后就成为一个难题的情况。要想让系统整体得到最大限度的扩展,只能让应用程序做更多的事情,来解决数据库集群无法较好解决的问题。

 

 

14.7  小结

通过数据切分技术将一个大的 MySQL Server 切分成多个小的 MySQL Server,既解决了写入性能瓶颈问题,同时也再一次提升了整个数据库集群的扩展性。不论是通过垂直切分,还是水平切分,都能够让系统遇到瓶颈的可能性更小。尤其是在使用垂直和水平相结合的切分方法之后,理论上将不再遇到扩展瓶颈

数据库的读写分离(ZT)

这些题目比较细节哦,有些还真不知道,我做了很多年项目,觉得出这些题目让别人好发挥些,能考察出哪些人经验丰富:
1. 一个算法题(递归,字符串处理或急转弯)
2. 如果让你设计一个登陆系统(安全,验证,sso,用户表的设计)
3. 你所熟悉的一个设计模式以及在.net源码里哪里用到了(foreach-iterator, DBFactory...., Provider-strategy)
4. 列举下你熟悉的几种事务使用(DB,SqlTransaction,TransactionScope,DTC,事务提升,wcf分布式事务)
5. 你熟悉的多线程的实现方式(异步委托,thread,threadpool,backgroundworker,4.0里的task,AsyncEnumerator)
6. .net的编译过程(clr,pe,il,jit)
7. 你熟悉的DB优化技巧(读写分离,负载均衡,横向竖向切分)
8. 聊聊你最近使用的一个新技术,用的怎么个爽法
9. 如果给你几个人带,你怎么管理他们的工作(document,coding rule,tfs, scrum,bug track,Daily Build...)
10. 用英语随便写下来你对WebOS的看法.

 

 

 
文章分类:数据库

      随着一个网站的业务不断扩展,数据不断增加,数据库的压力也会越来越大,对数据库或者SQL的基本优化可能达不到最终的效果,我们可以采用读写分离的策略来改变现状。读写分离现在被大量应用于很多大型网站,这个技术也不足为奇了。ebay就做得非常好。ebay用的是oracle,听说是用Quest Share Plex 来实现主从复制数据。

     读写分离简单的说是把对数据库读和写的操作分开对应不同的数据库服务器,这样能有效地减轻数据库压力,也能减轻io压力。主数据库提供写操作,从数据库提供读操作,其实在很多系统中,主要是读的操作。当主数据库进行写操作时,数据要同步到从的数据库,这样才能有效保证数据库完整性。Quest SharePlex就是比较牛的同步数据工具,听说比oracle本身的流复制还好,mysql也有自己的同步数据技术。mysql只要是通过二进制日志来复制数据。通过日志在从数据库重复主数据库的操作达到复制数据目的。这个复制比较好的就是通过异步方法,把数据同步到从数据库。

      主数据库同步到从数据库后,从数据库一般由多台数据库组成这样才能达到减轻压力的目的。读的操作怎么样分配到从数据库上?应该根据服务器的压力把读的操作分配到服务器,而不是简单的随机分配。mysql提供了MySQL-Proxy实现读写分离操作。不过MySQL-Proxy好像很久不更新了。oracle可以通过F5有效分配读从数据库的压力。

ebay的读写分离(网上找到就拿来用了)


mysql的读写分离
       上面说的数据库同步复制,都是在从同一种数据库中,如果我要把oracle的数据同步到mysql中,其实要实现这种方案的理由很简单,mysql免费,oracle太贵。好像Quest SharePlex也实现不了改功能吧。好像现在市面还没有这个工具吧。那样应该怎么实现数据同步?其实我们可以考虑自己开发一套同步数据组件,通过消息,实现异步复制数据。其实这个实现起来要考虑很多方面问题,高并发的问题,失败记录等。其实这种方法也可以同步数据到memcache中。听说oracle的Stream也能实现,不过没有试过。

Friday, March 04, 2011

(ZT)Memory in .NET - what goes where

http://www.yoda.arachsys.com/csharp/memory.html

 

 

A lot of confusion has been wrought by people explaining the difference between value types and reference types as "value types go on the stack, reference types go on the heap". This is simply untrue (as stated) and this article attempts to clarify matters somewhat.

What's in a variable?

The key to understanding the way memory works in .NET is to understand what a variable is, and what its value is. At the most basic level, a variable is just an association between a name (used in the program's source code) and a slot of memory. A variable has a value, which is the contents of the memory slot it's associated with. The size of that slot, and the interpretation of the value, depends on the type of the variable - and this is where the difference between value types and reference types comes in.

The value of a reference type variable is always either a reference or null. If it's a reference, it must be a reference to an object which is compatible with the type of the variable. For instance, a variable declared as Stream s will always have a value which is either null or a reference to an instance of the Stream class. (Note that an instance of a subclass of Stream, eg FileStream, is also an instance of Stream.) The slot of memory associated with the variable is just the size of a reference, however big the actual object it refers to might be. (On the 32-bit version of .NET, for instance, a reference type variable's slot is always just 4 bytes.)

The value of a value type is always the data for an instance of the type itself. For instance, suppose we have a struct declared as:

struct PairOfInts
{
public int a;
public int b;
}


The value of a variable declared as PairOfInts pair is the pair of integers itself, not a reference to a pair of integers. The slot of memory is large enough to contain both integers (so it must be 8 bytes). Note that a value type variable can never have a value of null - it wouldn't make any sense, as null is a reference type concept, meaning "the value of this reference type variable isn't a reference to any object at all".



So where are things stored?



The memory slot for a variable is stored on either the stack or the heap. It depends on the context in which it is declared:




  • Each local variable (ie one declared in a method) is stored on the stack. That includes reference type variables - the variable itself is on the stack, but remember that the value of a reference type variable is only a reference (or null), not the object itself. Method parameters count as local variables too, but if they are declared with the ref modifier, they don't get their own slot, but share a slot with the variable used in the calling code. See my article on parameter passing for more details.


  • Instance variables for a reference type are always on the heap. That's where the object itself "lives".


  • Instance variables for a value type are stored in the same context as the variable that declares the value type. The memory slot for the instance effectively contains the slots for each field within the instance. That means (given the previous two points) that a struct variable declared within a method will always be on the stack, whereas a struct variable which is an instance field of a class will be on the heap.


  • Every static variable is stored on the heap, regardless of whether it's declared within a reference type or a value type. There is only one slot in total no matter how many instances are created. (There don't need to be any instances created for that one slot to exist though.) The details of exactly which heap the variables live on are complicated, but explained in detail in an MSDN article on the subject.



There are a couple of exceptions to the above rules - captured variables (used in anonymous methods and lambda expressions) are local in terms of the C# code, but end up being compiled into instance variables in a type associated with the delegate created by the anonymous method. The same goes for local variables in an iterator block.



A worked example



The above may all sound a bit complicated, but a full example should make things a bit clearer. Here's a short program which does nothing useful, but should demonstrate the points raised above.



using System;

struct PairOfInts
{
static int counter=0;

public int a;
public int b;

internal PairOfInts (int x, int y)
{
a=x;
b=y;
counter++;
}
}

class Test
{
PairOfInts pair;
string name;

Test (PairOfInts p, string s, int x)
{
pair = p;
name = s;
pair.a += x;
}

static void Main()
{
PairOfInts z = new PairOfInts (1, 2);
Test t1 = new Test(z, "first", 1);
Test t2 = new Test(z, "second", 2);
Test t3 = null;
Test t4 = t1;
// XXX
}
}


Let's look at what's where in memory at the line marked with the comment "XXX". (Assume that nothing is being garbage collected.)




  • There's a PairOfInts instance on the stack, corresponding with variable z. Within that instance, a=1 and b=2. (The 8 byte slot needed for z itself might then be represented in memory as 01 00 00 00 02 00 00 00.)


  • There's a Test reference on the stack, corresponding with variable t1. This reference refers to an instance on the heap, which occupies "something like" 20 bytes: 8 bytes of header information (which all heap objects have), 8 bytes for the PairOfInts instance, and 4 bytes for the string reference. (The "something like" is because the specification doesn't say how it has to be organised, or what size the header is, etc.) The value of the pair variable within that instance will have a=2 and b=2 (possibly represented in memory as 02 00 00 00 02 00 00 00). The value of the name variable within that instance will be a reference to a string object (which is also on the heap) and which (probably through other objects, such as a char array) represents the sequence of characters in the word "first".


  • There's a second Test reference on the stack, corresponding with variable t2. This reference refers to a second instance on the heap, which is very similar to the one described above, but with a reference to a string representing "second" instead of "first", and with a value of pair where a=3 (as 2 has been added to the initial value 1). If PairOfInts were a reference type instead of a value type, there would only be one instance of it throughout the whole program, and just several references to the single instance, but as it is, there are several instances, each with different values inside.


  • There's a third Test reference on the stack, corresponding with variable t3. This reference is null - it doesn't refer to any instance of Test. (There's some ambiguity about whether this counts as a Test reference or not - it doesn't make any difference though, really - I generally think of null as being a reference which doesn't refer to any object, rather than being an absence of a reference in the first place. The Java Language Specification gives quite nice terminology, saying that a reference is either null or a pointer to an object of the appropriate type.)


  • There's a fourth Test reference on the stack, corresponding with variable t4. This reference refers to the same instance as t1 - ie the values of t1 and t4 are the same. Changing the value of one of these variables would not change the value of the other, but changing a value within the object they both refer to using one reference would make that change visible via the other reference. (For instance, if you set t1.name="third"; then examined t4.name, you'd find it referred to "third" as well.)


  • Finally, there's the PairOfInts.counter variable, which is on the heap (as it's static). There's only a single "slot" for the variable, however many (or few) PairOfInts values there are.

Struct VS Class Performance(ZT)

以前有过讨论,实际上struct是很不应该滥用的。
以下情况不应使用struct:
1、需要继承扩展的类型
2、包含引用类型成员的类型
3、不参与运算的类型
4、字段很多的类型
值类型的优势在于储存在堆栈上,劣势在于值传递。储存在堆栈上所以值类型很适合用来运算,譬如说你要在同一个函数中大量使用同一类型,如int,值类型就很有优势,因为堆栈的速度比托管堆要快很多。值类型的劣势在于值传递,这样就造成无论调用什么函数,值类型都会被复制一份,赋值的时候也会被复制一份。大数据量的值类型显然会带来不小的开销,并且如果需要频繁赋值和函数调用的时候。

JIT In .Net(ZT)

JIT(Just In Time简称JIT)是.Net边运行边编译的一种机制,这种机制的命名来源于丰田汽车在20世纪60年代实行的一种生产方式,中文译为“准时制”。

.Net 的JIT编译器在设计初衷和运行方式来上讲,都与丰田汽车的这种“准时生产”思想体系有着很大的相似之处,所以让我们先来透过“准时生产”方式来理解.Net的JIT机制吧。

“准时生产”的基本思想可概括为“在需要的时候,按需要的量生产所需的产品”,这正是.Net JIT编译器的设计初衷,即在需要的时候编译需要的代码。

第一节.Me JIT

以C#为例,在C#代码运行前,一般会经过两次编译,第一阶段是C#代码向MSIL的编译,第二阶段是IL向本地代码的编译。第一阶段的编译成果是生成托管模块,第二阶段的编译成果是生成本地代码以供运行,从这里各位同学可以看出,第一阶段生成的MSIL是不能直接运行的。

这里先要解释一下什么是MSIL和托管模块。

MSIL:

MSIL 全称为Microsoft Intermediate Language,中文译为“微软中间语言”,它是一种介于高级语言和汇编语言之间的伪汇编语言(姑且这么叫,各位有不同意见的同学不必激动)。当用户编译运行一个.NET程序时,高级语言编译器会将源代码翻译成一组可以独立于CPU的指令。

可以看出IL 包括用于加载(ldstr )、存储(压栈、弹栈)和初始化对象(locals)以及调用对象方法(call)的指令,还包括用于算术和逻辑运算、控制流、直接内存访问、异常处理和其他操作的指令。

C#代码:

string str_test = "test";
System.String Str_test = "test";



对应IL码:



 



// 代码大小 14 (0xe)
.maxstack 1
.locals init ([0] string str_test,[1] string Str_test)
IL_0000: nop
IL_0001: ldstr "test"
IL_0006: stloc.0
IL_0007: ldstr "test"
IL_000c: stloc.1
IL_000d: ret



托管模块:



托管模块(managed module)是一个标准32位或64位Microsoft  Windows可移植可执行体(PE32或PE32+)文件,托管模块需要CLR才能执行,它包含了上面介绍的IL代码,还包含元数据、PE头、CLR头几部分。



元数据(metadata)可以理解为一个HashTable,Table中映射了内置类型和成员以及引用的类型和成员,这些类型与成员供IL使用,所以元数据总是需要关联对应的IL代码,编译器也是同时生成元数据与IL,以保证自描述的同步。



PE头(Portable Executable,中文译为可移植的可执行的)包括了PE32与PE32+,标示了托管模块的运行环境以及JIT优化本地代码时所要用到的信息,这在后面会讲到。



CLR头主要包括方法的入口地址标记,以及资源、强命名等信息,这些信息是GAC重要的参数依据。



下图可以表示出JIT的介入时机:



 



image



JIT是运行时的一个重要职责模块,它将IL转换为本地CPU指令,从上图可以看出,也许你不敢相信,即时编译这个过程是在运行时发生的,这会不会对性能产生影响呢?事实上答案是虽然是肯定的,但这种开销物有所值。



1、JIT所造成的性能开销并不显著。



2、JIT遵循计算机体系理论中两个经典理论:局部性原理与8020原则。局部性原理指出,程序总是趋向于使用最近使用过的数据和指令,这包括空间的和时间的,将局部性原理引申可以得出,程序总是趋向于使用最近使用过的数据和指令,以及这些正在使用的数据和指令临近的数据和指令(凭印象写的,但不曲解原意);而8020原则指出,系统大多数时间总是花费80%的时间去执行那20%的代码。   根据这两个原则,JIT在运行时会实时的向前、后优化代码,这样的工作只有在运行时才可以做到。



3、JIT只编译需要的那一段代码,而不是全部,这样节约了不必要的内存开销。



4、JIT会根据运行时环境,即时的优化IL代码,即同样的IL代码运行在不同CPU上,JIT编译出的本地代码是不同的,这些不同代码面向自己的CPU做出了优化。



5、JIT会对代码的运行情况进行检测,并对那些特殊的代码经行重新编译,在运行过程中不断优化。



实际上JIT的优点还不止如此,它对内联、策略引擎(.Net Discovery 系列之四--深入理解.Net垃圾收集机制(下) 中包含对策略引擎的描述)、CLR反馈、代码回收(非垃圾回收,这在第二节中会有介绍)等方面都会有不可磨灭的贡献。



必须指出的是JIT在第一次编译IL后,会修改对应方法相应的内存地址入口(绕口啊~~),下一次需要执行这个方法时,CLR会直接访问对应的内存地址,而不会经过JIT了。



第二节.编译与执行



在上一节中我们讨论了与JIT相关的一些元素和JIT的优势,这一节将为大家重点介绍JIT在编译方面的原理。



C#等高级语言必须被编译为IL才可被执行,IL在执行前必须被便以为本地代码才可运行,这里有两种方法可以获得本地代码,JIT方式和Native Image Generator方式,本节主要讨论JIT方式。



必须指出的是JIT在第一次编译IL后,会修改对应方法相应的内存地址入口,下一次需要执行这个方法时,CLR会直接访问对应的内存地址,而不会经过JIT了,这样无疑加快了程序运行的速度,这是怎样的一个过程呢?



以Load()方法为例,假如Load()方法中调用了两次同类型中的方法:



 



Void Load()
{
A.a1("First");

A.a1("Second");
}
static class A
{
Public void a1(string str){}

Public void a2(string str){}

Public void a3(string str){}
}



运行时,操作系统会根据托管模块中各种头信息,装载相应的运行时框架,Load()被加载,由于是第一次加载,这会触发对Load()的即时编译,JIT会检测Load()中引用的所有类型,并结合元数据遍历这些类型中定义的所有方法实现,并用一个特殊的HashTable(仅用于理解)储存这些类型方法与其对应的入口地址(在未被JIT前,这个入口地址为一个预编译代理 (PreJitStub),这个代理负责触发JIT编译),根据这些地址,就可以找到对应的方法实现。





在初始化时,HashTable中各个方法指向的并不是对应的内存入口地址,而是一个JIT预编译代理,这个函数负责将方法编译为本地代码。注意,这里JIT还没有进行编译,只是建立了方法表



下表(表1)为首次加载调用时HashTable的情况:



表1 方法表示意



方法槽



方法描述



a1()



PreJitStub



a2()



PreJitStub



a3()



PreJitStub



好了有了这个HashTable后,JIT开始编译第一个被调用的方法A.a1("First"),这是由一个JIT内部函数来完成的(上面提到的),遗憾的事,目前还没有发现介绍这个函数的相关资料,有些书中称它为“JIT编译者”,那本文也这么称呼它吧。



下图为首次调用方法时的示意图:



image



图2 触发JIT编译



JIT借助元数据和IL生成被调用方法的本地代码后,会将这些代码缓存在动态内存中,然后修改HashTable中对应方法的入口地址,将其修改为本地代码的内存片地址(如表2所示),并将这个地址返回给CLR经行执行,A.a1("First")执行完毕,代码继续运行。



运行至A.a1("Second ")时,会直接执行A.a1()方法的内存代码,不会进行再次编译,表2 为再次加载时HashTable的情况。



表2 方法表变化



方法槽



方法描述



a1()



XXXXXXXXX内存地址



a2()



PreJitStub



a3()



PreJitStub



再次加载流程示意图:



image



图3 未触发JIT编译



image



图4 方法表、方法描述、预编译代理关系



图2中所示的MS核心引擎指的是一个叫做MSCorEE的DLL,即Microsoft .NET Runtime Execution Engine,它是一个桥接DLL,连同mscorwks.dll主要完成以下工作:




  1. 查找程序集中包含的对应类型清单,并调用元数据遍历出包含的方法。


  2. 结合元数据获得这个方法的IL。


  3. 分配内存。


  4. 编译IL为本地代码,并保存在第3步所分配的内存中。


  5. 将类型表(就是指上文中提到的HashTable)中方法地址修改为第3步所分配的内存地址。


  6. 跳转至本地代码中执行。



所以随着程序的运行时间增加,越来越多的方法的IL被编译为本地代码,JIT的调用次数也会不断减少。



下面借助WinDbg来证实以上的说法,示例中的源程序可以到这里下载到:



http://files.cnblogs.com/isline/IsLine.JITTester.rar



代码中定义了3个类,分别为A、B、C,在“GO”按钮按下后,将调用类型A中的a1()方法,而Form1_Load 中什么也不做,目的是程序运行后,在空载的情况下查看方法描述对应地址入口的情况。



好,第一步运行JITTester.exe程序,并打开WinDbg附加这个进程



image



图 5 附件进程



第二步,附加进程成功后,在WinDbg中加载SOS.dll



image



图6 加载SOS.dll



第三步,使用name2ee命令遍历所有已加载模块,name2ee格式为name2ee *! [程序集].[类型]



image



图7 查看类型信息



回车后注意高亮区域的信息:



image



图8 JIT前A类型的信息



高亮区域显示的是“”,这说明虽然运行和程序,但未点击按钮时,A类型未被JIT,因为它还没有入口地址。这一点体现了即时、按需编译的思想。



同样,!name2ee *!JITTester.B和!name2ee *!JITTester.C命令会得到同样的结果。



好,现在做第4步操作,Detach Debuggee进程,并回到程序中点击“GO”按钮



image



图9 点击按钮



第五步 重新附加进程(参考第一步),这时程序已经调用了new A().a1()方法,并重新执行命令!name2ee *!JITTester.A ,注意高亮部分



image



图10 JIT后A类型的信息



和图8中的信息比较,图10中的方法表地址已经变为JIT后的内存地址,这时图4中的Stub槽将被一条强制跳转语句替换,跳转目标与该地址有关。这一点说明JIT在大多情况下,只编译一次代码。



同样命令查看B类型:



image



图11 JIT后B类型的信息



该类型未被调用,所以还未被JIT。



C类型:



image



图12 JIT后C类型的信息



由于实例化A类型时和C类型相关,所以C类型已经JIT了。



第三节.Native Image Generator



Native Image Generator中文译为本地代码生成器,我更习惯叫它“本地映像”,因为通过工具NGen.exe生成的本地代码是无法部分载入的,这意味着操作系统会加载整个程序集文件。



    上一节中提到过,有两种方法可以获得本地代码,JIT方式和Native Image Generator方式,JIT方式是在运行时动态编译需要的代码,而NGen.exe会创建托管程序集的本机映像,并且将该映像安装到GAC中,运行该程序集时,就会自动使用该本机映像而不是JIT它们。



这听起来似乎很美妙,但是你必须做好以下准备:




  1. 当FrameWork版本、CPU类型、操作系统版本发生变化时,.Net会恢复JIT机制。


  2. NGen.exe工具并不能避免发布IL,事实上,即使使用NGen.exe工具,CLR依然会使用到元数据和IL。 


  3. 忽略了局部性原理(上一节中提到的),系统会加载整个映像文件到内存中,并很可能重定位文件,修正内存地址引用。 


  4. NGen.exe生成的代码无法在运行时进行优化,无法直接访问静态资源,也无法在应用程序域之间共享程序集。 



此外,JIT不但有编译的本事,还会根据内存资源情况换出使用率低的代码,节省资源,这对于一些基于.Net平台的电子产品是很重要的。



所以,除非你已十分清楚程序性能是由于首次编译造成的性能问题,否则尽量不要人工生成本地代码。

Thursday, March 03, 2011

正确理解 C# 中的 ref 关键字 (ZT)

http://www.cnblogs.com/Kellin/archive/2007/09/02/879084.html
 
Summary:
 
很容易理解,如果仅仅是改变传入对象的成员字段,使用和不使用ref是一样的。
而当函数内使用了new重新创建对象,而又想将这种变化带出函数,就需要使用ref了
 
// ----------------------------------------
// MyClass definition
public class MyClass
{
public int Value;
}


// ----------------------------------------
// Tester methods
public static void TestRef(ref MyClass m1)
{
// 这里的 m1 也就相当于大家所说的指向指针的指针:
// m1 指向 Main 中的 m,而 m 则指向那个实际的 MyClass 的实例
// 相当于 C++ 中的
// MyClass** m1 = &m;
// (*m1)->Value = 10;
//
m1.Value = 10;
}

public static void TestNoRef(MyClass m1)
{
// 这里是一个普通的传递引用类型的例子。相当于:
// MyClass m1 = m;
// m1.Value = 20;
//
// m1 复制了 m 中的内容,也就是说现在 m1 也和 m 一样,指向了 m 指向的实例
// 所以这里对 m1 的修改也会影响到 Main 中的 m。
//
m1.Value = 20;
}

public static void TestCreateRef(ref MyClass m1)
{
// 这里的 m1 也是一个指向引用的引用:
// m1 指向 Main 中的 m,而 m 则指向那个实际的 MyClass 的实例
// 相当于 C++ 中的
// MyClass** m1 = &m;
// *m1 = new MyClass();
// (*m1)->Value = 100;
// 在上面的 *m1 = new MyClass() 这个调用的时候,实际上只是将 Main 中 m 的值(引用)给修改了,
// 也就是说现在 m1 指向 Main 中的 m,而 m 现在则指向了这个新生成的实例。
// 所以这里 m1.Value = 100 是会影响到 Main 中的结果的
//
m1 = new MyClass();
m1.Value = 100;
}

public static void TestCreateNoRef(MyClass m1)
{
// 在这个方法里面,我们新申明了一个 MyClass 的实例,而让 m1 指向了这个实例
// 这时候,实际上将 m1 的值修改了, m1 和 Main 中的 m 各自指向不同的实例
// 所以对 m1 做的任何修改都不会影响到 m 了
//
m1 = new MyClass();
m1.Value = 200;
}

public static void Main()
{
MyClass m = new MyClass();
m.Value = 1;

TestRef(ref m);
Console.WriteLine(m.Value);

TestNoRef(m);
Console.WriteLine(m.Value);

TestCreateRef(ref m);
Console.WriteLine(m.Value);

TestCreateNoRef(m);
Console.WriteLine(m.Value);
}

 


 


Final Result:


10


20


100


100