ASP.NET 常用面试题(持续更新)
Sql随机取不固定的10-20的数据
- mysql
select * from table limit 10, 20
- sqlserver
SELECT TOP
11 *
FROM
MyTable
WHERE
ID NOT IN ( SELECT TOP 9 ID FROM MyTable ORDER BY ID ASC )
ORDER BY
ID ASC;
- Oracle
select * from (select t.*,rownum num from people t where rownum <= 20) where num >=10;
Webservice与Webapi的异同
- Webservice
- 它是基于SOAP协议的,数据格式是XML (SOAP )
- 只支持HTTP协议
- 不是开源的,但可以被任意一个了解XML的人使用
- 它只能部署在IIS上
- Webapi
- Web API 是一个开源的、理想的、构建REST-ful 服务的技术
- 它也支持MVC的特征,像路由、控制器、action、filter等
- 它可以部署在应用程序和IIS上
- Response可以被Web API的MediaTypeFormatter转换成Json、XML 或者任何你想转换的格式。
Ef或者Orm与原生ado的区别
- 封装与原生的区别
- 性能上肯定不如原生的,毕竟有一个sql生成的操作
如何解决高并发的问题
并发是什么
- 同时做多件事情。
- 终端用户程序利用并发功能,在输入数据库的同时响应 用户输入。服务器应用利用并发,在处理第一个请求的同时响应第二个请求。只要你希望 程序同时做多件事情,你就需要并发。几乎每个软件程序都会受益于并发。
如何解决高并发
- 单机(垂直)扩展
- 升级单机硬件,但单机扩展始终会有性能瓶颈
- 水平扩展
- 只要增加服务器数量,就能扩充系统性能。
- 反向代理层
- 如用nginx成为性能瓶颈的时候,增加nginx服务的部署,结合keepalived,就能扩展反向代理层的性能
- 站点层
- 通过nginx.conf设置多个web后端,理论上来说,这样可以无限高并发
- 服务层
- 暂时还未了解(RPC)
- 数据层
- 在数据量很大的情况下,数据层(缓存,数据库)涉及数据的水平扩展,将原本存储在一台服务器上的数据(缓存,数据库)水平拆分到不同服务器上去,以达到扩充系统性能的目的。
- 互联网数据层常见的水平拆分方式有这么几种,以数据库为例:
- 按照范围水平拆分
- user0库,存储uid范围1-1kw
- user1库,存储uid范围1kw-2kw
- 这个方案的好处是
- 规则简单,service只需判断一下uid范围就能路由到对应的存储服务
- 数据均衡性较好;
- 比较容易扩展,可以随时加一个uid[2kw,3kw]的数据服务;
- 不足是:
- 请求的负载不一定均衡,一般来说,新注册的用户会比老用户更活跃,大range的服务请求压力会更大;
- 按照hash水平拆分,比如
- user0库,存储偶数uid数据
- user1库,存储奇数uid数据
- 好处是:
- 规则简单,service只需对uid进行hash能路由到对应的存储服务;
- 数据均衡性较好;
- 请求均匀性较好;
- 不足是:
- 不容易扩展,扩展一个数据服务,hash方法改变时候,可能需要进行数据迁移;
- 按照范围水平拆分
数据库优化
- 给where、order by 经常用到的字段建立建立索引
- 避免全文扫描
- 避免使用like,如果非得使用,避免使用%…%,不然这样索引是无法使用的
- 利用reverse + function index
- 全模糊的话考虑使用搜索引擎
- is null 和 is not null 尽量不要使用
- 避免索引字段上有可以为null
- 不等于操作符(<>、!=)尽量不要使用,不会用到索引
- 使用 a> ‘’ && a< ‘’ 代替
- 使用or的是时候注意,如何其中一个字段没有建立索引,那么索引也不会生效
- 多表查询时候先分页在join
- 避免在where 子句中使用or 来连接条件
- select id from t where num=10 or num=20
- 可以这样查询:select id from t where num=10 union all select id from t where num=20
- in 和not in 也要慎用,否则会导致全表扫描
- 在where中尽量不要对字段进行表达式或者函数操作
- select id from t where num/2=100
- 应改为: select id from t where num=100*2
- mySql 可以通过explain查看执行计划进行调优
值类型和引用类型相关问题
值类型
- 值类型在栈内存;
- 值类型一定是sealed;
- 因为值类型是放在栈内存中的,栈内存会方法结束后自动释放;
引用类型
- 引用类型在堆内存;
- 因为引用类型是放在堆内存中的,堆内存则需要GC来释放;
值类型>引用类型 装箱过程
- 装箱的时候会在托管堆中自动创建一个对象实例
- 然后将该值复制到新对象内
引用类型>值类型 拆箱过程
- 检查对象实例,确保它是给定值类型的一个装箱值后
- 再将该值从实例复制到值类型变量中
- 注意:int 装箱后拆箱也必须是int
深拷贝
深拷贝与浅拷贝不同的是对于引用的处理,深拷贝将会在新对象中创建一个新的和原始对象中对应字段相同(内容相同)的字段,也就是说这个引用和原始对象的引用是不同的,我们在改变新对象中的这个字段的时候是不会影响到原始对象中对应字段的内容。
浅拷贝
浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。如果改变目标对象中引用型字段的值他将反映在原始对象中,也就是说原始对象中对应的字段也会发生变化。
js异步调用问题
已知:
条件1 数据表中有若干数据;
条件2 现在有一个接口,每次调用只能请求100条.
问:
js封装一个方法,得到数据表中所有的数据
答:
js闭包
- 闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数已经中执行完毕
js变量提升
-
变量提升
- 通过var 声明的变量,在定义语句之前就可以访问到,值:undefined
console.log(a) var a= 123;
const、static readonly的区别
- const的值是在编译期间确定的,因此只能在声明时通过常量表达式指定其值
- static readonly是在运行时计算出其值的, 只有两种途径为其赋值,在声明该变量的时候或在默认的静态构造函数里面为其赋值。实际上这两种方法最后生成的IL代码是相同的(都是在静态构造函数中赋值)。
1. static readonly MyClass myins = new MyClass();
2. static readonly MyClass myins = null;
3. static readonly A = B * 20;
static readonly B = 10;
4. static readonly int [] constIntArray = new int[] {1, 2, 3};
5. void SomeFunction()
{
const int a = 10;
}
1:不可以换成const。new操作符是需要执行构造函数的,所以无法在编译期间确定
2:可以换成const。我们也看到,Reference类型的常量(除了String)只能是Null。
3:可以换成const。我们可以在编译期间很明确的说,A等于200。
4:不可以换成const。道理和1是一样的,虽然看起来1,2,3的数组的确就是一个常量。
5:不可以换成readonly,readonly只能用来修饰类的field,不能修饰局部变量,也不能修饰property等其他类成员。
-
总结
const是编译时常量,readonly是运行时常量;在应用上以static readonly代替const,以平衡const在灵活性上的不足,同时克服编译器优化cosnt性能,所带来的程序集引用不一致问题;
C# 中常用的数据结构
-
数组(Array):
1、数组存储在连续的内存上
2、数组的元素类型必须相同
3、数组可以直接通过下标访问
4、查找与修改元素的速度非常快
5、必须在声明时指定长度
-
动态数组(ArrayList):
1、ArrayList的底层其实就是一个数组
2、不必在声明时指定长度,会根据存储的数据动态增加或减少长度
3、插入和删除一个元素时,会移动它之后所有元素的位置,效率低,频繁进行插入删除元素时推荐使用LinkedList
4、ArrayList会把所有元素都当做Object处理,因此可以存储不同类型的元素
5、ArrayList是非类型安全的,而且在插入和删除元素时会进行拆箱和装箱的操作,消耗性能,效率低
-
泛型List:
1、List是ArrayList的泛型等效类
2、需要在声明时通过泛型指定类型
3、没有拆箱装箱操作,因此在大多数情况下List要比ArrayList效率高且类型安全
-
双向链表(LinedList):
1、链表在内存中的空间不是连续的,每块空间称作一个节点,每个节点都存有与它之前和之后相连接的节点的地址,因此向链表中添加和删除元素时只需要更改相关节点存储的地址的指向,效率高
2、查找元素时不能通过下标访问,只能从头开始通过地址按顺序查找,效率低
-
堆栈(Stack):
先进后出原则,最先插入的元素最后被访问,最后被插入的元素最先被访问
-
队列(Queue):
先进先出的原则,最先插入的元素最先被访问,最后插入的元素最后被访问
-
字典(Dictionary):
1、创建字典时需要指定key和value的类型
2、字典中的key的值必须唯一,value的值不唯一
3、可以通过key快速查找对应的value,速度快,但是消耗内存
委托
- 委托是一种可以指向方法的数据类型
- 可以理解为指向函数的指针
- 一个委托可以被传递任何符合要求的方法。不同场合需要不同方法时,在调用的地方直接将委托参数替换为实际方法就行。因此,委托调用的方法是在程序运行时才能确定的。
SQL随机生成1W条数据
表名 template
id int 自增
title varchar(200)
lastdate datetime
正则替换
- 给定一个字符串,去除前后空格,中间的空格1个则保留,多个则只保留一个
try 里面返回了finally会执行吗?
- 执行顺序
- finally 块
- 返回值
try
{
return 1; //再返回
}
finally
{
Console.WriteLine("what?"); //先执行
}
单例模式
/// <summary>
/// 单例模式的实现
/// </summary>
public class Singleton
{
// 定义一个静态变量来保存类的实例
// volatile在代码被编译的时候不会微调代码
private static volatile Singleton uniqueInstance;
// 定义一个标识确保线程同步
private static readonly object locker = new object();
// 定义私有构造函数,使外界不能创建该类实例
private Singleton()
{
}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
// 双重锁定只需要一句判断就可以了
if (uniqueInstance == null)
{
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
JavaScript跨域
- JSONP
- 利用 script、img 、iframe 等标签实现
- 所有的请求都属于资源文件请求,所以jsonp只能处理get请求
$.ajax({
url:"",
method:"get",
dataType:"jsonp",
success:function(res){
console.log(res);
}
});
- webpack 代理
C#中DataTable如何添加数据
DataTable dt = new DataTable();
dt.Columns.Add("Name");
dt.Columns.Add("Age");
DataRow dr = dt.NewRow();
dr[0] = "张三";
dr[1] = "15";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["Name"] = "李四";
dr["Age"] = 12;
dt.Rows.Add(dr);
索引相关问题
数据库中索引一般有哪些?
索引分为两大类:聚集索引和非聚集索引,其中,非聚集索引又可细分为普通索引、唯一索引、组合索引。
聚集索引和非聚集索引有什么区别
-
概念层次
-
聚集索引:物理存储按照索引排序,就像字典正文,我们按照正文的字母顺序就能找到我们的记录。
-
非聚集索引:物理存储不按照索引排序,就像偏旁查字法目录,每个偏旁目录下的字的页码不是顺序的。
-
速度方面
-
聚集索引:插入数据时,速度较慢(时间主要花费在“物理存储的排序”上,也就是首先要找到位置然后再插入数据)。但查询数据的速度比非聚集索引要快。
-
实际举例说明
-
我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。
-
如果您认识某个字,您可以快速地从拼音表中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。
sql server中的varchar和Nvarchar有什么区别?
- 解决了什么问题
- Unicode字符集就是为了解决字符集这种不兼容的问题而产生的,它所有的字符都用两个字节表示,即英文字符也是用两个字节表示
- varchar(n)
- 最大8000 英文占1个字节 中文占2个字节
- nvarchar(n)
- 最大4000 不管英文或者中文都占有2个字节
.NET CORE中有什么新特性
- 跨平台
String和StringBuilder的区别
string 具有不可变性,每进行一次赋值操作就会开辟一个新的空间。
而stringbulider 原理类似于list ,当空间不够时,则会自动增加内存空间
前台线程和后台线程
- 前台线程:程序必须等待所有前台线程执行完才会关闭
- 后台线程:主线程执行完毕,则程序会自动关闭
事务中的几种隔离级别
Read uncommitted
读未提交,一个事务可以读取另一个未提交事务的数据。
事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。
Read committed
读提交,一个事务要等另一个事务提交后才能读取数据。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
那怎么解决可能的不可重复读问题?Repeatable read !
Repeatable read
重复读,开始读取数据(事务开启)时,不再允许修改操作
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
那怎么解决幻读问题?Serializable!
Serializable 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。
如何保证缓存与数据库数据的一致性
- 先删缓存(这时候可能会得到以前的旧数据,在数据库还未更新的时候数据又被获取了)
- 更新数据库
- 再删除缓存(更新成功后再次删除缓存,这时候获取缓存基本上就是最新的了)
微服务架构相关
-
微服务架构和单体架构的优劣
-
单体架构
- 缺点
- 大多数情况只能采用单一技术进行,很难使用其它语言或同一语言和不同版本 进行开发
- 耦合性强,一个模块出问题,系统就瘫痪;
- 升级其中一个模块,整个系统都必须停机;
- 集群只能复制整个系统,即使只是其中一个模块压力大
- 缺点
-
微服务架构
- 优点
- 可以使用不同的语言或不同的版本进行开发
- 耦合性弱,其中有一个模块有问题,可以通过“降级熔断”等手段保证系统不雪崩
- 可以独立上线,快速响应需求
- 可以对不同模块使用不同的集群策略
- 缺点
- 开发难度大,系统结构复杂
- 效率相对较低,网络通讯肯定没有进程内通讯快
- 优点
-
Consul 服务注册与服务发现
- 各种服务自己进行服务注册
- 服务的ip地址,端口,健康检查地址,服务名称,服务Id
- 由客户端进行选择消费
- 各种服务自己进行服务注册
-
Polly 熔断降级
- 熔断:就是“保险丝”,当发生某些状况时,切断服务,从而防止应用程序不断的请求执行给系统造成“雪崩”,或大量的超时等待导致系统卡死
- 降级:当某个服务器发生故障时,向调用方返回一个错误的响应或替代响应。
- 例如:调用短信服务时,调用移动的发送短信服务失败,然后去调用联通的,再失败就去调用电信的,如果都失败了,向用户返回错误响应。
- Polly
- 降级
- 重试
- 短路保护
- 比如连续出错3次熔断5秒
- 避免一个服务不可用了还使劲请求对服务器造成巨大压力
- 超时
- 比如当5秒钟请求未响应执行其他代码
-
Ocelot 网关
- 请求转发,类似于Nginx【配置】
- 可以配合Consul使用,提供几种负载均衡策略
- 轮询
- 选择当前连接最少的服务器
- 可以配合Consul使用,提供几种负载均衡策略
- 限流
- 比如:双11时请求量太大,可以关闭商品留言等对主体框架影响不大的功能
- 请求转发,类似于Nginx【配置】
依赖注入的生命周期
- AddTransient: 每一次GetService都会创建一个新的实例
- AddScoped: 在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)
- AddSingleton :整个应用程序生命周期以内只创建一个实例