最近我们工作室群里说百度要招聘了,也有师兄建议我去华为。

我那个去,各位师兄啊,你们太看得起我了,小弟事实上是没有那个水平的.........

不是自卑,是确实可能积累不够,看了招聘,上述两个公司根本不招.net,而我那个java功底确实不足以打动面试官哟。

于是昨天看数据库时候突然长长的叹息了一番。我们老大叶老师便问,怎么,遇到问题了,我便说。实力不够,进不了大公司哟。

他便说那就进小公司嘛,当项目组长哦,想干什么就干什么。呵呵当时又聊了下天,挺开心的。

 看C#时候,也看了下think in java 才发现,最开始我把java一书想得简单了。这本书在思想上的熏陶其实效果不错。

但是我总觉得那个翻译有点绕口,读起来不大舒服。看起C#一书效率高多了。

其实回想下,CLR via C# 我都看了3次了,肯定要比think in java 效率高嘛,况且主方向又是.net,没有可比性。

生活上没什么可说的,仍然有不少事情,能拖的尽量拖,确实不能拖的,我就直接要么去图书馆要么去教室看书,反正就是不做事......

看得出来,我们老大其实还是有点为难的,一方面他觉得我应该准备找工作;但是,另一方面,我看得出来他还是想我做点事。

但是,我现在的决定搞不好影响今后的发展,也只好拖下了,等这段时间过完在补上吧。人生总是会有一点遗憾的,我真想一体三化........

好了,现在稍微总结下这三天学的东西吧,学的不多,总结肯定也有很多错误,大家看着了帮我改下吧,顺便求数据库面试方面的书籍......

 

构造器:运行类型实例为有效状态的特殊方法(.ctor),使用对象必须实例化

1 构造方法为实例数据字段分配内存

2 初始化对象的系统开销字段(类型对象指针,同步索引块)

3 设置对象初始状态。

4 在构造器调用前,对象始终清0(0或者null)

5 极少数情况下,可以在不调用构造器情况下创建一个类型实例对象(比如:object的Memberwiseclone,便可以分配内存,实例化对象;反序列化对象通常也不需要构造器)。

 

类构造器(静态构造器,.ccotr)

1 类构造器用于设置类的初始状态,默认没有类构造器,一个类只能有一个类构造器。

2 类构器直接由CLR调用

3 调用流程

① JIT编译器在编译一个方法时会查看代码引用了哪些类。

② 若有类定义了类构造器,JIT会检查当前appdomain是否执行类型构造器,未执行便会在本地代码中添加对类构造器的一个调用。

③ 在CLR中由于多个线程可能同时执行相同方法,而类构造器又只能执行一次,所以在调用时候会执行一个互斥线程同步锁。

④ Java会调用父类的静态方法构造器,CLR不支持,但clr提供了一个接口,可以调用父类类构造器

⑤ 类构造器在创建第一个实例前,或者刚好访问类的一个非继承字段或成员之前调用(1情况)

⑥ 编译器在首次访问一个静态字段或者静态/实例方法前,或者在调用实例构造器之前随便一个时间调用  (2情况)

⑦ 显示定义类构造器可提高效率......

 

属性 

1 .net中的属性其实是一种get、set的方法

2 属性分有参属性,无参属性(索引器)

3 属性其实没有什么说头,我在java开发中没有发现属性,最近才知道属性除了优点以外,也有一定缺点,只不过大家都用习惯了,在项目中想改会被认为是个怪胎。

我说实话,我第一次看见java中的get、set便知道这是什么,但是当年第一次看见属性真的不知道是什么......

① 属性方法可能花费长时间执行,字段访问总是立即完成。许多人使用属性的一个常见原因是因为执行线程同步,它可能造成线程的永远终止。

② 如果连续多次调用,属性方法每次都可能返回一个不同的值;而字段每次调用都返回相同的值。System.Datatime 类有一个只读属性为Now,他返回当前日期和时间。

每次查询这个值,它都返回不同的值,这是个错误。(究竟哪里错了,我还没搞明白)

③ 属性方法可能需要额外的内存,或者返回一个不正确的引用。

