博客 (85)

“/”应用程序中的服务器错误。


The host xxx does not support SSL connections.

说明: 执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。 

异常详细信息: MySql.Data.MySqlClient.MySqlException: The host xxx does not support SSL connections.



在 Web.config  的 connectionStrings 节点找到相关数据库连接,在 connectionString 中增加属性值:SslMode=none

xoyozo 7 年前
5,328

本文未完成,部分测试方法、条件或结果可能有误,请谨慎参考! :)

本文基于 MySQL 的 InnoDB BTREE 方法的索引进行测试。

以一张包含 2000 万条记录的表做实验:

CREATE TABLE `dt_read`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `time` datetime(0) NOT NULL,
  `a_id` int(11) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
);

这张表是用于记录文章点击量的,

`id` 为主键,int(11) 自增;

`time` 为非空 datetime,表示文章打开时间,测试数据是从 2017-03-11 至 2018-04-28;

`a_id` 为非空 int(11),表示文章 ID,在此表中不唯一,测试数据是从 1 至 260218。


体验“全表扫描”


首先来体验一下什么是全表扫描,执行下面语句:

SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' LIMIT 10

> 时间: 0.012s


SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' LIMIT 10

> 时间: 7.317s


表中数据是按主键从小到大排列的,当查询条件为 `time` < '2020-1-1' 时,能很快地从表的前端找到 10 条满足条件的数据,所以不再继续判断后面的记录,立刻返回结果,耗时 0.012 秒;但当条件改为 `time` < '2000-1-1' 时,同样逐条判断,直到最后一条也没有找到,这种情况就是所谓的“全表扫描”,耗时 7 秒。


索引对 ORDER BY 的 ASC 和 DESC 的影响


我们给 `time` 建一个索引,同样执行刚才需要全表扫描的语句:

SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' LIMIT 10

> 时间: 0.012s


创建 `time` 的索引后,相当于生成了一张按 `time` 字段排列的新表,这时 MySQL 就能够很快地定位并找到符合条件的记录,避免了全表扫描。


试试按 `time` 倒序排:

SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `time` DESC LIMIT 10

> 时间: 0.013s


结论:索引对 ORDER BY 的顺序(ASC)和倒序(DESC)都是有效的。


索引字段的次序对 WHERE 和 ORDER BY 的影响


删除所有索引,创建一个新的索引,字段依次为 `time`, `a_id`。

分别执行以下查询:


SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' AND `a_id` < 0 LIMIT 10

> 时间: 0.013s


SELECT * FROM `dt_read` WHERE `a_id` < 0 AND `time` < '2000-1-1' LIMIT 10

> 时间: 0.013s


结论:MySQL 会自动优化 WHERE 条件的次序来匹配最合适的索引。

但在 ORDER BY 中却不是这么回事了:


SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10

> 时间: 0.013s


SELECT * FROM `dt_read` ORDER BY `a_id`, `time` LIMIT 10

> 时间: 14.066s


原因也很好理解,对两个字段进行排序,先后次序肯定会影响结果集,因此只能以 SQL 语句指定的字段次序来 ORDER BY,这样,按索引的字段次序进行 ORDER BY 查询无疑是更快的。


索引中的字段必须依次使用


保持上例创建的索引不变,即 `time`, `a_id`。


SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' AND `a_id` < 0 LIMIT 10

> 时间: 0.013s


SELECT * FROM `dt_read` WHERE `a_id` < 0 LIMIT 10

> 时间: 6.438s


上句合理利用了索引的字段,而下句跳过了 `time`,直接 WHERE 了 `a_id`,这是不受该索引支持的。

我们可以想象一下这张由索引生成的虚拟表,其实就是一张普通的平面二维表格,按索引指定的字段次序进行了排序,所以全表中仅仅是索引指定的第一个字段是按大小排列的,第二个字段是在第一个字段值相同的区域内按大小排列,后同。所以,跳过索引指定的第一个字段直接对第二个字段进行检索,是无法应用该索引的。这个结论也同样也体现在 ORDER BY 语句中:


SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10

> 时间: 0.013s


