-
view对象和Fragment分别作为数据源实现ViewPager(导航栏)功能
2015-06-18 11:19:15//由于文件过多,所以请仔细阅读,有看不懂的,可以留言! //由于是由View和Fragment分别作为ViewPager 数据源,所以需要区分出来看 package com.example.xin.pager; import java.util.ArrayList; import ...//由于文件过多,所以请仔细阅读,有看不懂的,可以留言!
//由于是由View和Fragment分别作为ViewPager 数据源,所以需要区分出来看
package com.example.xin.pager;
import java.util.ArrayList;
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v7.app.ActionBarActivity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity implements OnPageChangeListener{
private List<View> viewlist;
private ViewPager vp;//导航栏,理解为一个容器,可以存放由布局文件转化的View对象,也可以存放Fragment,具体怎么将v布局文件转变为View对象,请看下面注释,以及怎么在Fragment内加载布局文件,请看下面单独的文件。
private PagerTabStrip pts;
private List<String>titleList;
private List<Fragment> fragmentlist;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vp=(ViewPager) findViewById(R.id.pager);
viewlist=new ArrayList<View>();
pts=(PagerTabStrip) findViewById(R.id.tab);
/*
* *1。第一种方法:用view对象作为ViewPager数据源
*/
//将layout转化成View对象
View view1=View.inflate(this, R.layout.view1, null);//这4个文件内容是一样的,都是放着一个TextView代表以后页面中可能出现的内容,为了偷懒,这个布局文件就不上了,因为大家也都会。
View view2=View.inflate(this, R.layout.view2, null);
View view3=View.inflate(this, R.layout.view3, null);
View view4=View.inflate(this, R.layout.view4, null);
//将view添加到viewlist集合中
viewlist.add(view1);
viewlist.add(view2);
viewlist.add(view3);viewlist.add(view4);
//将标题加入到String集合中
titleList=new ArrayList<String>();
titleList.add("第一页");
titleList.add("第二页");
titleList.add("第三页");
titleList.add("第四页");
/*
* *用Fragment作为ViewPager数据源
*/
fragmentlist=new ArrayList<Fragment>();
fragmentlist.add(new Fragment1());
fragmentlist.add(new Fragment2());
fragmentlist.add(new Fragment3());
fragmentlist.add(new Fragment4());
//为PagerTabStrip(标题栏)添加一些属性
pts.setBackgroundColor(Color.WHITE);
pts.setTextColor(Color.BLACK);
pts.setDrawFullUnderline(false);
pts.setTabIndicatorColor(Color.GREEN);
//创建适配器//1.用ViewPagerAdapter适配器
MyViewPagerAdapter mvpa=new MyViewPagerAdapter(viewlist,titleList);
//2:用FramentPagerAdapter作为适配器
//MyFragmentPagerAdapter mfpa=new MyFragmentPagerAdapter(getSupportFragmentManager(), fragmentlist,titleList);FragmentPagerAdapter2 mfpa2=new FragmentPagerAdapter2(getSupportFragmentManager(), fragmentlist,titleList);
//加载适配器
vp.setAdapter(mfpa2);
vp.setOnPageChangeListener(this);//添加监听,可以监听当前页面
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
Toast.makeText(this, "当前是第"+(arg0+1)+"页", 0).show();
}}
//2.以下文件是Fragment加载layout文件的方法
package com.example.xin.pager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class Fragment1 extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.view1, container, false);
}
}//同理,其他的Fragment也是这种方法加载3.几种适配器类的编写
//1.PagerAdapter适配器
package com.example.xin.pager;
import java.util.List;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
public class MyViewPagerAdapter extends PagerAdapter{
private List<View> viewlist;
private List<String> titleList;//构造方法,将View和标题以集合的方式传进来
public MyViewPagerAdapter(List<View> viewlist,List<String> titleList)
{
this.viewlist=viewlist;
this.titleList=titleList;
}
@Override
//返回所有视图的数量
public int getCount() {
// TODO Auto-generated method stub
return viewlist.size();
}
@Override
//判断视图是否由对象产生
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0==arg1;
}
@Override
//实例化页面
public Object instantiateItem(ViewGroup container, int position) {
// TODO Auto-generated method stub
container.addView(viewlist.get(position));
return viewlist.get(position);
}
@Override
//删除页面
public void destroyItem(ViewGroup container, int position, Object object) {
// TODO Auto-generated method stub
container.removeView(viewlist.get(position));
}
@Override//添加标题
public CharSequence getPageTitle(int position) {
// TODO Auto-generated method stub
return titleList.get(position);
}
}//2.创建FragmentPagerAdapter类
package com.example.xin.pager;
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class MyFragmentPagerAdapter extends FragmentPagerAdapter{
private List<Fragment>fragmentlist;
private List<String>titlelist;
public MyFragmentPagerAdapter(FragmentManager fm,List<Fragment> fragmentlist,List<String> titlelist) {
super(fm);
// TODO Auto-generated constructor stub
this.fragmentlist=fragmentlist;
this.titlelist=titlelist;
}
@Override
public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
return fragmentlist.get(arg0);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return fragmentlist.size();
}
@Override
public CharSequence getPageTitle(int position) {
// TODO Auto-generated method stub
return titlelist.get(position);
}
}//创建FragmentStatePagerAdapter类
package com.example.xin.pager;
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.view.View;
import android.view.ViewGroup;
public class FragmentPagerAdapter2 extends FragmentStatePagerAdapter{
private List<Fragment>fragmentlist;
private List<String>titlelist;
public FragmentPagerAdapter2(FragmentManager fm,List<Fragment> fragmentlist,List<String> titlelist) {
super(fm);
// TODO Auto-generated constructor stub
this.fragmentlist=fragmentlist;
this.titlelist=titlelist;
}
@Override
public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
return fragmentlist.get(arg0);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return fragmentlist.size();
}
@Override
public CharSequence getPageTitle(int position) {
// TODO Auto-generated method stub
return titlelist.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// TODO Auto-generated method stub
super.destroyItem(container, position, object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// TODO Auto-generated method stub
return super.instantiateItem(container, position);
}
}//最后是activity_main布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.xin.pager.MainActivity" >
<android.support.v4.view.ViewPager
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/pager"
>
<android.support.v4.view.PagerTabStrip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tab"
android:layout_gravity="top" >
</android.support.v4.view.PagerTabStrip>
</android.support.v4.view.ViewPager>
</RelativeLayout> -
java类在jvm中经历的几个阶段以及对象中的属性赋值和方法的执行顺序
2017-07-31 12:08:56我们介绍下前3个阶段加载加载是jvm加载二进制字节流转换成运行时数据结构的过程连接连接又分为三个小阶段,分别是验证、准备和解析验证 验证解决要的事就是验证当前执行代码是否可以被当前jvm正常执行。比如jvm版本...本文基于个人的一些理解做的整理,如果有什么位置有问题,欢迎留言指教。
jvm加载资源的几个阶段
jvm加载一个类需要经过加载、连接、初始、使用和卸载几个阶段。我们介绍下前3个阶段
加载
加载是jvm加载二进制字节流转换成运行时数据结构的过程
连接
连接又分为三个小阶段,分别是验证、准备和解析
验证
验证解决要的事就是验证当前执行代码是否可以被当前jvm正常执行。比如jvm版本导致的问题就出现在这个阶段
准备
jvm在准备阶段开始一些内存的分配,并且对一些静态资源进行赋值操作,静态变量在这个阶段会赋给默认值,而静态常量在这个阶段会赋给期望值。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
初始化
在初始化阶段jvm会做以下几件事
a) 加载类的实例
b) 加载类的静态变量
c) 加载类的静态方法
d) 实例化一个对象
e) 初始化时先初始化父类
f) 类初始化是类加载过程的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。初始化阶段是执行类构造器()方法的过程。()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的。java类中的方法和属性在jvm中的执行顺序
java属性分为静态属性、和非静态属性。
静态属性的访问是基于类直接访问的。不会依附于某个实例,这也是静态代码块在实例化对象的时候只会执行一次的原因,而非静态属性是依附于当前的java对象实例的。
在jvm执行过程中静态资源是在对象初始化之前就完成了内存的分配,但是不同的是静态常量(final)的是在内存分配的同时完成了赋值操作,而静态变量在分配内存的时候并没有赋值。而在是初始化阶段的类初始化时进行赋值操作的。
在java类中属性的代码的执行顺序是从上往下的。并且是静态代码优先于非静态,另外就是子类实例化之前必须要实例化父类。实例一 package com.example.classes; public class SingleTon { private static SingleTon singleTon = new SingleTon(); public static int count1; public static int count2 = 0; private SingleTon() { count1++; count2++; } public static SingleTon getInstance() { return singleTon; } public static void main(String[] args) { SingleTon singleTon = SingleTon.getInstance(); System.out.println("count1=" + singleTon.count1); System.out.println("count2=" + singleTon.count2); } } count1=1 count2=0
实例二 package com.example.classes; public class SingleTon { public static int count1; public static int count2 = 0; private static SingleTon singleTon = new SingleTon(); private SingleTon() { count1++; count2++; } public static SingleTon getInstance() { return singleTon; } public static void main(String[] args) { SingleTon singleTon = SingleTon.getInstance(); System.out.println("count1=" + singleTon.count1); System.out.println("count2=" + singleTon.count2); } } count1=1 count2=1
我们简单的看下上面的两段代码,曾经我一直对这个问题不是很理解,但是现在我感觉自己的思想还是比较清晰了。
介绍下我的理解
出现这种情况的原因是java代码是顺序执行的,在实例一中是先实例化了一个singleTon实例,那这个时候两个值都是默认值也就是0,然后在进行++操作之后都变成了1,但是顺序执行的过程中,count2又被重新赋值了,这个时候就把原有值覆盖掉了。
但是实例二的执行顺序是先进行赋值操作,然后在实例化singleTon对象,那这个时候已经完成了赋值操作了,后续的++是在赋值以后的基础上做的,这个时候也就看到的结果就很容易接受了。注:想想这种问题很有可能在我们不经意间写错了代码的顺序,然后当代码执行的过程中就会出现一些很意外的现象,这时候我们就会很费劲,现在了解了这个执行顺序以后,就感觉思维清晰了很多了
public class SimpleField { private final BlockingQueue<Runnable> workQueue; public SimpleField(BlockingQueue<Runnable> workQueue) { this.workQueue = workQueue; } public void set(BlockingQueue<Runnable> workQueue) { // this.workQueue = workQueue; } }
记录一段代码。上面的代码块中被注释的代码是编译不通过的,再贴一段代码对比。下面代码编译都不通过
import java.util.concurrent.BlockingQueue; public class SimpleField { private static final BlockingQueue<Runnable> workQueue; public SimpleField(BlockingQueue<Runnable> workQueue) { this.workQueue = workQueue; } public void set(BlockingQueue<Runnable> workQueue) { this.workQueue = workQueue; } }
我们会发现final修饰的静态常量在声明的时候就必须给定初始值。但是final修饰的类变量并不用,不过类变量只支持构造赋值。这正好符号了静态属性是直接挂到类上的,而非静态属性是挂在实例对象上的。
-
python中 和is的区别_说说Python 中 is 和 == 的区别?
2021-02-03 08:43:43小猿会从最基础的面试题开始,每天一题。...答:讲is和==这两种运算符区别之前,首先要知道Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。is 和 == 都可以进行对象比...小猿会从最基础的面试题开始,每天一题。如果参考答案不够好,或者有错误的话,麻烦大家可以在留言区给出自己的意见和讨论,大家是要一起学习的 。
废话不多说,开始今天的题目:
问:说说Python 中 is 和 == 的区别?
答:讲is和==这两种运算符区别之前,首先要知道Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。
is 和 == 都可以进行对象比较判断作用的,但对对象比较判断的内容并不相同。下面来看看具体区别在哪?
is 比较的是两个对象的id值是否相等,也就是比较两个对象是否为同一个实例对象,是否指向同一个内存地址。
== 比较的是两个对象的内容是否相等,默认会调用对象的 __eq__ 方法,== 是python标准操作符中的比较操作符,用来比较判断两个对象的值是否相等。
下面分别来说说几种数据类型is的比较情况:a = 1 #a和b为数值类型b = 1print(a is b) #Trueprint(id(a)) #2037015728print(id(b)) #2037015728a = '程序IT圈' #a和b为字符串类型b = '程序IT圈'print(a is b) #Trueprint(id(a)) #24843328print(id(b)) #24843328a = (1,2,3) #a和b为元组类型b = (1,2,3)print(a is b) #Trueprint(id(a)) #15001280print(id(b)) #15001280a = [1,2,3] #a和b为list类型b = [1,2,3]print(a is b) #Falseprint(id(a)) #15004567print(id(b)) #14796359a = {'name':'小明','age':25} #a和b为dict类型b = {'name':'小红','age':24}print(a is b) #Falseprint(id(a)) #15006478print(id(b)) #14790408a = set([1,2,3])#a和b为set类型b = set([1,2,3])print(a is b) #Falseprint(id(a)) #15009578print(id(b)) #14793567
从上面代码可看出,只有数值型、字符串型和元组的情况下,a is b才为True,如果是list,dict或set型时,a is b为False。
不可变数据(3个):Number(数字)、String(字符串)、Tuple(元组)。
可变数据(3个):List(列表)、Dictionary(字典)、Set(集合)。
如果对于参考答案有不认同的,大家可以在评论区指出和补充,欢迎留言!
更多题目:
关注小猿公众号,每天学习一道题
-
基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)
2011-03-12 10:44:33由于J2EE的开源的框架中提供了MVC模式实现框架Struts、对象关系模型中的Hibernate 的框架及拥有事务管理和依赖注入的Spring。利用现存框架可以更快开发系统。所以选择Java技术作为blog 的开发工具。 为了增加系统的... -
留言点赞获赠书 | vSAN架构细节(5) - vSAN I/O流
2017-10-22 00:00:00原文摘自《vSAN权威指南(第2版)》,经译者徐炯授权。 上几期分别是: 《vSAN架构细节-分布式RAID》 《vSAN架构细节(2) - 对象和组件》 ...欢迎大家转发本篇文章,您的留言获赞数较多的,有机会原文摘自《vSAN权威指南(第2版)》,经译者徐炯授权。
上几期分别是:
另外,文末有注册参加10月26日vForum大会的二维码,无法亲临现场的朋友,也可以参加在线虚拟大会的直播。
欢迎大家转发本篇文章,您的留言获赞数较多的,有机会同时获得《vSAN权威指南(第2版)》和《企业级数据中心现代化技术与实践》的两本或其中一本,机会多多。
---Begin---
5.5 vSAN I/O流
在接下去的几个段落中,我们将跟踪I/O流,看看一台部署到vSAN数据存储上的虚拟机,其客户操作系统中的应用程序进行读写操作时的I/O流是怎样的。我们将观察当条带宽带设置成2的时候的读操作,还将观测当允许的故障数(也即FTT值)为1的时候写操作都又是怎样的。这将有助于理解底层的I/O流,并帮助你了解在设定其他功能值时的I/O流。我们还将讨论回写到容量层的过程,因为这是也去重和压缩起作用的过程。在开始之前,让我们先来看看在I/O路径中闪存的角色。
5.5.1 缓存算法
混合配置和全闪存配置的缓存算法是不同的。概括来说,混合配置关心如何优化从缓存层到容量层的回写(destaging),而全闪存配置关心的是如何确保热数据块(活动数据)保留在缓存层中而冷数据块(不被访问的数据块)保留在容量层中。
5.5.2 缓存层的角色
如前面的章节所述,当在混合配置中用作缓存层时,SSD(在这里此术语等同于闪存设备)对vSAN来说有2个用途:读缓冲和写缓存,这大幅提升了虚拟机的性能。在某些方面,vSAN可以比作是市场上大量的“混合” (Hybrid)存储解决方案,那些混合存储解决方案也使用了把SSD和磁盘组合在一起来增加I/O性能的方法,但是它们基于低成本的SATA或SAS磁盘驱动器,仅具有横向扩展容量的能力。
全闪存配置的vSAN没有读缓冲,缓存层仅用作写缓存。
-
读缓冲的目的
混合配置中读缓冲的目的是维护一个经常被虚拟机访问的磁盘块的列表。当缓冲命中的时候可以减少读I/O的延迟,缓冲命中的意思就是数据块位于缓冲区内,不需要从磁盘取回。虚拟机中应用程序正在读取的真正的数据块可能不在虚拟机运行的同一台主机上,在这种情况下,DOM为特定的读(基于offset)选择一个镜像并把它(读请求)发送给正确的(镜像)组件,然后它被发送给LSOM来判断数据块是否在缓存中。如果发生了缓冲未命中的情况,数据将会直接从磁盘取回,当然,这会引起延迟惩罚,并可能影响到vSAN每秒输入/输出操作的数量(IOPS)。这就是混合配置vSAN中读缓冲的目的,它减少了需要发往磁盘的IOPS数,其目标是读缓冲命中至少达到90%。vSAN还具有读缓冲预读优化功能,它将要读取的数据块附近的1MB数据也同时读进缓冲区内。
vSAN总是试图确保读请求是发往同一个镜像副本的,这样数据块只会在群集中被缓存一次;换句话来解释:它只存在于一个缓存设备的缓冲区内,这个缓存设备位于读请求发往的那个镜像副本所在的那台ESXi主机上。因为缓存空间是相对昂贵的,这个机制可以对vSAN所需的缓存进行优化。正确的设置vSAN缓冲的大小会对稳态下的性能产生非常显著的影响。
-
为什么全闪存配置vSAN中没有读缓冲
在全闪存配置vSAN中,因为容量层也同样是闪存,如果读缓冲没有命中,从容量层取回数据块的代价不会像混合配置要去磁盘中取回那样大。事实上,这是一个非常快的操作(通常小于1毫秒)。因此,对于全闪存配置vSAN来说完全没有必要再加入一个基于闪存的读缓冲,因为容量层就已经能够高效地处理读操作了。缓存层不用作读缓冲,还可以释放出缓存层的空间更多地用于写操作,进一步提高总体性能。
-
写缓存的目的
无论是全闪存还是混合配置vSAN中的写缓存表现为一种回写的缓存。写在进入缓存层中的闪存设备的准备阶段时被确认。事实上,在混合配置中对写操作使用闪存设备可以显著减少写入的延迟,因为写在被确认前不需要直接回写进容量层。
因为写会先进入缓存层闪存设备,我们必须确保这个数据块会有另外一个拷贝存放在vSAN群集的某个地方。vSAN中所有虚拟机都具有一个可用性策略设置来保证虚拟机数据至少另有一个可用的拷贝(除非管理员特别选择FFT=0来覆盖掉默认策略)。这个可用性策略包含写缓存的内容。一旦客户操作系统中的应用程序发起了写操作,这个动作就会同时写入到所有副本中。写会被缓存到VMDK存储对象的组件所在的磁盘组关联的缓存层闪存设备上。
这意味着当一台主机发生故障时,我们还另有一份缓存内的数据,所以不会发生数据丢失。虚拟机只需简单的重新使用这个被复制的缓存副本及其被复制的磁盘数据即可。
注意,全闪存配置vSAN继续利用缓存层作为写缓存,和混合配置一样,所有虚拟机的写都会先进入这个缓存设备。这里主要的算法变化是写缓存是如何被使用的。写缓存现在用来保存“热”数据块(处于变化状态的数据)。只有当数据块变“冷”(不再变更/被写入)的时候,才会被移动到容量层。
5.5.3 剖析混合vSAN中的读操作
对于vSAN数据存储中的对象来说,当使用RAID-1配置且虚拟机存储策略中的允许的故障数的值大于0的时候,是有可能存在多个副本的。此时,读可能跨副本发生。根据磁盘上的逻辑块地址(Logical BlockAddress, LBA)的不同,不同的读请求可能会被发往不同的副本。这保证了vSAN不会消耗不必要的读缓存,而且避免了把同一份数据缓存到多个地方。
举例来说,当虚拟机中的应用程序发起了一个读请求,就会先询问群集服务(CMMDS)来判断谁是数据的属主。属主使用LBA判断应由哪个组件来服务这个请求,并将请求发往那个组件。如果数据块位于读缓冲内,那么读请求就直接由那个读缓冲提供服务。如果读缓冲未能命中,那么说明数据块不在缓冲区内,下一步就会从磁盘读取数据,而对于混合vSAN来说,容量层是由磁盘构成的。
如前所述,对象的属主会把读请求分散到组成这个对象的各个组件上,这样一个给定的数据块就最多只会在1个节点上被缓存起来,这最大限度地有效利用了缓存层。很多情况下,数据存在另一台ESXi主机的存储上,就可能必须通过网络来进行传输。一旦数据被取回,就会反馈给请求者的ESXi主机并服务于应用程序。
图5-9给出的就是vSAN上读操作的相关步骤的示意图。这个特定的例子中,条带宽度设成了2,虚拟机存储对象的条带分布到了不同主机的磁盘上(用vSAN术语来说,每个条带都是一个组件)。注意,stripe-1a和stipe-1b位于同一台主机上,而stripe-2a和stripe-2b位于不同的主机上。在我们的例子中,需要从stripe-2b来读取数据。如果数据属主不拥有虚拟机上的应用程序试图读取的数据块,读请求将通过万兆以太网络来取回数据块。
图5-9 vSAN I/O流:允许的故障数=1 + 条带宽度=2
5.5.4 剖析全闪存vSAN中的读操作
因为全闪存vSAN配置中没有读缓冲,所以和混合配置中的读操作比起来,输入输出流有些细微的不同。在全闪存vSAN中,当读请求发起的时候,会先检查写缓存,看看(被请求的)数据块是否存在其中(也就是检查这是不是一个热数据块)。顺便说一句,混合配置中也有这个步骤。如果要读取的数据块在写缓存中,就会直接从那里取回。如果请求的数据块不在写缓存中,数据块将从容量层取回。记得在全闪存vSAN中,容量层也是由闪存组成的。所以读取延迟开销非常小,这就是为什么我们在全闪存vSAN配置中不实施读缓存而将所有缓存层都用于写缓存的原因。如前所述,通过不实施读缓存,我们可以将缓存层空间更多地用于写操作,从而提升整体的IOPS性能。
5.5.5 剖析混合vSAN中的写操作
现在我们知道了读的工作原理,让我们来看看写操作。当部署一台新的虚拟机的时候,组件是存储在多台主机上的。vSAN并没有数据本地化的意图,因此有可能出现虚拟机在esxi-01上运行(从CPU和内存角度来看)而其虚拟机存储组件实际上是存放在esxi-02和esxi-03上的。如图5-10所示。
图5-10 vSAN I/O流:写确认
当虚拟机中的应用程序发起一个写操作时,对象属主克隆这个写操作。并发的写请求同时通过万兆网络发往esxi-02和esxi-03上的写缓存(SSD闪存设备)。当数据写入缓存时,写就被确认了,此时SSD上的准备操作就完成了。属主等待所有2台主机的ACK信号后完成I/O。稍后这个写入会作为批量处理的一部分最终回写到磁盘上。各主机的回写操作都是相互独立的,也就是说,esxi-02的回写操作的时间可能和esxi-03不同。这无须协调一致因为不同主机的情况不同,例如缓存空间填满的速度、剩余空间的大小以及数据将存放在磁盘的什么地方都可能是不同的。
5.5.6 剖析全闪存vSAN中的写操作
全闪存vSAN中的写操作和混合vSAN中的非常相似。在混合配置中,只有30%的缓存层用于写缓存,其他70%被分配给了读缓冲。因为在全闪存配置中没有读缓冲,所有100%的缓冲层都被分配给了写缓存(在当前版本的vSAN中其上限为600GB)。不过,笔者觉得这个上限值在不久的将来应该会增大。
混合vSAN和全闪存vSAN的缓冲层的角色也有一些细微的差别。如同我们曾经提过的,混合vSAN配置中的写缓存提升了写的性能,因为写操作不需要直接写入由磁盘组成的容量层,从而改进了延迟值。在全闪存vSAN中,写缓存的目的是增加耐久度。全闪存vSAN的设计目标是将高耐久度的闪存设备用作缓存层,让它们来处理大多数的I/O。这使得容量层可以不需要和缓冲层一样处理大量的写操作,从而可以使用较低配置的闪存设备。
即使如此,全闪存配置的写操作仍然和混合配置非常相似,仍然是只有当数据块被写入到所有副本的写缓存中时,写才会被确认。
5.5.7 将写操作回写入磁盘
来自于vSAN中虚拟机的客户操作系统和应用程序的写入的数据随着时间的推移会慢慢在闪存层上累积起来。在混合vSAN配置中(也就是说配置使用闪存设备作为缓冲层而将磁盘作为容量层),vSAN采用电梯算法周期性地将缓存层内写缓存中的数据按照地址顺序“冲刷”进磁盘中。SSD中的写缓存被分割成大量的“贮存区”(Bucket)。数据块在写入的时候以逻辑块地址(LBA)增大的顺序分配给这些贮存区。当回写发生的时候,可能因为资源的限制,最老的贮存区内的数据会被先回写。
vSAN通过启用磁盘写缓存来最大限度地优化性能。不过写缓存中的数据仅仅在数据从SSD中丢弃之前被冲刷进磁盘。如前面所提及的,回写时vSAN会考虑I/O的位置。按照贮存区为单位累积起来的数据给磁盘提供了一个顺序的(物理位置上邻近的)工作负载。换而言之,它靠把逻辑块地址邻近的数据块组合在一起回写入磁盘来提升性能。事实上,邻近原则即使是对于写入全闪存vSAN配置容量层中的闪存设备也能够提升性能。
用于这个操作的探索算法是成熟的,它考虑了很多参数,例如进入I/O的速率、队列、磁盘利用率和最优的批次(Optimalbatching)。这是一个能进行自我调整的算法,它决定了SSD回写到磁盘的频率。
5.5.8 去重与压缩
vSAN 6.2中加入了2种新的数据简化特性:去重(deduplication)和压缩(compression)。当这2个特性在群集层面启用时,vSAN会试图在把数据块回写到容量层之前对每个块进行去重并压缩。这2个特性仅存在于全闪存vSAN中。压缩和去重不能分开启用,要么一起开启要么一起关闭。去重和压缩作用于磁盘组层面上。换句话说,只有部署在同一个磁盘组上的对象才可以利用这些特性来节省空间。如果完全相同的2台虚拟机被部署在了不同的磁盘组上,即使数据块完全相同也不会有去重发生。然而,去重和压缩特性是群集范围的特性,要么开启要么关闭,你无法选择对某一个虚拟机或某一个磁盘组来开启这2个特性。
对于部署在同一个磁盘组上的组件,如果开启了去重和压缩,那么去重将会在4KB数据块的粒度上实现。磁盘组上只会保留那个4KB数据块的一个拷贝而其他所有重复的数据块都会被清除,如图5-11所示。
图5-11 数据块去重
去重的过程在数据块从缓存层回写入容量层的时候发生,并使用哈希表来跟踪已去重的数据块。去重过的数据块和哈希表的元数据散布在组成此磁盘组的容量设备中。
同一磁盘组内不同的组件间的去重并没有什么不同——数据块去重可能发生在虚拟机主页名字空间、虚拟机交换文件、VMDK对象或快照增量对象上。
如果一个磁盘组的容量快被填满,vSAN会检查那些去重过的组件的构成,并将那些会对磁盘组容量产生最大变化的数据块移走。
不过请注意,如果去重和压缩被启用,那么磁盘组中的单个设备故障会使得整个磁盘组显示为不健康状态。
一旦数据块被去重,vSAN会试图将那个4KB的数据块压缩到2KB或更小。如果vSAN可以做到将那个数据块压缩到小于2KB,它将会保留那个压缩过的数据块,否则会保留未压缩过的数据块。
如图5-12所示,整个过程相对简单明了。第一步,虚拟机将数据写入vSAN的缓存层。当数据变“冷”需要被回写的时候,vSAN将这些数据块读入内存(第二步)。它将会计算哈希值,清除掉重复的块,并将剩余的数据块进行压缩,最后写入容量层(第三步)。
图5-12 去重和压缩过程
写给有兴趣的读者:vSAN现在使用SHA1作为去重哈希算法,并使用LZ4算法来压缩。当然这些在将来的版本可能会变。
5.5.9 数据本地化
一个常见的问题是:需要数据本地化么?缓存需要和虚拟机保持在同一台主机上吗?每当vSphere DRS因为计算资源的不平衡而迁移虚拟机的时候,该虚拟机的缓存以及VMDK存储对象需要和虚拟机一起移动吗?对于vSAN 6.2之前版本中标准的vSAN部署来说,答案是否定的,vSAN不存在数据本地化的概念。然而,自vSAN 6.2起,一些变化加入到了架构中:出现了不同的层面的缓存。让我们先列出这些缓存类型,再来解释哪些地方适用于“本地化”而又有哪些是不适用的。
• 基于闪存的写缓存
• 基于闪存的读缓冲(混合模式中)
• 基于内存的读缓冲
对于基于闪存的缓存来说不存在本地化原则。
原因很简单:考虑到读I/O网络传输最多只有一跳(one hop),万兆以太网引起的延迟比起其他延迟(例如闪存延迟甚至是内核延迟)小到可以忽略不计,而把数据迁来迁去带来的好处远远抵不上其耗费的成本。请特别考虑一下这样的事实:默认的vSphere DRS最小间隔是每5分钟就要运行一次,这可能导致虚拟机每5分钟就会迁移一次。考虑到闪存的成本和基于闪存的缓存的大小,将基于闪存的缓存层中的数据移来移去实在是很不经济。所以vSAN不会这么做,而是把注意力集中在对存储资源的负载均衡上,将它们用最高效和最优化的方法分布到群集中去,因为这对vSAN才是更有收益和成本有效的。
话又说回来,vSAN 版本6.2还有一个小小的内存读缓存(in-memory read cache)。小在这里的意思是主机内存容量的0.4%,且上限为每台主机1GB。这个内存读缓存是一个客户端的缓存,意思就是虚拟机的数据块被缓存在虚拟机所在主机的内存中。当虚拟机迁移时,因为客户端缓存不再有效,所以缓存需要被重新“激活” 。注意,尽管如此,大多数情况下热数据已经存在于闪存读取缓存或写缓存层中,因此对性能的影响很低。
如果由于特别的要求必须提供某种形式的数据本地化,我可以告诉你vSAN可以和CBRC(用于VMware View的内存读缓存)集成,因此你可以启用之,而完全不需要更改任何vSAN的配置。注意,CBRC不需要在vSAN数据存储上创建任何特殊的对象或组件,CBRC摘要存储在虚拟机主页名字空间对象中。
【以下是编者Peter Ye的备注】
数据本地化是一个美好的愿景,但是架构设计需要综合考虑付出的代价和取得的收益是否合算? 有些技术实现的数据本地化,是要以数据复制和移动占用不少CPU、内存和网络的开销做为代价的,而这样的本地化只对第二次以后的读有些微(因为网络延迟在目前并不是瓶颈)的提升,对数据的写没有帮助。
因此,我个人以为,只有当网络延迟与其他延迟如闪存延迟相比,高到一定成程度,例如当NVDIMM这一类闪存(例如3D Xpoint的NVDIMM)商用并大规模普及,而且又有越来越多业务应用有迫切的低延迟需求时,这时考虑数据本地化或许有更高的投入产出比。然而,即使闪存延迟迅猛缩短,是的网络延迟的瓶颈开始凸显,此时仍有其他现成的技术,例如RDMA可以极大地降低网络延迟的瓶颈。详见:《InfiniBand支持VMware vSAN吗?》。vSAN在未来将支持RDMA。
【备注结束】
5.5.10 vSAN延伸群集中的数据本地化
关于数据本地化的处理有个特别值得一提的地方,就是在考虑vSAN延伸群集部署的时候。vSAN延伸群集是在vSAN 6.1引入的,它允许vSAN群集中的主机分布在地理上分散的不同站点。在vSAN延伸群集中,数据的一个镜像位于站点1而另一个镜像位于站点2(目前vSAN延伸群集只支持RAID-1)。之前我们提起过vSAN从镜像读取数据时采用轮询策略,这对于vSAN延伸群集来说是不合适的,因为这样的话50%的读就要从远端的站点传输过来。由于VMware在站点间允许的延迟最高可以达到5ms,这会对虚拟机性能产生负面影响。vSAN现在不再继续采用轮询和块偏移算法,而是可以智能地找出延伸群集中虚拟机所在的站点,并更改读算法,使得100%的读都从本地站点的镜像副本中获取,这意味着在稳定运行状态中不会有跨链路的读发生。这还意味着所有的缓存都位于本地站点,或者说甚至是位于使用了内存缓存的本地主机上。由于读不再需要通过跨站点的链路,从而避免了额外的延迟。注意,这不是基于每台主机的读本地化,而是基于站点的真正的本地化。在同一个站点,虚拟机的计算部分可以位于任何ESXi主机上,而其本地数据对象可以位于其他任何ESXi主机上。
---End
微信公众号-乐生活与爱IT 将连载vSAN架构细节的系列文章,本篇是
vSAN架构细节系列之五。上几期分别是:
赠书规则:
欢迎大家转发本篇文章,您的留言获赞数较多的,有机会同时获得《vSAN权威指南(第2版)》和《企业级数据中心现代化技术与实践》的两本或其中一本。至少5个名额,有两位名额可获取《vSAN权威指南(第2版)》和《企业级数据中心现代化技术与实践》两本书。有三位名额可获取一本《vSAN权威指南(第2版)》。留言获赞数是比较重要的参考指标,但不完全按照数量,也会适当结合留言是否有针对文章本身提出有价值的问题,或者感想、观点来综合评估。
关于vForum 2017:
2017年10月26-27日,VMware中国在北京-中国大饭店举办vForum 2017年度用户大会。欢迎扫描如下二维码报名参加,并亲临现场参与软件定义存储与超融合分会场,届时会有VMware存储及高可用性的全球总经理李严冰,以及vSAN中国团队的干货分享。您如果无法亲临现场,也欢迎您注册,参加线上虚拟大会的同步直播。
另外大会还提供了至少100多本《vSAN权威指南》第二版的书籍。
点击左下角 "阅读原文" ,可直接跳转到京东自营店购买《VMware Virtual SAN权威指南(原书第2版)》
-
-
外卖订餐管理系统论文(源代码可留言向我要)
2010-05-09 13:54:10本外卖订餐管理系统是针对高校外卖店进行具体的需求分析,采用OOA(面向对象分析)和采用UML工具辅助开发分析,基于S2SH(Struts2+Spring+Hibernate)架构进行设计和开发。论文主要描述外卖订餐管理系统的开发流程,... -
iOS笔记—内存管理以及内存的分区
2015-11-23 11:08:44MRC(MannulReference Counting):程序员自己负责管理对象生命周期,负责对象的创建和销毁。 ARC(Automatic Reference Counting):iOS5.0以后出现的一种内存管理机制,在编写代码的时候,编译器会在适当的位置... -
基于解耦合的考虑,Aria的下载功能是和状态获取相分离的,状态的获取并不会集成到链式代码中,但是Aria提供了另一种更简单更灵活的方案。 通过注解,你可以很容易获取任务的所有状态。 将对象注册到Aria protected...
-
Remoting基本实现的一些代码样例程序
2004-09-29 15:39:00一些朋友在我的博客上给我留言,想看看我的...我整理了一下,分别分为客户端和服务器端及远程对象。这些代码仅仅是为了说明Remoting的基本功能而已,没有什么具体价值。有兴趣的朋友,可以点击这里下载源代码。... -
PHP基础教程 是一个比较有价值的PHP新手教程!
2010-04-24 18:52:44PHP支持整数、浮点数、字符串、数组和对象。变量类型通常不由程序员决定而由PHP运行过程决定(真是好的解脱!)。但是类型也可以被函数cast或者settype()明确的设定。 数值 数值类型可以是整数或是浮点数。你可以... -
问题解答-AsyncTask问题汇总及解答
2019-07-30 22:18:09本篇博文部分由本人所写,部分由网友总结,如有纰漏...AsyncTask对象在初始化的过程中,分别初始化了Handler,WorkerRunnable,FuturaTask三个对象,这个Handler是用来处理消息的(回调oncancel,onPostExecute,onProg... -
千里马酒店前台管理系统V7使用手册
2011-06-16 14:09:38千里马酒店前台管理系统V7.2采用了面向对象的三层逻辑结构的体系架构,即分为用户界面层、业务逻辑层(或称应用层)和数据处理层。 用户界面层负责人-机交互的操作界面,即接受用户的指令和输入、并将结果显示... -
设计模式-简单工厂模式
2017-04-18 14:02:47这是我刚开始写博客,正确的说也不算是博客,只是记录下我看书的笔记罢了。...举个例子我们要设计一个包含加、减、乘、除的计算器,首先我们就想到了,写一个类里面有四个方法分别是加法、减法、乘法和除法这样 -
【C#】数组与集合(LIst、ArrayList、HashTable、Dictionary)
2016-12-11 20:53:04可C#视频马上就接近尾声...下面就先介绍集合,到目前为止我们学了4中,分别是:列表,数组列表,哈希表和字典。 集合: 集合,表示可以通过遍历每个元素来访问的一组对象(特别是foreach循环访问),别一个集合包 -
C++MFC教程
2013-05-21 13:37:15而各种对象都拥有各种属性,下面分别讲述各种GUI对象和拥有的属性。 字体对象CFont用于输出文字时选用不同风格和大小的字体。可选择的风格包括:是否为斜体,是否为粗体,字体名称,是否有下划线等。颜色和背景色不... -
张家口视窗点歌祝福台
2005-06-08 01:03:29功能: 1)用户自己添加歌曲,添加的信息可以分别在点歌祝福留言薄和播放器下端游走显示。 2)用户添加内容包括:歌曲名称/歌曲地址(url)/赠送对象/你的名字/你的QQ号/歌曲分类/祝福内容,其中的QQ可以在线留言或... -
你是色弱么?
2009-05-09 23:30:00以上六个图为重点讨论对象,你可以留言分别说出6个图是啥(1号图是25,最初级的)我相信并不是所有的读者看到上面6个图案时会一下子分辨出来,当然,你能看到1-2个很正常,但如果你看不清楚你想要看的图,即使你站... -
点击商品之后,需要显示商品详细信息,通过商品的id返回指定商品的Bean对象,转化为JSON格式的数据,返回到前端页面显示,需要显示商品的留言信息,因此需要留言的Bean对象,包括数据留言id、留言的商品id、留言...
-
乘风广告联盟系统 v6.4
2019-06-08 19:55:365.加入了用户列表、广告列表、公告列表和留言列表等可多选批量删除的功能 6.加入对IPV6地址的支持 7.加入了负载均衡时对多台服务器的检测和缓存同步更新功能,单台服务器每天负载至少两千万PV多台服务器流量可累加 8... -
乘风广告联盟系统 v6.4.zip
2019-07-03 18:00:245.加入了用户列表、广告列表、公告列表和留言列表等可多选批量删除的功能 6.加入对IPV6地址的支持 7.加入了负载均衡时对多台服务器的检测和缓存同步更新功能,单台服务器每天负载至少两千万PV多台服务器流量可累加... -
乘风广告联盟系统 v6.0 MsSql版
2016-10-21 13:53:009.系统可以针对不同对象使用,站长和非站长成员,可以设置最大安全点击率,并可以对整个广告扣点或对单个用户扣点 10.完全防止Sql注入,密码都是加salt进行Md5不可逆加密。 11.模板多样,前台、网站主、广告主、管理主... -
JAVA自学之路
2012-09-21 20:39:46对于shell,对于各种协议过于详细的细节,对于喜欢的游戏编程,对于javascript各种华而不实的效果,都暂时和它们分别吧。一切和我们的直接工作目标关联不大的东西,扔在一边或者弄清楚到足够支持下一步的学习就可以... -
入门学习Linux常用必会60个命令实例详解doc/txt
2011-06-09 00:08:45在使用mount这个指令时,至少要先知道下列三种信息:要加载对象的文件系统类型、要加载对象的设备名称及要将设备加载到哪个目录下。 (1)Linux可以识别的文件系统 ◆ Windows 95/98常用的FAT 32文件系统:vfat ;... -
javapms门户网站源码
2014-08-07 17:24:08点击“功能组件”左侧常用功能里的“留言管理”后,右侧会出现网友对网站的留言列表,双击某条留言所在行,可以查看留言详细信息,也可以回复和审核留言。 2.4.3. 论坛分区管理 点击“功能组件”左侧常用功能里的... -
本工程包含了 SpringAOP,死锁,JUC同步锁,读-写同步锁,ThreadLocal使用,JUC线程池和Spring提供的线程池,jdk 1.8 中的日期时间API,数据结构中 图的实现及操作和广度优先遍历/深度优先遍历(其他待完善),生成XML...
-
C++Builder5高级技巧与范例
2010-11-19 09:07:318.3 --- 一个基于ISAPI编程的留言板 1. 将Mydbf.dbf和Mydbf.db数据库文件拷贝到C盘根目录。 2. 将Images目录下的文件拷贝到Inetpub \images\目录下。 3. 将Write.dll和Display.dll文件拷贝到Inetpub \scripts\目录下... -
parse 方法接收一个参数即 html,代理网址的 html,在 parse 方法里只需要写好 html 的解析,解析出 host 和 port,并构建 Proxy 对象 yield 返回即可。 网页的爬取不需要实现,BaseCrawler 已经有了默认实现,如需...
-
spring+hibernate实现持久化
2009-01-02 22:17:50//论坛的留言 public class Bbs { private int bbsId; private String bbsTitle; private String bbsContent; private Board board;//论坛版块 private User author; ......省略get,set方法 } bbs.... -
十天学会ASP.net--我认为ASP.NET比ASP难很多,希望大家做好准备
2008-12-05 08:41:03在这里我要说明两点:1、我的示例文件总是有A和B分别是用C#和VB写的,演示的图片就用C#那一种的,都一样嘛,教程里面代码也是写两种用分割开,大家可以比较一下。2、我写教程的时候用的都是记事本来编写APS.NET大家...
-
MySQL 数据类型和运算符
-
傍轴光束传输的动力学分析
-
1068 Find More Coins (30 分)
-
vue同时渲染多个echarts图表
-
Isight培训初级教程.zip
-
Oracle_11g_Linux到Linux_DataGuard部署
-
libFuzzer视频教程
-
2021年 系统架构设计师 系列课
-
Windows系统管理
-
【布道者】Linux极速入门
-
MySQL 事务和锁
-
MySQL 数据库权限管理(用户高级管理和精确访问控制)
-
关于行扫描仪的目标分辨能力
-
MySQL 高可用工具 heartbeat 实战部署详解
-
MySQL 数据库的基本操作(数据完整性约束)
-
html_presentation-源码
-
2008-2009学年第一学期高等数学AI期末试题及答案.pdf
-
拜登的SEC主席提名人说比特币和密码是变革性技术
-
山区航道AIS信号场强分布特性
-
使用vue搭建微信H5公众号项目