所以属性不知不能提升代码性能,还会有一些不利因素。但是它能阻止我们使用属性吗?????

 

泛型

1 泛型的算法实现:CLR实现了专门的IL指令支持泛型操作,具体过程为:

① 初次编译时,首先生成IL代码和元数据,T只作为类型占位符,不进行泛型类型实例化。

② 在进行JIT编译时,将以实例类型替换IL代码以及元数据中T占位符,并将其转化为本地代码,下一次对该泛型类型的引用将使用相同的本地代码以提高效率。

③ 对于值类型和引用类型,泛型类型实例化有所不同。类型参数为值类型时,JIT编译器为不同的值类型创建不同的本地代码;类型为引用类型时,则共享本地代码的单个副本,这源于引用类型变量都是指向托管堆的引用指针,而对指针可以使用相同方式操作。这是一种优化手段以缓解代码爆炸的压力。

④ 泛型方法会优先选择更明确的匹配

⑤ 泛型依然是一个特殊的类。

2 泛型的优点:

① 源代码保护,使用一个泛型算法的开发人员不需要访问算法源代码。???

② 类型安全,不同类型会导致编译时错误。(并且支持协变(返回值为基类),逆变(参数为子类))

③ 代码清晰

④ 性能提升,减少装箱拆箱操作(装箱会导致托管堆上对象创建内存分配,会造成频繁的垃圾回收),另外由于不需要转型所以CLR不会进行检查转型类型安全。

3 缺点

① JIT编译过程的代码爆炸,代码爆炸:(见上面原理)使用泛型会造成应用程序工作集显著增大,有一定性能影响。

4 应用

泛型可用于大多数对象(枚举不能),比如类,方法。由于接口,委托皆是特殊的类,所以都适用于委托

5 约束

 

多态:用于消除类型之间的耦合关系(虚方法,override,抽象类,抽象方法)

1 运行一个基类的引用指向其所有派生类,当通过基类引用派生类实现的方法时,不同的派生类将实现不同的方法。

2 运行机制:.net的动态绑定成就了面向对象的多态特性。

3 动态绑定又称晚期绑定,区别于静态绑定。静态绑定是在编译期就可以确定关联,一般以方法重载实现。动态绑定是在运行期通过检查虚拟方法表,来确定动态关联复写的方法,

一般以继承和虚方法来实现,基类(virtual方法),派生类(override方法),达到方法重新实现。(晕,这个叫撒子原理......我还是没有搞懂原理)

 

接口,特殊的类(修饰为abstract),在接口中定义的方法为abstract virtual 方法,实现接口中的方法在中间语言中被定义为override 。

内部当然是采用callvirt调用接口方法,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace 多态
{
    public class DrawObject
    {
        public virtual void  Draw()
        {
            Console.WriteLine("我是画");
        }
    }

    public class DrawCircle:DrawObject
    {

        public override void Draw()
        {
            Console.WriteLine("我是画圆");
        }
    }

    public class DrawSquare : DrawObject,wl
    {

        public override void Draw()
        {
            Console.WriteLine("我是画正方形");
        }

        
        public void test()
        {
            Console.WriteLine("实现接口");
        }

       
    }

    public interface wl
    {
          void test(); 
    }

    class Program
    {
        static void Main(string[] args)
        {
            DrawSquare o = new DrawSquare();
            o.test();
            Console.ReadLine();
        }
    }
}
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       21 (0x15)
  .maxstack  1
  .locals init ([0] class '多态'.DrawSquare o)
  IL_0000:  nop
  IL_0001:  newobj     instance void '多态'.DrawSquare::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  callvirt   instance void '多态'.DrawSquare::test()
  IL_000d:  nop
  IL_000e:  call       string [mscorlib]System.Console::ReadLine()
  IL_0013:  pop
  IL_0014:  ret
} // end of method Program::Main

 

String(ldstr,密封类sealed修饰,不可继承,不可变)

1 string为基元数据,引用类型。其实C# 中应该是没有string类型的,我在java中就没有看到。我们能使用string,是因为在文件中有using string=System.String 的缘故。