SELECT * FROM `dt_read` ORDER BY `a_id` LIMIT 10

> 时间: 29.566s


WHERE 和 ORDER BY 混合


保持上例创建的索引不变,即 `time`, `a_id`。


先来执行这两句:


SELECT * FROM `dt_read` ORDER BY `a_id` LIMIT 10

> 时间: 12.29s


SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `a_id` LIMIT 10

> 时间: 0.013s


仅仅 WHERE 了一个 `time`,对 ORDER BY `a_id` 的效率却有质的提升,是因为 WHERE 中的 `time` 和 ORDER BY 中的 `a_id` 一起找到了索引吗?答案是否定的。

我们把时间改大,让它能马上找到符合条件的数据:


SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' ORDER BY `a_id` LIMIT 10

> 时间: 22.34s


为什么这个语句就不走索引了呢?

其实,一个简单的 SELECT 查询语句,首先执行 WHERE,然后 ORDER BY,最后是 LIMIT。每一步都独自去找了索引,而非 WHERE 和 ORDER BY 混在一起去找索引。必须保证每一步是快的,最终才是快的。

当 `time` < '2000-1-1' 时,WHERE 用到了索引,所以很快,ORDER BY 却没有用到索引,但为什么也很快呢?因为 WHERE 的结果集非常小(示例中为 0 条)。

当 `time` < '2020-1-1' 时,WHERE 也用到了索引,但其结果集非常大(示例中为所有记录),再 ORDER BY `a_id` 就非常慢了,因为我们没有创建以 `a_id` 开头的索引。


现在把索引改成只有 `time` 一个字段。


SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' ORDER BY `a_id` LIMIT 10

> 时间: 6.033s


