Entity Framework Core 2.0 入门

http://www.cnblogs.com/cgzl/p/8543772.html

该文章比较基础, 不多说废话了, 直接切入正题.

该文分以下几点:

  • 创建Model和数据库
  • 使用Model与数据库交互
  • 查询和保存关联数据

EF Core支持情况

EF Core的数据库Providers:

此外还即将支持CosmosDB和 Oracle.

EFCore 2.0新的东西:

查询:

  • EF.Functions.Like()
  • Linq解释器的改进
  • 全局过滤(按类型)
  • 编译查询(Explicitly compiled query)
  • GroupJoin的SQL优化.

映射:

  • Type Configuration 配置
  • Owned Entities (替代EF6的复杂类型)
  • Scalar UDF映射
  • 分表

性能和其他

  • DbContext Pooling, 这个很好
  • Raw SQL插入字符串.
  • Logging
  • 更容易定制配置

1.创建数据库和Model

准备.net core项目

项目结构如图:

由于我使用的是VSCode, 所以需要使用命令行:

复制代码
mkdir LearnEf && cd LearnEf
dotnet new sln // 创建解决方案

mkdir LearnEf.Domains && cd LearnEf.Domains
dotnet new classlib // 创建LearnEf.Domains项目

cd ..
mkdir LearnEf.Data && cd LearnEf.Data
dotnet new classlib // 创建LearnEf.Data项目

cd ..
mkdir LearnEf.UI && cd LearnEf.UI
dotnet new console // 创建控制台项目

cd ..
mkdir LearnEf.Tests && cd LearnEf.Tests
dotnet new xunit // 创建测试项目
复制代码

为解决方案添加项目:

dotnet sln add LearnEf.UI/LearnEf.UI.csproj
dotnet sln add LearnEf.Domains/LearnEf.Domains.csproj
dotnet sln add LearnEf.Data/LearnEf.Data.csproj
dotnet sln add LearnEf.Tests/LearnEf.Tests.csproj

 

为项目之间添加引用:

LearnEf.Data依赖LearnEf.Domains:

cd LearnEf.Data
dotnet add reference ../LearnEf.Domains/LearnEf.Domains.csproj

 

LearnEf.Console依赖LearnEf.Domains和LearnEf.Data:

cd ../LearnEf.UI
dotnet add reference ../LearnEf.Domains/LearnEf.Domains.csproj ../LearnEf.Data/LearnEf.Data.csproj

 

LearnEf.Test依赖其它三个项目:

cd ../LearnEf.Tests
dotnet add reference ../LearnEf.Domains/LearnEf.Domains.csproj ../LearnEf.Data/LearnEf.Data.csproj ../LearnEf.UI/LearnEf.UI.csproj

 

(可能需要执行dotnet restore)

在Domains项目下直接建立两个Model, 典型的一对多关系Company和Department:

复制代码
using System;
using System.Collections.Generic;

namespace LearnEf.Domains
{
    public class Company
    {
        public Company()
        {
            Departments = new List<Department>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime StartDate { get; set; }
        public List<Department> Departments { get; set; }
    }
}
复制代码
复制代码
namespace LearnEf.Domains
{
    public class Department
    {
        public int Id { get; set; }
        public int CompanyId { get; set; }
        public Company Company { get; set; }
    }
}
复制代码

 

添加Entity Framework Core库:

首先Data项目肯定需要安装这个库, 而我要使用sql server, 参照官方文档, 直接在解决方案下执行这个命令:

dotnet add ./LearnEf.Data package Microsoft.EntityFrameworkCore.SqlServer
dotnet restore

 

创建DbContext:

在Data项目下创建MyContext.cs:

复制代码
using LearnEf.Domains;
using Microsoft.EntityFrameworkCore;

namespace LearnEf.Data
{
    public class MyContext : DbContext
    {
        public DbSet<Company> Companies { get; set; }
        public DbSet<Department> Departments { get; set; }
    }
}
复制代码

指定数据库Provider和Connection String:

在EFCore里, 必须明确指定Data Provider和Connection String.

可以在Context里面override这个Onconfiguring方法:

有一个错误, 应该是Server=localhost;

(这里无需调用父类的方法, 因为父类的方法什么也没做).

UseSqlServer表示使用Sql Server作为Data Provider. 其参数就是Connection String.

在运行时EfCore第一次实例化MyContext的时候, 就会触发这个OnConfiguring方法. 此外, Efcore的迁移Api也可以获得该方法内的信息.

EF Core迁移:

简单的来说就是 Model变化 –> 创建migration文件 –> 应用Migration到数据库或生成执行脚本.

添加Migration (迁移):

由于我使用的是VSCode+dotnet cli的方法, 所以需要额外的步骤来使dotnet ef命令可用.

可以先试一下现在的效果:

可以看到, dotnet ef 命令还不可用.

所以参考官方文档: https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet

可执行项目(Startup project)需要EFCore迁移引擎库, 所以对LearnEf.UI添加这个库:

dotnet add ./LearnEf.UI package Microsoft.EntityFrameworkCore.Design
dotnet restore

 

然后打开LearnEf.UI.csproj 添加这段代码, 这个库是EF的命令库:

 <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
  </ItemGroup>

 

最后内容如下:

然后再执行dotnet ef命令, 就应该可用了:

现在, 添加第一个迁移:

cd LearnEf.UI
dotnet ef migrations add Initial --project=../LearnEf.Data

 

–project参数是表示需要使用的项目是哪个.

命令执行后, 可以看到Data项目生成了Migrations目录和一套迁移文件和一个快照文件:

检查这个Migration.

前边带时间戳的那两个文件是迁移文件.

另一个是快照文件, EFCore Migrations用它来跟踪所有Models的当前状态. 这个文件非常重要, 因为下次你添加迁移的时候, EFcore将会读取这个快照并将它和Model的最新版本做比较, 就这样它就知道哪些地方需要有变化.

这个快照文件解决了老版本Entity Framework的一个顽固的团队问题.

使用迁移文件创建脚本或直接生成数据库.

生成创建数据库的SQL脚本:

dotnet ef migrations script --project=../LearnEf.Data/LearnEf.Data.csproj

 

Sql脚本直接打印在了Command Prompt里面. 也可以通过指定–output参数来输出到具体的文件.

这里, 常规的做法是, 针对开发时的数据库, 可以通过命令直接创建和更新数据库. 而针对生产环境, 最好是生成sql脚本, 然后由相关人员去执行这个脚本来完成数据库的创建或者更新.

直接创建数据库:

dotnet ef database update --project=../LearnEf.Data/LearnEf.Data.csproj --verbose

 

–verbose表示显示执行的详细过程, 其结果差不多这样:

这里的执行过程和逻辑是这样的: 如果数据库不存在, 那么efcore会在指定的连接字符串的地方建立该数据库, 并应用当前的迁移. 如果是生成的sql脚本的话, 那么这些动作必须由您自己来完成.

然后查看一下生成的表.

不过首先, 如果您也和我一样, 没有装Sql server management studio或者 Visual Studio的话, 请您先安装VSCode的mssql这个扩展:

重启后, 建立一个Sql文件夹, 然后建立一个Tables.sql文件, 打开命令面板(windows: Shift+Ctrl+P, mac: Cmd+Shift+P), 选择MS SQL: Connect.

然后选择Create Connection Profile:

输入Sql的服务器地址:

再输入数据库名字:

选择Sql Login(我使用的是Docker, 如果windows的话, 可能使用Integrated也可以):

输入用户名:

密码:

选择是否保存密码:

最后输入档案的名字:

随后VSCode将尝试连接该数据库, 成功后右下角会这样显示 (我这里输入有一个错误, 数据库名字应该是LearnEF):

随后在该文件中输入下面这个sql语句来查询所有的Table:

--  Table 列表
SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE='BASE TABLE';

 

执行sql的快捷键是windows: Shift+Ctrp+E, mac: Cmd+Shift+E, 或者鼠标右键.

结果如图:

OK表是创建成功了(还有一个迁移历史表, 这个您应该知道).

接下来我看看表的定义:

-- Companies表:
exec sp_help 'Companies';

 

其中Name字段是可空的并且长度是-1也就是nvarchar(Max).

Departments表的Name字段也是一样的.

再看看那个MigrationHistory表:

-- MigrationHistory:
SELECT * FROM dbo.__EFMigrationsHistory;

可以看到, efcore到migration 历史表里面只保存了MigrationId.

在老版本到ef里, migration历史表里面还保存着当时到迁移的快照, 创建迁移的时候还需要与数据库打交道. 这就是我上面提到的如果团队使用ef和源码管理的话, 就会遇到这个非常令人头疼的问题.

如果使用asp.net core的话.

在解决方案里再建立一个asp.net core mvc项目:

mkdir LearnEf.Web && cd LearnEf.Web
dotnet new mvc

 

在解决方案里添加该项目:

dotnet sln add ./LearnEf.Web/LearnEf.Web.csproj

 

为该项目添加必要的引用:

cd LearnEf.Web
dotnet add reference ../LearnEf.Domains/LearnEf.Domains.csproj ../LearnEf.Data/LearnEf.Data.csproj

 

为测试项目添加该项目引用:

cd ../*Tests
dotnet add reference ../LearnEf.Web/LearnEf.Web.csproj

 

操作完之后, 我们可以做以下调整, 去掉MyContext里面的OnConfiguring方法, 因为asp.net core有内置的依赖注入机制, 我可以把已经构建好的DbContextOptions直接注入到构造函数里:

这样的话, 我们可以让asp.net core来决定到底使用哪个Data Provider和Connection String:

这也就意味着, Web项目需要引用EfCore和Sql Provider等, 但是不需要, 因为asp.net core 2.0这个项目模版引用了AspNetCore.All这个megapack, 里面都有这些东西了.

虽然这个包什么都有, 也就是说很大, 但是如果您使用Visual Studio Tooling去部署的话, 那么它只会部署那些项目真正用到的包, 并不是所有的包.

接下来, 在Web项目的Startup添加EfCore相关的配置:

复制代码
 public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddDbContext<MyContext>
                (options => options.UseSqlServer("Server=localhost; Database=LearnEf; User Id=sa; Password=Bx@steel1;"));
        }
复制代码

 

这句话就是把MyContext注册到了asp.net core的服务容器中, 可以供注入, 同时在这里指定了Data Provider和Connection String.

与其把Connection String写死在这里, 不如使用appSettings.json文件:

然后使用内置的方法读取该Connection String:

复制代码
 public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddDbContext<MyContext>
                (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        }
复制代码

 

回到命令行进入Web项目, 使用dotnet ef命令:

说明需要添加上面提到的库, 这里就不重复了.

然后, 手动添加一个Migration叫做InitialAspNetCore:

dotnet ef migrations add InitialAspNetCore --project=../LearnEf.Data

 

看一下迁移文件:

是空的, 因为我之前已经使用UI那个项目进行过迁移更新了. 所以我要把这个迁移删掉:

dotnet ef migrations remove --project=../LearnEf.Data

 

然后这两个迁移文件就删掉了:

多对多关系和一对一关系:

这部分的官方文档在这: https://docs.microsoft.com/en-us/ef/core/modeling/relationships

对于多对多关系, efcore需要使用一个中间表, 我想基本ef使用者都知道这个了, 我就直接贴代码吧.

建立一个City.cs:

复制代码
namespace LearnEf.Domains
{
    public class City
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}
复制代码

 

Company和City是多对多的关系, 所以需要建立一个中间表,叫做 CompanyCity:

复制代码
namespace LearnEf.Domains
{
    public class CompanyCity
    {
        public int CompanyId { get; set; }
        public int CityId { get; set; }
        public Company Company { get; set; }
        public City City { get; set; }
    }
}
复制代码

 

修改Company:

修改City:

尽管Efcore可以推断出来这个多对多关系, 但是我还是使用一下FluentApi来自定义配置一下这个表的主键:

MyContext.cs:

复制代码
using LearnEf.Domains;
using Microsoft.EntityFrameworkCore;

namespace LearnEf.Data
{
    public class MyContext : DbContext
    {
        public MyContext(DbContextOptions<MyContext> options)
            : base(options)
        {

        }
        public DbSet<Company> Companies { get; set; }
        public DbSet<Department> Departments { get; set; }
        public DbSet<CompanyCity> CompanyCities { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<CompanyCity>()
                .HasKey(c => new { c.CompanyId, c.CityId });
        }

        // protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        // {
        //     optionsBuilder.UseSqlServer("Server=localhost; Database=LearnEf; User Id=sa; Password=Bx@steel1;");
        //     base.OnConfiguring(optionsBuilder);
        // }
    }
}
复制代码

 

完整的写法应该是:

其中红框里面的部分不写也行.

接下来建立一个一对一关系, 创建Model叫Owner.cs:

复制代码
namespace LearnEf.Domains
{
    public class Owner
    {
        public int Id { get; set;}
        public int CompanyId { get; set; }
        public string Name { get; set; }
        public Company Company { get; set; }
    }
}
复制代码

 

修改Company:

配置关系:

复制代码
protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<CompanyCity>()
                .HasKey(c => new { c.CompanyId, c.CityId });

            modelBuilder.Entity<CompanyCity>().HasOne(x => x.Company)
                .WithMany(x => x.CompanyCities).HasForeignKey(x => x.CompanyId);

            modelBuilder.Entity<CompanyCity>().HasOne(x => x.City)
                .WithMany(x => x.CompanyCities).HasForeignKey(x => x.CityId);

            modelBuilder.Entity<Owner>().HasOne(x => x.Company).WithOne(x => x.Owner)
                .HasForeignKey<Owner>(x => x.CompanyId);
        }
复制代码

 

 

这里面呢, 这个Owner对于Company 来说 是可空的. 而对于Owner来说, Company是必须的. 如果针对Owner想让Company是可空的, 那么CompanyId的类型就应该设置成int?.

再添加一个迁移:

dotnet ef migrations add AddRelationships --project=../LearnEf.Data

 

查看迁移文件:

查看一下快照;

没问题, 那么更新数据库:

dotnet ef database update AddRelationships --project=../LearnEf.Data --verbose

 

更新成功:

对现有数据库的反向工程.

这部分请查看官方文档吧, 很简单, 我实验了几次, 但是目前还没有这个需求.

使用Model与数据库交互

输出Sql语句.

对于asp.net core 2.0项目, 参考官方文档: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?tabs=aspnetcore2x

实际上, 项目已经配置好Logging部分了, 默认是打印到控制台和Debug窗口的. 源码: https://github.com/aspnet/MetaPackages/blob/dev/src/Microsoft.AspNetCore/WebHost.cs

而对于console项目, 文档在这: https://docs.microsoft.com/en-us/ef/core/miscellaneous/logging

需要对LearnEf.Data项目添加这个包:

cd LearnEf.Data
dotnet add package Microsoft.Extensions.Logging.Console
dotnet restore

 

然后为了使用console项目, 需要把MyContext改回来:

这部分首先是使用LoggerFactory创建了一个特殊的Console Logger. .net core的logging可以显示很多的信息, 这里我放置了两个过滤: 第一个表示只显示Sql命令, 第二个表示细节的显示程度是Information级别.

最后还要在OnConfiguring方法里告诉modelBuilder使用MyLoggerFactory作为LoggerFactory.

这就配置好了.

插入数据.

这部分很简单, 打开UI项目的Program.cs:

这里都懂的, 创建好model之后, 添加到context的DbSet属性里, 这时context就开始追踪这个model了.

SaveChanges方法, 会检查所有被追踪的models, 读取他们的状态. 这里用到是Add方法, context就会知道这个model的状态是new, 所以就应该被插入到数据库. 然后它就根据配置会生成出相应的sql语句, 然后把这个SQL语句执行到数据库. 如果有返回数据的话, 就取得该数据.

下面就运行一下这个console程序:

dotnet run --project=./LearnEf.UI

 

看下控制台:

可以看到输出了sql语句, 而且这个出入动作后, 做了一个查询把插入数据生成的Id取了回来.

默认情况下log不显示传进去的参数, 这是为了安全. 但是可以通过修改配置来显示参数:

然后控制台就会显示这些参数了:

批量插入操作.

可以使用AddRange添加多条数据. 其参数可以是params或者集合.

可以看到这个和之前Add的Sql语句是完全不同的:

这个语句我不是很明白.

批量添加不同类型的数据:

使用context的AddRange或Add方法, DbContext可以推断出参数的类型, 并执行正确的操作. 上面的方法就是使用了DbContext.AddRange方法, 一次性添加了两种不同类型的model.

这两个方法对于写一些通用方法或者处理复杂的情况是很有用的.

Sql Server对于批量操作的限制是, 一次只能最多处理1000个SQL命令, 多出来的命令将会分批执行.

如果想更改这个限制, 可以这样配置参数:

简单查询.

针对DbSet, 使用Linq的ToList方法, 会触发对数据库对查询操作:

首先把Company的ToString方法写上:

这样方便输入到控制台.

然后写查询方法:

看结果:

EfCore到查询有两类语法, 一种是Linq方法, 另一种是Linq查询语法:

这种是Linq方法:

下面这种是Linq查询语法:

我基本都是使用第一种方法.

除了ToList(Async)可以触发查询以外, 遍历foreach也可以触发查询:

但是这种情况下, 可能会有性能问题. 因为:

在遍历开始的时候, 数据库连接打开, 并且会一直保持打开的状态, 直到遍历结束.

所以如果这个遍历很耗时, 那么可能会发生一些问题.

最好的办法还是首先执行ToList, 然后再遍历.

查询的过滤.

这部分和以前的EF基本没啥变化.

这个很简单, 不说了.

这里列一下可触发查询的Linq方法:

还有个两个方法是DbSet的方法, 也可以触发查询动作:

上面这些方法都应该很熟悉, 我就不写了.

过滤的条件可以直接家在上面的某些方法里面, 例如:

通过主键查询, 就可以用DbSet的Find方法:

这个方法有个优点, 就是如果这条数据已经在Context里面追踪了, 那么查询的时候就不查数据库了, 直接会返回内存中的数据.

EF.Functions.Like 这个方法是新方法, 就像是Sql语句里面的Like一样, 或者字符串的Contains方法:

这个感觉更像Sql语句, 输出到Console的Sql语句如下:

这里还要谈的是First/FirstOrDefault/Last/LastOrDefaut方法.

使用这些方法必须先使用OrderBy/OrderByDescending排序. 虽然不使用的话也不会报错, 但是, 整个过程就会变成这样, context把整个表的数据家在到内存里, 然后返回第一条/最后一条数据. 如果表的数据比较多的话, 那么就会有性能问题了.

更新数据.

很简单, context所追踪的model属性变化后, SaveChanges就会更新到数据库.

当然, 多个更新操作和插入等操作可以批量执行.

离线更新.

就是这种情况, 新的context一开始并没有追踪one这个数据. 通过使用Update方法, 追踪并设置状态为update. 然后更新到数据库.

可以看到, 在这种情况下, EfCore会更新该model到所有属性.

Update同样也有DbSet的UpdateRange方法, 也有context到Update和UpdateRange方法, 这点和Add是一样的.

还有一种方法用于更新, 这个以后再说.

删除数据.

DbContext只能删除它追踪的model.

非常简单, 从log可以看到, 删除动作只用到了主键:

如果是删除的离线model, 那么Remove方法首先会让Dbcontext追踪这个model, 然后设置状态为Deleted.

删除同样有RemoveRange方法.

Raw SQL查询/命令:

这部分请看文档:

命令: DbContext.Database.ExecuteSqlCommand();

查询: DbSet.FromSql() https://docs.microsoft.com/en-us/ef/core/querying/raw-sql;

这个方法目前还有一些限制, 它只能返回实体的类型, 并且得返回domain model所有的属性, 而且属性的名字必须也得一一对应. SQL语句不可以包含关联的导航属性, 但是可以配合Include使用以达到该效果(https://docs.microsoft.com/en-us/ef/core/querying/raw-sql#including-related-data).

更多的传递参数方式还需要看文档.

查询和保存关联数据.

插入关联数据.

我之前忘记在Department里面添加Name字段了, 现在添加一下, 具体过程就不写了.

插入关联数据有几种情况:

1.直接把要添加的Model的导航属性附上值就可以了, 这里的Department不需要写外键.

看一下Sql:

这个过程一共分两步: 1 插入主表, 2,使用刚插入主表数据的Id, 插入子表数据.

2.为数据库中的数据添加导航属性.

这时, 因为该数据是被context追踪的, 所以只需在它的导航属性添加新记录, 然后保存即可.

3.离线数据添加导航属性.

这时候就必须使用外键了.

预加载关联数据 Eager Loading.

也就是查询的时候一次性把数据和其导航属性的数据一同查询出来.

看看SQL:

这个过程是分两步实现的, 首先查询了主表, 然后再查询的子表. 这样做的好处就是性能提升.

(FromSql也可以Include).

预加载子表的子表:

可以使用ThenInclude方法, 这个可以老版本ef没有的.

这里查询Department的时候, 将其关联表Company也查询了出来, 同时也把Company的关联表Owner也查询了出来.

查询中映射关联数据.

使用Select可以返回匿名类, 里面可以自定义属性.

这个匿名类只在方法内有效.

看下SQL:

可以看到SQL中只Select了匿名类里面需要的字段.

如果需要在方法外使用该结果, 那么可以使用dynamic, 或者建立一个对应的struct或者class.

使用关联导航属性过滤, 但是不加载它们.

SQL:

这个比较简单. 看sql一切就明白了.

修改关联数据.

也会分两种情况, 被追踪和离线数据.

被追踪的情况下比较简单, 直接修改关联数据的属性即可:

看一下SQL:

确实改了.

这种情况下, 删除关联数据库也很简单:

看下SQL:

删除了.

下面来看看离线状态下的操作.

这里需要使用update, 把该数据添加到context的追踪范围内.

看一下SQL:

这个就比较怪异了.

它update了该departmt和它的company以及company下的其他department和company的owner. 这些值倒是原来的值.

这是因为, 看上面的代码, 查询的时候department的关联属性company以及company下的departments和owner一同被加载了.

尽管我只update了一个department, 但是efcore把其他关联的数据都识别出来了.

从DbContext的ChangeTracker属性下的StateManger可以看到有多少个变化.

这一点非常的重要.

如何避免这个陷阱呢?

可以这样做: 直接设置dbContext.Entry().State的值

这时, 再看看SQL:

嗯. 没错, 只更新了需要更新的对象.

 

 

2.1版本将于2018年上半年发布, 请查看官网的路线图: https://github.com/aspnet/EntityFrameworkCore/wiki/roadmap

完.

草根专栏, 草根的.net core专栏
分类: .Net Core

由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作

utorrent 出现 “由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作”tracker红肿如何解决:

答:
可能是你留作种的原因,所以tcp的端口(UserPort)请求已经达到你pc上本地设置的界限(MaxUserPort),可以试着修改此键值;
方法如下(修改前请备份好你的注册表文件,以免发生意外):
.启动注册表编辑器。
..在注册表中,找到以下子项,然后单击$参数(Parameters 翻译过来就是>>参数<<的意思)
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
…在编辑菜单中,单击新建,然后添加以下注册表项:
值名称: MaxUserPort
值类型: 双字节
值数据: 65534
有效范围: 5000-65534 (十进制)
默认值: 0x1388 (5000 十进制)
….退出注册表编辑器,然后重新启动计算机
参考:

TcpTimedWaitDelay和MaxUserPort设置与网络吞吐量

TcpTimedWaitDelay

    • 描述:确定 TCP/IP 在释放已关闭的连接并再次使用其资源前必须经过的时间。关闭与释放之间的这段时间称为 TIME_WAIT 状态或者两倍最大段生存期(2MSL)状态。此时间期间,重新打开到客户机和服务器的连接的成本少于建立新连接。通过减少此条目的值,TCP/IP 可以更快地释放关闭的连接,并为新连接提供更多资源。如果运行中的应用程序要求快速释放连接或创建新连接,或者由于多个连接处于 TIME_WAIT 状态而导致吞吐量较低,请调整此参数。
    • 如何查看或设置:

1.     使用regedit命令,访问 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters 注册表子键,然后创建新的 REG_DWORD 值TcpTimedWaitDelay。

2.     将此值设置为十进制30,即十六进制 0x0000001e。此值将等待时间设置为 30 秒。

3.     停止并重新启动系统。

    • 缺省值:0xF0,此值将等待时间设置为 240 秒(4 分钟)。
    • 建议值:最小值为0x1E,此值将等待时间设置为 30 秒。
  • MaxUserPort
    • 描述:确定当应用程序向系统请求获取可用的用户端口时,TCP/IP 可指定的最高端口号。
    • 如何查看或设置:

1.     使用regedit命令,访问 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters 注册表子键,然后创建新的 REG_DWORD 值MaxUserPort。

2.     将此值至少设置为十进制32768。

3.     停止并重新启动系统。

    • 缺省值:
    • 建议值:至少为十进制32768。
  • 最大连接储备
    • 描述:如果同时接收到许多连接尝试,请增大操作系统支持的缺省暂挂连接数。
    • 如何查看或设置:

1.     使用regedit命令并访问 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters 注册表子键。

2.     根据需要创建并设置下列值:

3. “EnableDynamicBacklog”=dword:00000001

4. “MinimumDynamicBacklog”=dword:00000020

5. “MaximumDynamicBacklog”=dword:00001000

6. “DynamicBacklogGrowthDelta”=dword:00000010

7.     这些值将最小可用连接数设置为 20,将最大可用连接数设置为 1000。每当可用连接数小于最小可用连接数时,可用连接数都会增加 10。

8. 停止并重新启动系统。

  • KeepAliveInterval
    • 描述:确定 TCP 在未接收到响应时重新尝试保持活动传输的频率。
    • 如何查看或设置:

1.     使用regedit命令,访问 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters 注册表子键,然后创建新的 REG_DWORD 值KeepAliveInterval。

2.     将此值设置为1秒。

3.     停止并重新启动系统。

  • 缺省值:1秒
  • 建议值:
参考:

windows 分页缓冲池 非分页缓冲池

最近在windows server 2012机器上在做性能测试时,发现8G物理内存,内存使用率占到了90%多,在“进程”列表中所有进程内存相加才2个多G,同时任务管理器->“性能”标签一项中,非内存缓冲池很高占到了5个多G。

在网上找了一些资料,说是windows8系列有内存泄露的BUG,会引起“非内存缓冲池”一直占用很高。当前系统初步怀疑可能也是这个问题。

参考文档:

一次DB服务器性能低下引发的对Nonpaged Pool Leak问题的诊断

 http://tieba.baidu.com/p/2728129582

 

先使用poolmon.exe来分析哪个组件占用内存高,再对这个组件做相应的处理。

 

对于分页缓冲池与非页面缓冲池

PagedPool 和 NoPagedPool的区别Windows kernel pool

1、页面一直锁定在物理内存中,不会被换出到页面交换文件中

2、Windows把虚拟地址分为用户地址空间和系统地址空间,用户地址空间是给应用程序使用的,系统地址空间是给系统核心和驱动程序使用的。系统地址空间分为分页池和非分页池,分页池是指映射到分页文件的虚拟地址,当要使用该地址时才交换到物理内存中,由系统来调度;非分页池是指直接在物理内存中分配的内存。“页面缓冲池”就是进程占用的分页池中的虚拟内存,是进程调用某些系统功能时,由系统核心或者驱动程序分配的。如果一个程序占用的页面缓冲池内存不断增大,就是内存泄露,通常应该是创建或打开了句柄没有关闭。

 

在perfmon计数器里统计这两个参数时,momory对象与process对象里都存在相关的值(两种pool都会被映射到每一个进程空间内)。

Perfmon – Windows 自带系统监测工具

解决windows系统因TCP端口不足导致mysql数据库无法访问的问题

在windows服务器上面批量处理数据的时候,遇到下面的异常,意思是说连接数用完了,无法再建立连接。

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The driver was unable to create a connection due to an inability to establish the client portion of a socket.

This is usually caused by a limit on the number of sockets imposed by the operating system. This limit is usually configurable. 

For Unix-based platforms, see the manual page for the 'ulimit' command. Kernel or system reconfiguration may also be required.

For Windows-based platforms, see Microsoft Knowledge Base Article 196271 (Q196271).
    at sun.reflect.GeneratedConstructorAccessor16.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

默认情况下,Windows 允许用于使用5000 个临时(短命)TCP 端口。任何端口关闭后,它将在TIME_WAIT 状态保持120 秒。与重新初始化全新的连接相比,该状态允许以更低的开销重新使用连接。 但是,在该时间逝去前,无法再次使用该端口。
对于小的可用TCP 端口堆栈(5000 ),以及具有TIME_WAIT 状态的大量在短时间内打开和关闭的 TCP 端口,你很可能遇到端口耗尽问题。
我们可以通过修改注册表配置来解决问题:
1,启动注册表编辑器(Regedt32.exe )。

2,在注册表中确定下述键值的位置:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
在“编辑”菜单上点击“添加值”,然后增加下述注册值:
Value Name: MaxUserPort
Data Type: REG_DWORD
Value: 65534
它用于设置为任何用户提供的临时端口数。有效范围介于5000 和65534 之间(十进制)。默认值为0x1388 (5000 ,十进制)。

3,在“编辑”菜单上点击“添加值”,然后增加下述注册值:
Value Name: TcpTimedWaitDelay
Data Type: REG_DWORD
Value: 30
它用于设置关闭之前将TCP 端口连接保持在TIME_WAIT 状态的秒数。 有效范围介于0 秒和300 秒之间。默认值为0x78 (120 秒)。

4,退出注册表编辑器。

5,重启服务器。