2 string代表一不可变的顺序字符集,派生自object,c#不能使用new 构造实例,只能使用ldstr指令。

3 字符串留用(不大用,不讨论)

4 字符串池,编译源代码时,编译器必须处理每个文本常量字符串,并在托管模块的元数据中嵌入字符串。如果同一字符串在源代码中多次出现,便会大量生成最终文件。

由此引出字符串池。

5 由于字符串不可变,每次操作字符串,可能都会导致堆上的对象产生,导致效率低下,所以涉及过多操作一般使用StringBuilder对象。

6

 

委托与事件!!!重点啊

1 委托是.net框架提供的一种函数回调机制,也是一特殊的类(好像对应于java 的动态代理

class ClassName : System.MuliticastDelegate(继承自Delegate)

{
构造函数(.ctor)

virtual returnType Invoke(.....);

//异步调用

virtual returnType IAsynResult(.....);

virtual returnType EndInvoke(.....)


}

2 MuliticastDelegate

① _targate object类型,委托包装静态方法时为null,实例方法便于this值对应

② _methodptr intptr 类型,一个内部整型值,clr用它标识要返回调用的方法。

③ _invocationList object类型,用于链式委托构造委托链。

委托实际便是一个包装器,包装了一个方法和一个调用该方法的对象。在内部IL中,其实是构造的相关对象实例,然后在调用其方法。

 

事件

1 事件是一特殊的委托,实际上是委托的封装形式。通常格式为:

public event eventName(object sender,EventArgs e);

 

代码例子:下课铃响了,老师笑了,学生醒了。整个场景动作发源在下课铃响了。

下课铃想后老师开始笑,学生开始醒,老师和 学生没有关系,

等下代码补上来

 

 

数据库存储形式:数据库有两种存储形式

①  随机存储形式,按照输入顺序,数据被存储在磁盘的任意空位置上;适合快速插入。后期搜寻很痛苦。

② 固定存储结构,数据被存储在预先指定的磁盘上,方便快速搜索,插入慢。

 

数据库性能调整

① 识别性能瓶颈,寻找可能的原因,并锁定原因(原因一般分为硬件瓶颈数据库技术,寻求原因对于开发人员比较痛苦)。

② 解决性能瓶颈,寻求解决方案,对比后测试之。

 

数据库性能低下的几大因素:(我只说下我能理解的)

1 低质量的索引。低质量的索引导致sql server在执行查询时读取和处理多得多的数据,这将导致磁盘、内存和cpu的压力增大;查询时间增加后也可能导致额外的阻塞以及死锁。

由于数据库存储在数据块上,分散的数据将导致物理I/O数增加,而索引可以有效缓解此问题。

2 不确定的统计??

3 过多的阻塞和死锁,sql server 兼容于 原子性,一致性,隔离性,持续性,以确保并发事务被正确的相互隔离,所以会导致阻塞 ,严重后便形成死锁。

4 不基于数据集的操作,游标影响性能

① 游标:用途就是从查询结构中遍历数据,一行一行的操作。http://www.cnblogs.com/sujingran/articles/2091076.html

5 低质量的sql查询,不合理的查询可能导致索引失效。

6 低质量的数据库设计

7 过多碎片???

8 不可重用的执行计划

9 低质量的执行计划

10 频繁的重编译计划

11 游标的错误使用

12 错误配置数据库日志

13 过多使用tempdb,或错误配置tempdb,tempdb作为一个公共资源(临时表,表变量,游标,皆使用tempdb),所以空间,I/O争用也是一问题。

 

数据库性能低下的硬件问题

1  瓶颈

① 比较常见的性能问题一般来自内存或磁盘I/O问题。

② 过多的页面调度(内存瓶颈),慢速的磁盘(磁盘瓶颈),可能导致cpu处理器瓶颈

③ 增加系统吞吐量(硬件升级);降低请求到达资源的到达率(降低I/O数量,可以使用索引,表分区)

④ 消除过多的编译/重编译(cpu)

⑤ 使用更大的二级、三级缓存(cpu)

2 内存瓶颈

① 硬件升级,为sql server 分配更多内存。

② 增加系统内存;更换32位处理器为64位(直接将提升sql内存限制);启用3g进程空间,留给系统1g

3 磁盘瓶颈

① 创建多个文件和文件组(不知道如何操作)

② 将表和索引放到不同的磁盘上

③ 将日志文件保存在独立的物理磁盘,以并发操作

④ 表分区

 

 索引:关于索引的文章非常多,也很多非常经典,我就是简单说说便是了。而且很多我也 没有经过试验只能纸上谈兵了。。。。。。

 1 减少磁盘I/O和逻辑读次数的最佳方式之一便是索引;索引可有效减少返回行

 2 索引的点点滴滴。

① 索引允许sql server在表里查找数据而不需要进行表扫描。

② 没有聚簇索引的表便称为堆表;堆只是一堆未经加工的数据,以行标识符作为指向存储位置的指针。

③  非聚簇索引有一个好处,它独立于数据表结构,所以任何被放置在不同文件组以使用不同的I/O。

④ 索引采用B-Tree结构存储信息

⑤  索引带来查询速度的同时也有一定代价:有索引的表需要更多的内存、空间以容纳表的数据页之多的索引页面。数据更新会花费更多的时间以维护不断变化的数据。

 

如何使用索引

1 使用where语句避免表扫描。

2 使用窄索引(少使用列组合)

3 检查列的唯一性,在很小范围变化的数据(比如男,女)不适合建立索引

4 列数据类型为整型的列上建立索引效率会高点,(int类型尺寸较小)

 

聚簇索引与非聚簇索引(这位兄弟写的非常不错http://www.cnblogs.com/kissknife/archive/2009/03/30/1425534.html,以下有抄袭)

因为索引非常重要,我理解不够深入便完全抄袭算了....SnowToday的作品,作者有意见我就删了。
一、引言

对数据库索引的关注从未淡出我的们的讨论,那么数据库索引是什么样的?聚集索引与非聚集索引有什么不同?希望本文对各位同仁有一定的帮助。有不少存疑的地方,诚心希望各位不吝赐教指正,共同进步。[最近首页之争沸沸扬扬,也不知道这个放在这合适么,苦劳?功劳?……]

 

二、B-Tree

我们常见的数据库系统,其索引使用的数据结构多是B-Tree或者B+Tree。例如,MsSql使用的是B+TreeOracleSysbase使用的是B-Tree。所以在最开始,简单地介绍一下B-Tree

B-Tree不同于Binary Tree(二叉树,最多有两个子树),一棵M阶的B-Tree满足以下条件:
1)每个结点至多有M个孩子;
2)除根结点和叶结点外,其它每个结点至少有M/2个孩子;
3)根结点至少有两个孩子(除非该树仅包含一个结点);
4)所有叶结点在同一层,叶结点不包含任何关键字信息;
5)有K个关键字的非叶结点恰好包含K+1个孩子;