因为索引里有 `


SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `a_id` LIMIT 10

> 时间: 0.013s


SELECT * FROM `dt_read` WHERE `a_id` < 0 ORDER BY `time` LIMIT 10

> 时间: 6.033s


第二句先 WHERE `a_id`,后 ORDER BY `time` 是不能匹配所建的索引的。


索引中的字段越多越好


分别在创建索引(`time`)和索引(`time`, `a_id`)的情况下执行下面语句:

本例使用 ORDER BY 而不是 WHERE 来测试是因为,在 WHERE 的多个条件下,如果符合前一条件的筛选结果集过小会导致判断第二条件时数据量不足,无法判断索引是否起作用。


SELECT * FROM `dt_read` ORDER BY `time` LIMIT 10


仅创建索引(`time`)的情况下:

> 时间: 0.013s


仅创建索引(`time`, `a_id`)的情况下:

> 时间: 0.013s


SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10


仅创建索引(`time`)的情况下:

> 时间: 15.015s


仅创建索引(`time`, `a_id`)的情况下:

> 时间: 0.014s


可以看到,在索引字段依次使用的前提下,索引字段数不少于查询字段数才能避免全表扫描。

虽然索引中的字段越多越好,但必须依次使用,否则也是无效索引。


索引对 INSERT / UPDATE / DELETE 的效率影响


分别在创建索引(`time`)和索引(`time`, `a_id`)的情况下执行下面语句:


INSERT INTO `dt_read` (`time`, `a_id`) VALUES ('2018-4-28', 260218)


不建索引的情况下:

> 时间: 0.01s


仅创建索引(`time`)的情况下:

> 时间: 0.01s


同时创建索引(`time`)和索引(`time`, `a_id`)的情况下:

> 时间: 0.01s


UPDATE `dt_read` SET `time` = '2018-4-28' WHERE `id` = 20000000(注:存在该 id 值的记录)


不建索引的情况下:

> 时间: 0.01s


仅创建索引(`time`)的情况下:

> 时间: 0.01s


同时创建索引(`time`)和索引(`time`, `a_id`)的情况下:

> 时间: 0.01s


虽然在 INSERT / UPDATE / DELETE 时数据库会更新索引,但从实测数据来看,索引对其效率的影响可忽略不计。


一些误区


“in 语法效率很低”?

in 语法也是应用索引的,网传 in 会比一个一个 WHERE OR 要慢得多的说法是不靠谱的。in 主键和 in 索引同理。


另外:

对于字符串类型,LIKE '%abc%' 是不能应用索引的,但 LIKE 'abc%' 可以。更多关于字符串类型的索引,请查阅全文索引(FULLTEXT)。

索引的字段是可以指定长度的,类似字符串索引指定前面若干唯一字符就可以优化效率。


本文系个人实践总结,欢迎批评指正!


xoyozo 7 年前
3,775

删除主题时,会变更主题表 pre_forum_thread:

displayorder = -1

moderated = 1

帖子表(或分表) pre_forum_post* 该主题的所有帖子的

invisible = -1

同时 pre_forum_threadmod 会新增一条 DEL 记录,用于显示到 Discuz! 控制面板 - 内容 - 主题回收站。


删除回帖时,会变更回帖表 pre_forum_post(或分表):

invisible = -5


屏蔽回复时,会变更回复表 pre_forum_post(或分表):

status = 1

xoyozo 7 年前
4,272

* 仅列出部分数据表及字段


mag_ads 广告

类型注释
id
int
titlevarchar标题
picvarchar图片
begin_timeint开始时间
end_timeint结束时间
linkvarchar链接
……



mag_circle 圈子(相当于 PC 版的版块)

类型注释
id
int
namevarchar名称
……


mag_common_applaud_(n) 点赞详情表(分表)n 与 content_id 末位一致

类型注释
id
int
typevarchar 类型(主题/回复/打卡等)
content_idint 内容 id,末位与表名后缀一致
user_idint 用户 id
statusint1:点赞;0:取消点赞


mag_common_attachment_(n) 附件详情表(分表)n 与 aid 末位一致

类型注释
aid
int附件ID(末位与表名后缀一致)
……


mag_common_attachment_index 附件索引表

类型注释
idint附件ID
user_idint用户ID
table_idint分表标志
……



mag_common_comment_index 评论索引表(相当于 PC 版的回复表)

类型注释
idint附件ID
content_idint文章ID(mag_show_content)
user_idint用户ID
contentvarchar评论内容
……



mag_common_content_log 对圈子内容的操作日志(点赞、评论等)

类型注释
idint
typevarchar用户ID
type_valueint内容ID
user_idint用户ID
create_timeint操作时间
……


mag_show_content 圈子内容(相当于 PC 版的主题表)

类型注释
idint
user_idint用户ID
contentvarchar内容
picsvarchar以半角逗号分隔的图片(附件)id
……



mag_user 用户表

类型注释
user_idint用户ID
namevarchar用户名/昵称
headvarchar头像
……



xoyozo 7 年前
4,709

重点提前看:


https://xoyozo.net/Blog/Details/MySQL-on-Windows




Connector/Net 6.10.4(暂)不支持在 Visual Studio 2015/2017 添加 ADO.NET 实体数据模型时选择 MySQL Data Provider 数据源。并且,在编译项目时会出现“违反了继承安全性规则”的异常。


只能降回 Connector/Net 6.9.10,NuGet 中将 MySql.DataMySql.Data.Entity 用 6.9.10 和 6.10.4 版本皆可(除非遇到下文中提到的异常)。

重新生成或重启 VS 使之生效。


另外,MySql.DataMySql.Data.Entity 6.9.10 可以配合 EnityFrameworkEntityFramework.zh-Hans 6.1.x6.2.0 使用。


MySql.Data 和 MySql.Data.Entity 6.9.9 在创建/更新实体模型向导中会出现闪退,更换为 6.9.10 试试。

或重新安装 Connector/NET 和 MySQL for Visual Studio 并重启电脑试试。

仍然闪退,参此文:https://xoyozo.net/Blog/Details/mysql-for-vs-flashback


重要提醒,如果在添加 EF 模型时报错:

您的项目引用了最新版的实体框架;但是,找不到进行数据连接所需的与此版本兼容的实体框架数据库提供程序。请退出向导,安装兼容提供程序,重新生成您的项目,然后再执行此操作。

那么在菜单中选择:生成 - 清理解决方案,然后直接添加数据模型。如果在添加前生成了项目(bin 目录下有相关 .dll 文件),那么将出现上述错误。


类型“MySql.Data.MySqlClient.MySqlProviderServices”违反了继承安全性规则。派生类型必须与基类型的安全可访问性匹配或者比基类型的安全可访问性低。

将 MySql.Data 和 MySql.Data.Entity 从 6.10.4 退回 6.9.10 版本即可。


以上问题出现在:

VS 15.4.*

VS 15.4.4

VS 15.4.5

----------------

VS 15.5 已经可以在 EF 向导中连接 MySQL 数据库(Connector/Net 6.10.4),但 MySql.Data.Entity 6.10.4 编译仍然出现“违反了继承安全性规则”。另:MySQL 官网下架了 Connector/Net 6.10.4,退回提供 6.9.10 版本。

2017-12-9 发布的 Connector/Net 6.10.5 仍然存在无法在 EF 向导中连接 MySQL 数据源的问题。


服务器上安装 Connector/Net 6.10.4 没有任何问题。


表“TableDetails”中列“IsPrimaryKey”的值为 DBNull。怎么办?

xoyozo 7 年前
6,846

Session 有多种存储模式,默认是 InProc,顾名思义是“在进程内”,即在 IIS 中,随网站重启而重启,网站发布后(或改动 .dll 文件)会导致应用重新运行,Session 清空。

Session 可以使用 StateServer 来保存,需要服务器开启 ASP.NET State Service 服务,此种方式要求保存在 Session 的信息必须序列化,然后从 Session 中获取的时候也要反序列化,这就导致性能有略微的损失。

Session 还可以配置为 SQLServer,将 Session 存储在数据库中,同样需要序列化。

当配置为 Custom 时,可以在使用 Memcached、Redis 等第三方缓存技术来实现 Session 的管理,本文不作讨论。

Off 模式即关闭 Session,这种方式如果要实现用户登录功能,就必须依赖 Cookie 等来实现。

下面针对常见的三种模式进行比较:


InProcStateServerSQLServer
存储位置应用进程内ASP.NET State Service 服务SQL Server 数据库
远程存储不支持支持支持
存储格式
任意序列化的序列化的
可能导致重启(丢失)的情况

配置文件中 processModel 标签的 memoryLimit 属性;

Global.asax 或者 Web.config 文件被更改;

Bin 文件夹中的 Web 程序(DLL)被修改;

杀毒软件扫描了一些 .config 文件;

系统资源紧张进行资源回收导致 IIS 进程崩溃或重启等。

ASP.NET State Service 服务停止;-
Session_End() 事件
部署难度简单稍复杂

Web.config 配置示例

(<system.web> 节点内)

<sessionState mode="InProc" /><sessionState mode="StateServer" /><sessionState mode="SQLServer" sqlConnectionString ="data source=x.x.x.x; user id=[user]; password=[pwd]" />

因此,如果你仅仅为了标题所述的“每次发布后丢失 Session”而烦恼,那么推荐使用 StateServer 模式来存储 Session。


注:

如果 Session 的 Key 是在应用启动时随机生成的话,使用 StateServer 和 SQLServer 还是会“丢失” Session。

这是对 Session 的配置,对 Cache 无效。

xoyozo 7 年前
4,890

如果我们直接更改模型的属性名,在迁移的时候会报错:

Data\DbInitializer.cs(,): error CS0117: '表' does not contain a definition for '新列名'


原因是它无法判断是把什么列名更改为什么列名(比如一次改多列呢),那么我们需要用 [Column("列名")] 来映射


方法一:

先加上 [Column("新列名")] ,执行迁移

再修改模型的属性名并删除 [Column("新列名")],执行迁移


方法二:

先加上 [Column("原列名")] ,同时修改模型的属性名,执行迁移,

再删除 [Column("原列名")],执行迁移


xoyozo 8 年前
4,670

本文不定时更新!


A: MySQL 执行 SHOW FULL PROCESSLIST 

Q: 查看连接数和慢查询,适用于 MySQL 数据库无法连接 1040


A: iftop -i eth0

Q: 查看占用带宽的IP(命令:iftop -i eth0 -F ip/24),添加到安全组、防火墙、宝塔的黑名单中。

命令 grep -l "x.x.x.x" /www/wwwlogs/*.log 可以在 wwwlogs 目录下的所有 .log 文件中查找指定的恶意 IP。


A: goaccess -f xxx.log

Q: 实时分析网站日志,查看请求最多的IP


A: net.xoyozo.weblog 日志分析工具

Q: 自制的 Web 日志分析工具,可按多种方式排序,纠出可疑访问


A: 重启 web 服务器

Q: 有时候能解决 CPU 和内存消耗的问题,如果一会儿又升高,则需要找另外的原因


Q: 500 服务器内部错误

502 Bad Gateway

504 Gateway Time-out

A: 查看 php 日志,可能的路径:

/usr/local/php/var/log/php-fpm.log

/www/server/php/[版本]/var/log/php-fpm.log


Q: RDS MySQL IOPS 使用率高的原因和处理

A: 根据时间点查看慢查询


Q: Discuz! 论坛界面错乱、表情不显示、模块缺失、登录失败、发帖失败等等

A: 进入管理中心 - 工具 - 更新缓存,能解决大部分问题


Q: Discuz! 浏览帖子提示“没有找到帖子

A: 进入数据库,修复表 pre_forum_post 或分表


Q: CPU 100% 或内存 100%,负载100+

A: 原因有很多,以下是一些建议:

Windows 在任务管理器中查看进程

当前是否有正常的大流量访问(譬如民生类论坛的某个帖子突然火了)特别是重启无效的情况

对比网站日志大小可大致确定哪个网站被大量恶意请求。

观察:命令 top

排查:通过关闭网站来确定是某网站的问题,通过关闭功能确定是某功能的问题,如果 nginx 崩溃请参下条

案例:通过修改 mobcent 文件夹名确定是安米的文件被疯狂请求导致的,更新插件和 mobcent 包解决问题。

如果都是正常访问,top 看到很多 php-fpm,而且个个占用 CPU 还不小,那么根据服务器硬件配置来修改 php 的并发量,如宝塔面板在 php 设置 - 性能调整 页,300 并发方案的推荐配置是:

max_children:300
start_servers:30
min_spare_servers:30
max_spare_servers:180

另外,memcached 或 redis 的配置也可以进行相应的修改。

另一个案例是 kswapd0 进程占满 CPU,原因是内存不足导致 swap 分区与内存频繁交换数据。同样调整 php 的设置即可。

也可以通过 iftop 来查询占用带宽较多的 IP 并封禁(出方向),如果 CPU 能降下来,那这个 IP 就是罪魁祸首。


Q: 阿里云 ECS 的 CPU 突然达到 100%,并持续到次日 0:00 左右

A: 可能 ECS 是 t5 规格,受 CPU 积分制度限制,积分耗尽时 CPU 不工作。解决方法是更换其它规格产品或升配。


Q: ASP.NET 所在服务器 CPU 突然达到 50% 或 100%,并持续

A: 首先确定哪个网站,再依次排查网站各功能。可能是 HttpWebRequest 请求远程数据时长时间未返回结果导致的程序阻塞。


Q: nginx 服务停止

A: 查看 nginx 日志

WDCP 路径:/www/wdlinux/nginx-1.0.15/logs/error.log


Q: 公网出带宽 100%,其它指标正常

A: Windows 在任务管理器-性能-资源监视器-网络 查看占用带宽的进程PID,然后在任务管理器-详细信息中的找到对应的用户(如果为每个网站分别创建了用户,就能知道是哪个网站占用了带宽);如果是被 PID 为 4 的 System 占用大部分带宽,也可以尝试重启 IIS 来解决。

CentOS 使用 nethogs 查看占用带宽的进程PID和USER,如果为每个网站分别创建了用户,就能知道是哪个网站占用了带宽,否则只能一个个关闭网站来判断,不知道大家有没有好的方法?当然还可以直接用 iftop 命令查看占用带宽的 IP。另外,查看每个网站在那个时间段的日志文件的大小也能大概看出是哪个网站被采集了。


A: Linux 显示每个用户会话的登入和登出信息

utmpdump /var/log/wtmp

参考:http://www.tulaoshi.com/n/20160331/2050641.html


Q: RDS 的 CPU 100%

A: 如果是突然持续占满(同时伴随 ECS 资源使用率下降,页面出现 502),很大可能是受攻击(或社交网站推送突发事件等),查看“慢查询”,添加相关索引;如果是 Discuz! 论坛,可尝试修复优化表 pre_common_session。

如果是数日缓步上升,或新项目上线,考虑 SQL 慢查询,思路:MySQL / SQL Server

MySQL:SHOW FULL PROCESSLIST

SQL Server:sp_who


Q: php 网站的服务器,内存在数天内缓慢上升

A: 大概是 php-fpm 占用过多,或进程数太多

更改 php 的配置(如 max_spare_servers),执行:service php-fpm reload


Q: 进程 cloudfs 占用内存过多

A: 参:https://xoyozo.net/Blog/Details/cloudfs-cache


Q: RDS 磁盘占用过大

A: 参:https://xoyozo.net/Blog/Details/how-to-use-rds


Q: ECS 受到 DDoS 攻击怎么办?

A: 参:https://xoyozo.net/Blog/Details/aliyun-ddos-without-bgp


Q: 如果 ECS 和 RDS 各项指标都没有异常,但网页打开慢或打不开502,TTFB 时间很长,是什么原因?(ECS 的 CPU 100%,RDS 的连接数上升,也可参考此条)

A: 数据库有坏表,尝试优化/修复表(慢 SQL 日志中锁等待时间较长的表?),或主备切换。show full processlist 时看到许多

DELETE FROM pre_common_session WHERE  sid='******'  OR lastactivity<******  OR (uid='0' AND ip1='*' AND ip2='*' AND ip3='*' AND ip4='*' AND lastactivity>******)


Q: Discuz! 创始人(站长)密码被改

A: 数据库找到 pre_ucenter_members 表,复制其它的已知登录密码的账号,复制其 password 和 salt 两个字段的值到创始人账号中,创始人账号即可用该密码登录了。


Q: 通过 iftop 观察到,Discuz! 网站从 RDS 数据库到 ECS 网站服务器私网流量非常大,远大于公网流量

A: 可能是缓存出问题了,尝试卸载重装 Redis 来解决。


Q: 宝塔面板中安装的 Redis 经常自动停止

A: 尝试卸载重装 Redis 来解决。


Q: 马甲客户端出现“您的网络有些问题”

A: 原因有许多,其中一个就是新建了一个数据表,然后 /source/class/table/ 下面丢失了对应的文件,具体可以找官方排查原因。


Q: 排查服务器安全需要检查哪些日志?

A: Web日志、登录日志(/var/log/secure)等。


xoyozo 8 年前
8,177

论坛使用阿里云的 ECS + RDS + OSS 搭建,最近经常隔三差五出现 RDS 的 CPU 和连接数突然满负荷的情况,导致数据库无法连接。这种情况一般会认为是受到了攻击,因为如果是访问量大或者是哪里有慢查询,应该是资源消耗逐步上升直至崩溃的,沿着这个思路去查 Web 日志封 IP,但效果不大,关闭功能、卸载插件也没用。

开启阿里云后台的 SQL 审计,能看到 SQL 查询日志,但是很难找有问题的 SQL。

最终在重启 RDS 后执行以下语句列出所有正在执行或阻塞的语句:

show full processlist

在结果列中,Command 为 Query 是正在执行查询操作的语句,发现几乎所有的 SQL 都是:

SELECT * FROM pre_forum_thread WHERE tid>0 AND fid IN('42','95','247','41','567','62','149','229','37','230','93','190','284','75','38','568') AND `fid`<>'546' AND replies > 0 AND displayorder>=0 ORDER BY lastpost DESC  LIMIT 10

再加上之前出现的情况是,论坛帖子列表和详情页面能正常打开时,论坛首页也不一定能打开,所以基本定位到是“首页四格”的数据库查询导致的。

进入论坛后台首页四格设置,对比了版块 id 后确认了这个 bug。

单独执行该语句大约耗时 5s(主题帖 200 万),设置的缓存时间 10 分钟。

processlist 中看到这些语句的 state 都是 Creating sort index,尝试去掉 ORDER BY 后执行果然只需要 16ms。

5s 内的访客都是从数据库读取的,能处理完就正常,否则累积就导致 RDS 崩溃,每 10 分钟都会重现一次风险。

当然这个问题可以通过添加索引来解决。

xoyozo 8 年前
6,696

新建项目

使用 VS2017 / VS2015 新建项目 - ASP.NET Web 应用程序 - MVC - 个人身份验证

添加模型

右击 Models 文件夹,添加 Movie

using System;
using System.Data.Entity;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }

    public class MovieDBContext : DbContext
    {
        public DbSet<Movie> Movies { get; set; }
    }
}

Movie 类表示一部电影,一个对象实例对应数据库中一行,每个属性对应表中一列。

MovieDBContext 代表 EF 数据库上下文,处理抓取、存储、更新。

创建连接字符串和使用 SQL Server 

打开 Web.config

在 <configuration /> 中的 <connectionStrings /> 中添加

<add name="MovieDBContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Movies.mdf;Initial Catalog=Movies;Integrated Security=True"
      providerName="System.Data.SqlClient" />

name 必须与 DbContext 类的名称匹配(MovieDBContext)

这步将会把数据库文件 Movies.mdf 创建到 App_Data 文件夹中,你也可以使用其它 SQL Server 数据库的连接字符串,简单的方法是:

在“服务器资源管理器”中添加连接

从右击属性中获取连接字符串

从控制器访问模型的数据

右击 Controllers 文件夹添加控制器,选择 包含视图的 MVC 5 控制器(使用 Entity Framework)

模型类:Movie (MvcMovie.Models)

数据上下文类:MovieDBContext (MvcMovie.Models)

F5 运行,访问 /Movies 可添加、查看、编辑、删除影片

添加新字段

设置模型更改的 Code First 迁移

程序包管理器控制台窗口中,在 PM> 提示符下输入

Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext

在 Migrations 文件夹中新建了 Configurations.cs,打开并在 Seed 方法中加入

context.Movies.AddOrUpdate(i => i.Title,
  new Movie
  {
    Title = "When Harry Met Sally",
    ReleaseDate = DateTime.Parse("1989-1-11"),
    Genre = "Romantic Comedy",
    Price = 7.99M
  },
  new Movie
  {
    Title = "Ghostbusters ",
    ReleaseDate = DateTime.Parse("1984-3-13"),
    Genre = "Comedy",
    Price = 8.99M
  }
);

Seed() 将在每次迁移(PM>update-database)后被调用执行,AddOrUpdate 将执行 upsert 操作(有则 update,无则 insert)

AddOrUpdate 的第一个参数指定用于检查行是否已存在的属性

如果该属性值不唯,则出现异常

序列包含多个元素

创建迁移命令:

PM>add-migration Initial

名称“Initial”是任意的

执行迁移:

PM>update-database

F5 运行将显示种子数据

向 Movie 模型添加 Rating 属性

向 Movie 类添加属性,生成

public string Rating { get; set; }

CreateEdit Action 方法的 Bind 属性添加 Rating

更改各视图支持新的 Rating 属性

此时 F5 运行将提示

System.InvalidOperationException:“支持“MovieDBContext”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/?LinkId=238269)。”

要解决错误除了手动往数据库中添加 Rating 字段外可以利用 Code First 迁移:

更新 Migrations\Configuration.cs 给每个 Movie 对象添加一个 Rating 字段

PM>add-migration Rating

名称 Rating 是任意的

创建了 DbMigration 的派生类 Rating,可以看到添加新列的代码

PM>update-database

数据库自动完成了对新字段的添加,当然 update-database 也会把种子数据还原。

参考

Getting Started with ASP.NET MVC 5

 

xoyozo 8 年前
3,915