另外,对于一个结点,其内部的关键字是从小到大排序的。以下是B-TreeM=4)的样例:

  

对于每个结点,主要包含一个关键字数组Key[],一个指针数组(指向儿子)Son[]。在B-Tree内,查找的流程是:使用顺序查找(数组长度较短时)或折半查找方法查找Key[]数组,若找到关键字K,则返回该结点的地址及KKey[]中的位置;否则,可确定K在某个Key[i]Key[i+1]之间,则从Son[i]所指的子结点继续查找,直到在某结点中查找成功;或直至找到叶结点且叶结点中的查找仍不成功时,查找过程失败。

接着,我们使用以下图片演示如何生成B-TreeM=4,依次插入1~6):
从图可见,当我们插入关键字4时,由于原结点已经满了,故进行分裂,基本按一半的原则进行分裂,然后取出中间的关键字2,升级(这里是成为根结点)。其它的依类推,就是这样一个大概的过程。

  

 

三、数据库索引

1.什么是索引

在数据库中,索引的含义与日常意义上的“索引”一词并无多大区别(想想小时候查字典),它是用于提高数据库表数据访问速度的数据库对象。
A)索引可以避免全表扫描。多数查询可以仅扫描少量索引页及数据页,而不是遍历所有数据页。
B对于非聚集索引,有些查询甚至可以不访问数据页。
C聚集索引可以避免数据插入操作集中于表的最后一个数据页。
D一些情况下,索引还可用于避免排序操作。

当然,众所周知,虽然索引可以提高查询速度,但是它们也会导致数据库系统更新数据的性能下降,因为大部分数据更新需要同时更新索引。

 

2.索引的存储

一条索引记录中包含的基本信息包括:键值(即你定义索引时指定的所有字段的值)+逻辑指针(指向数据页或者另一索引页)。

  

当你为一张空表创建索引时,数据库系统将为你分配一个索引页,该索引页在你插入数据前一直是空的。此页此时既是根结点,也是叶结点。每当你往表中插入一行数据,数据库系统即向此根结点中插入一行索引记录。当根结点满时,数据库系统大抵按以下步骤进行分裂:
A)创建两个儿子结点
B)将原根结点中的数据近似地拆成两半,分别写入新的两个儿子结点
C)根结点中加上指向两个儿子结点的指针

通常状况下,由于索引记录仅包含索引字段值(以及4-9字节的指针),索引实体比真实的数据行要小许多,索引页相较数据页来说要密集许多。一个索引页可以存储数量更多的索引记录,这意味着在索引中查找时在I/O上占很大的优势,理解这一点有助于从本质上了解使用索引的优势。

 

3.索引的类型

A聚集索引,表数据按照索引的顺序来存储的。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。
B非聚集索引,表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针,该层紧邻数据页,其行数量与数据表行数据量一致。

在一张表上只能创建一个聚集索引,因为真实数据的物理顺序只可能是一种。如果一张表没有聚集索引,那么它被称为堆集Heap)。这样的表中的数据行没有特定的顺序,所有的新行将被添加的表的末尾位置。

 

4.聚集索引

在聚集索引中,叶结点也即数据结点,所有数据行的存储顺序与索引的存储顺序一致。

  

1)聚集索引与查询操作

如上图,我们在名字字段上建立聚集索引,当需要在根据此字段查找特定的记录时,数据库系统会根据特定的系统表查找的此索引的根,然后根据指针查找下一个,直到找到。例如我们要查询“Green”,由于它介于[Bennet,Karsen],据此我们找到了索引页1007,在该页中“Green”介于[Greane, Hunter],据此我们找到叶结点1133(也即数据结点),并最终在此页中找以了目标数据行。

此次查询的IO包括3个索引页的查询(其中最后一次实际上是在数据页中查询)。这里的查找可能是从磁盘读取(Physical Read)或是从缓存中读取(Logical Read),如果此表访问频率较高,那么索引树中较高层的索引很可能在缓存中被找到。所以真正的IO可能小于上面的情况。

 

2)聚集索引与插入操作

最简单的情况下,插入操作根据索引找到对应的数据页,然后通过挪动已有的记录为新数据腾出空间,最后插入数据。

如果数据页已满,则需要拆分数据页(页拆分是一种耗费资源的操作,一般数据库系统中会有相应的机制要尽量减少页拆分的次数,通常是通过为每页预留空间来实现):
A在该使用的数据段(extent)上分配新的数据页,如果数据段已满,则需要分配新段。
B调整索引指针,这需要将相应的索引页读入内存并加锁。
C大约有一半的数据行被归入新的数据页中。
D
如果表还有非聚集索引,则需要更新这些索引指向新的数据页。

特殊情况:
A如果新插入的一条记录包含很大的数据,可能会分配两个新数据页,其中之一用来存储新记录,另一存储从原页中拆分出来的数据。
B通常数据库系统中会将重复的数据记录存储于相同的页中。
C类似于自增列为聚集索引的,数据库系统可能并不拆分数据页,页只是简单的新添数据页。

 

3)聚集索引与删除操作

删除行将导致其下方的数据行向上移动以填充删除记录造成的空白。

如果删除的行是该数据页中的最后一行,那么该数据页将被回收,相应的索引页中的记录将被删除。如果回收的数据页位于跟该表的其它数据页相同的段上,那么它可能在随后的时间内被利用。如果该数据页是该段的唯一一个数据页,则该段也被回收。

对于数据的删除操作,可能导致索引页中仅有一条记录,这时,该记录可能会被移至邻近的索引页中,原索引页将被回收,即所谓的“索引合并”。

 

5.非聚集索引

非聚集索引与聚集索引相比:
A叶子结点并非数据结点
B叶子结点为每一真正的数据行存储一个-指针
C叶子结点中还存储了一个指针偏移量,根据页指针及指针偏移量可以定位到具体的数据行。
D
类似的,在除叶结点外的其它索引结点,存储的也是类似的内容,只不过它是指向下一级的索引页的。

聚集索引是一种稀疏索引,数据页上一级的索引页存储的是页指针,而不是行指针。而对于非聚集索引,则是密集索引,在数据页的上一级索引页它为每一个数据行存储一条索引记录。

对于根与中间级的索引记录,它的结构包括:
A索引字段值
BRowId(即对应数据页的页指针+指针偏移量)。在高层的索引页中包含RowId是为了当索引允许重复值时,当更改数据时精确定位数据行。
C下一级索引页的指针

对于叶子层的索引对象,它的结构包括:
A
索引字段值
BRowId

  

1)非聚集索引与查询操作

针对上图,如果我们同样查找“Green”,那么一次查询操作将包含以下IO3个索引页的读取+1个数据页的读取。同样,由于缓存的关系,真实的IO实际可能要小于上面列出的。

 

2)非聚集索引与插入操作

如果一张表包含一个非聚集索引但没有聚集索引,则新的数据将被插入到最末一个数据页中,然后非聚集索引将被更新。如果也包含聚集索引,该聚集索引将被用于查找新行将要处于什么位置,随后,聚集索引、以及非聚集索引将被更新。

 

3)非聚集索引与删除操作

如果在删除命令的Where子句中包含的列上,建有非聚集索引,那么该非聚集索引将被用于查找数据行的位置,数据删除之后,位于索引叶子上的对应记录也将被删除。如果该表上有其它非聚集索引,则它们叶子结点上的相应数据也要删除。

如果删除的数据是该数所页中的唯一一条,则该页也被回收,同时需要更新各个索引树上的指针。

由于没有自动的合并功能,如果应用程序中有频繁的随机删除操作,最后可能导致表包含多个数据页,但每个页中只有少量数据。

 

6.索引覆盖

索引覆盖是这样一种索引策略:当某一查询中包含的所需字段皆包含于一个索引中,此时索引将大大提高查询性能。

包含多个字段的索引,称为复合索引。索引最多可以包含31个字段,索引记录最大长度为600B。如果你在若干个字段上创建了一个复合的非聚集索引,且你的查询中所需Select字段及Where,Order By,Group By,Having子句中所涉及的字段都包含在索引中,则只搜索索引页即可满足查询,而不需要访问数据页。由于非聚集索引的叶结点包含所有数据行中的索引列值,使用这些结点即可返回真正的数据,这种情况称之为索引覆盖

在索引覆盖的情况下,包含两种索引扫描:
A)匹配索引扫描
B)非匹配索引扫描

1)匹配索引扫描

此类索引扫描可以让我们省去访问数据页的步骤,当查询仅返回一行数据时,性能提高是有限的,但在范围查询的情况下,性能提高将随结果集数量的增长而增长。

针对此类扫描,索引必须包含查询中涉及的的所有字段,另外,还需要满足:Where子句中包含索引中的引导列Leading Column),例如一个复合索引包含A,B,C,D四列,则A引导列。如果Where子句中所包含列是BCD或者BD等情况,则只能使用非匹配索引扫描。

2)非配置索引扫描

正如上述,如果Where子句中不包含索引的导引列,那么将使用非配置索引扫描。这最终导致扫描索引树上的所有叶子结点,当然,它的性能通常仍强于扫描所有的数据页。

作者: 叶小钗

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"