QuartZ中的Cron表达式

一个cron 表达式是由6或者7个以空格分隔的字段组成。字段能够包含任何允许值,伴随着特殊字符不同组合。这些字段如下所示:

字段名称 是否一定需要 允许的值 允许的特殊字符串
Seconds YES 0-59 , – * /
Minutes YES 0-59 , – * /
Hours YES 0-23 , – * /
Day of month YES 1-31 , – * ? / L W
Month YES 1-12 or JAN-DEC , – * /
Day of week YES 1-7 or SUN-SAT , – * ? / L #
Year NO empty, 1970-2099 , – * /

可见,一个简单的cron表达式可以如此* * * * ? *表达或者更复杂些,像这样

0/5 14,18,3-39,52 * ? JAN,MAR,SEPMON-FRI 2002-2010

特殊字符串说明

  • * (“所有的值”) – 意味着所有值。
  • ? (“无特殊值”) – 当你需要指定两个字段中的一个时,这个字符很有用。比如,如果我想在一个月中的特别的一天(可以是第10号之类的)触发我的触发器,但是我并不关心它是哪个星期执行。这样的话,可以在月份字段上设置0,星期字段上设置问号(?)。
  • (“指定区间”)-  用来指定区间。比如,“10-12”在小时字段中,这意味着“10点、11点和12点”
  • (“定额外的值”)- 用来指定额外的值。比如,在星期字段中,“MON,WED,FRI”表示“星期一,星期三,星期五”。
  • / (“递增”)-用来指定递增量。比如,在秒字段上,“0/15”表示“0秒、15秒、30秒和45秒”。
  • L (“最后”) –在两个字段上,它们有不同的含义。比如,在天数字段上,“L”值意味着“一个月的最后一天”–平年中2月份的28号,一月的31号等。如果使用在星期 字段中,它简单地意味着“7”或者“星期六”。当使用“L”时,那是很重要的,不去指定列表或者区间,因为你将获得令人困惑的结果。
  • W (“星期”) – 使用来指定星期中最靠近给予的天。例如,我在天数字段上指定了“15W”,这就意味着“这个月离15号最近的某个星期”。如果15号是星期天,它将在周六 的14号触发。如果15好是一个星期二,那么他将在15号的星期二触发。在天字段上,“W”不会跳跃过月份来触发执行。在天数字段上,“L”和“W”能够 结合使用,表示的意思是:“月份的最后一个星期”
  • # (“天数”) –用来指定一个月的某一天。比如,在星期字段上,“6#3”表示“每月的第三个星期五”(6表示星期五,#3表示这个月的第三个)。如果像“#3”指定的话,它将不会触发。

合法的字符串、月份的名字和星期的字母表示,这些都不是大小写敏感的。

下面有一个完全的例子:

0 0 12 * * ? Fire at 12pm (noon) every day
0 15 10 ? * * Fire at 10:15am every day
0 15 10 * * ? Fire at 10:15am every day
0 15 10 * * ? * Fire at 10:15am every day
0 15 10 * * ? 2005 Fire at 10:15am every day during the year 2005
0 * 14 * * ? Fire every minute starting at 2pm and ending at 2:59pm, every day
0 0/5 14 * * ? Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day
0 0/5 14,18 * * ? Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day
0 0-5 14 * * ? Fire every minute starting at 2pm and ending at 2:05pm, every day
0 10,44 14 ? 3 WED Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.
0 15 10 ? * MON-FRI Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday
0 15 10 15 * ? Fire at 10:15am on the 15th day of every month
0 15 10 L * ? Fire at 10:15am on the last day of every month
0 15 10 ? * 6L Fire at 10:15am on the last Friday of every month
0 15 10 ? * 6L Fire at 10:15am on the last Friday of every month
0 15 10 ? * 6L 2002-2005 Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005
0 15 10 ? * 6#3 Fire at 10:15am on the third Friday of every month
0 0 12 1/5 * ? Fire at 12pm (noon) every 5 days every month, starting on the first day of the month.
0 11 11 11 11 ? Fire every November 11th at 11:11am.

 

常用的SQL优化技巧

常见的字段类型选择

•字符类型建议采用varchar/nvarchar数据类型
•金额货币建议采用money数据类型
•科学计数建议采用numeric数据类型
•自增长标识建议采用bigint数据类型   (数据量一大,用int类型就装不下,那以后改造就麻烦了)
•时间类型建议采用为datetime数据类型
•禁止使用text、ntext、image老的数据类型
•禁止使用xml数据类型、varchar(max)、nvarchar(max)

约束与索引

每张表必须有主键

•每张表必须有主键,用于强制实体完整性
•单表只能有一个主键(不允许为空及重复数据)
•尽量使用单字段主键

不允许使用外键

•外键增加了表结构变更及数据迁移的复杂性
•外键对插入,更新的性能有影响,需要检查主外键约束
•数据完整性由程序控制

索引设计准则

•应该对 WHERE 子句中经常使用的列创建索引
•应该对经常用于连接表的列创建索引
•应该对 ORDER BY 子句中经常使用的列创建索引
•不应该对小型的表(仅使用几个页的表)创建索引,这是因为完全表扫描操作可能比使用索引执行的查询快
•单表索引数不超过6个
•不要给选择性低的字段建单列索引
•充分利用唯一约束
•索引包含的字段不超过5个(包括include列)

不要给选择性低的字段创建单列索引

•SQL SERVER对索引字段的选择性有要求,如果选择性太低SQL SERVER会放弃使用•
•不适合创建索引的字段:性别、0/1、TRUE/FALSE
•适合创建索引的字段:ORDERID、UID等

充分利用唯一索引

唯一索引给SQL Server提供了确保某一列绝对没有重复值的信息,当查询分析器通过唯一索引查找到一条记录则会立刻退出,不会继续查找索引

•表索引数不超过6个, 表索引数不超过6个(这个规则只是携程DBA经过试验之后制定的。。。)
•索引加快了查询速度,但是却会影响写入性能
•一个表的索引应该结合这个表相关的所有SQL综合创建,尽量合并
•组合索引的原则是,过滤性越好的字段越靠前
•索引过多不仅会增加编译时间,也会影响数据库选择最佳执行计划

SQL查询

•禁止在数据库做复杂运算
•禁止使用SELECT *
•禁止在索引列上使用函数或计算
•禁止使用游标
•禁止使用触发器
•禁止在查询里指定索引
•变量/参数/关联字段类型必须与字段类型一致
•参数化查询
•限制JOIN个数
•限制SQL语句长度及IN子句个数
•尽量避免大事务操作
•关闭影响的行计数信息返回
•除非必要SELECT语句都必须加上NOLOCK
•使用UNION ALL替换UNION
•查询大量数据使用分页或TOP
•递归查询层级限制
•NOT EXISTS替代NOT IN
•临时表与表变量
•使用本地变量选择中庸执行计划
•尽量避免使用OR运算符
•增加事务异常处理机制
•输出列使用二段式命名格式

禁止在数据库做复杂运算

•XML解析
•字符串相似性比较
•字符串搜索(Charindex)
•复杂运算在程序端完成

禁止使用SELECT *

•减少内存消耗和网络带宽
•给查询优化器有机会从索引读取所需要的列
•表结构变化时容易引起查询出错

•禁止在索引列上使用函数或计算

禁止在索引列上使用函数或计算

假设在字段Col1上建有一个索引,则下列场景将可以使用到索引:

[Col1]=3.14

[Col1]>100

[Col1] BETWEEN 0 AND 99

[Col1] LIKE ‘abc%’

[Col1] IN(2,3,5,7)

像上面这样的查询,将无法用到O_OrderProcess表上的PrintTime索引

LIKE查询的索引问题

•[Col1] like “abc%”  –index seek  这个就用到了索引查询
•[Col1] like “%abc%”  –index scan  而这个就并未用到索引查询
•[Col1] like “%abc”  –index scan 这个也并未用到索引查询
我想从上而三个例子中,大家应该明白,最好不要在LIKE条件前面用模糊匹配,否则就用不到索引查询。

禁止使用游标

•关系数据库适合集合操作,也就是对由WHERE子句和选择列确定的结果集作集合操作,游标是提供的一个非集合操作的途径。一般情况下,游标实现的功能往往相当于客户端的一个循环实现的功能。
•游标是把结果集放在服务器内存,并通过循环一条一条处理记录,对数据库资源(特别是内存和锁资源)的消耗是非常大的。
(再加上游标真心比较复杂,挺不好用的,尽量少用吧)

禁止使用触发器

•触发器对应用不透明(应用层面都不知道会什么时候触发触发器,发生也也不知道,感觉莫名其秒)

禁止在查询里指定索引

With(index=XXX)( 在查询里我们指定索引一般都用With(index=XXX) )

•随着数据的变化查询语句指定的索引性能可能并不最佳
•索引对应用应是透明的,如指定的索引被删除将会导致查询报错,不利于排障
•新建的索引无法被应用立即使用,必须通过发布代码才能生效

变量/参数/关联字段类型必须与字段类型一致(这是我之前不太关注的)

避免类型转换额外消耗的CPU,引起的大表scan尤为严重

看了上面这两个图,我想我不用解释说明,大家都应该已经清楚了吧。

如果数据库字段类型为VARCHAR,在应用里面最好类型指定为AnsiString并明确指定其长度

如果数据库字段类型为CHAR,在应用里面最好类型指定为AnsiStringFixedLength并明确指定其长度

如果数据库字段类型为NVARCHAR,在应用里面最好类型指定为String并明确指定其长度

参数化查询

以下方式可以对查询SQL进行参数化:

•sp_executesql
•Prepared Queries
•Stored procedures
用图来说明一下,哈哈。

限制JOIN个数

•单个SQL语句的表JOIN个数不能超过5个
•过多的JOIN个数会导致查询分析器走错执行计划
•过多JOIN在编译执行计划时消耗很大

限制IN子句中条件个数

•在 IN 子句中包括数量非常多的值(数以千计)可能会消耗资源并返回错误 8623 或 8632,要求IN子句中条件个数限制在100个以内

尽量避免大事务操作

•只在数据需要更新时开始事务,减少资源锁持有时间
•增加事务异常捕获预处理机制
•禁止使用数据库上的分布式事务
用图来说明一下
也就是说我们不应该在1000行数据都更新完成之后再commit tran,你想想你在更新这一千行数据的时候是不是独占资源导致其它事务无法处理。

关闭影响的行计数信息返回

在SQL语句中显示设置Set Nocount On,取消影响的行计数信息返回,减少网络流量

除非必要,尽量让所有的select语句都必须加上NOLOCK

指定允许脏读。不发布共享锁来阻止其他事务修改当前事务读取的数据,其他事务设  置的排他锁不会阻碍当前事务读取锁定数据。允许脏读可能产生较多的并发操作,但其代价是读取以后会被其他事务回滚的数据修改。这可能会使您的事务出错,向 用户显示从未提交过的数据,或者导致用户两次看到记录(或根本看不到记录)

使用UNION ALL替换UNION

UNION会对SQL结果集去重排序,增加CPU、内存等消耗

查询大量数据使用分页或TOP

合理限制记录返回数,避免IO、网络带宽出现瓶颈

递归查询层次限制

使用 MAXRECURSION 来防止不合理的递归 CTE 进入无限循环

临时表与表变量

使用本地变量选择中庸执行计划

在存储过程或查询中,访问了一张数据分布很不平均的表格,这样往往会让存储过程或查询使用了次优甚至于较差的执行计划上,造成High CPU及大量IO Read等问题,使用本地变量防止走错执行计划。

采用本地变量的方式,SQL在编译的时候是不知道这个本地变量的值,这时候SQL会根据表格里数据的一般分布,“猜测”一个返回值。不管用户在调用存储过程或语句的时候代入的变量值是多少,生成的计划都是一样的。这样的计划一般会比较中庸一些,不一定是最优的计划,但一般也不会是最差的计划

如果查询中本地变量使用了不等式运算符,查询分析器使用了一个简单的 30% 的算式来预估
Estimated Rows =(Total Rows * 30)/100
如果查询中本地变量使用了等式运算符,则查询分析器使用:精确度 * 表记录总数来预估
Estimated Rows = Density * Total Rows

尽量避免使用OR运算符

对于OR运算符,通常会使用全表扫描,考虑分解成多个查询用UNION/UNION ALL来实现,这里要确认查询能走到索引并返回较少的结果集

增加事务异常处理机制

应用程序做好意外处理,及时做Rollback。
设置连接属性 “set xact_abort on”

输出列使用二段式命名格式

二段式命名格式:表名.字段名

有JOIN关系的TSQL,字段必须指明字段是属于哪个表的,否则未来表结构变更后,有可能发生Ambiguous column name的程序兼容错误

架构设计

•读写分离
•schema解耦
•数据生命周期

读写分离

•设计之初就考虑读写分离,哪怕读写同一个库,有利于快速扩容
•按照读特征把读分为实时读和可延迟读分别对应到写库和读库
•读写分离应该考虑在读不可用情况下自动切换到写端

Schema解耦

禁止跨库JOIN

数据生命周期

根据数据的使用频繁度,对大表定期分库归档

主库/归档库物理分离

日志类型的表应分区或分表

对于大的表格要进行分区,分区操作将表和索引分在多个分区,通过分区切换能够快速实现新旧分区替换,加快数据清理速度,大幅减少IO资源消耗

频繁写入的表,需要分区或分表

自增长与Latch Lock

闩锁是sql Server自己内部申请和控制,用户没有办法来干预,用来保证内存里面数据结构的一致性,锁级别是页级锁

C# 中发送和接收MSMQ的一个示例

Ok, 直接上代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication3
{
    public class Form1 : Form
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.label1 = new System.Windows.Forms.Label();
            this.button3 = new System.Windows.Forms.Button();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(198, 52);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(143, 23);
            this.button1.TabIndex = 0;
            this.button1.Text = "Send Message";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(12, 52);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(157, 23);
            this.button2.TabIndex = 1;
            this.button2.Text = "Received Message";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(151, 94);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(31, 13);
            this.label1.TabIndex = 2;
            this.label1.Text = "none";
            // 
            // button3
            // 
            this.button3.Location = new System.Drawing.Point(366, 52);
            this.button3.Name = "button3";
            this.button3.Size = new System.Drawing.Size(157, 23);
            this.button3.TabIndex = 3;
            this.button3.Text = "Stop Receiveing";
            this.button3.UseVisualStyleBackColor = true;
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(12, 94);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(133, 13);
            this.label2.TabIndex = 4;
            this.label2.Text = "Received a new message:";
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(565, 146);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.button3);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button button3;
        private System.Windows.Forms.Label label2;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            System.Messaging.MessageQueue mq;

            // 如果不存在google这个队列, 那么创建这个队列
            if (System.Messaging.MessageQueue.Exists(".\\google"))
            {
                mq = new System.Messaging.MessageQueue(".\\google");
            }
            else
                mq = System.Messaging.MessageQueue.Create(".\\google");
            mq.Purge();// 将队列清空
            // 向队列中发送一条消息, 1个随机的数字, 并使用XML序列化进行传输
            mq.Send(new System.Messaging.Message(new Random().NextDouble(), new System.Messaging.XmlMessageFormatter()));

        }



        private void Fn()
        {
            var mq = new System.Messaging.MessageQueue(".\\google")
            {
                Formatter = new System.Messaging.XmlMessageFormatter(new[] { typeof(double) })
            };
            while (true)
            {
                var message = mq.Receive();
                if (null != message)
                    Invoke(Convert.ToString(message.Body));
                Thread.Sleep(100);
            }

        }

        private delegate void Do(string str);


        private void Set(string str)
        {
            label1.Text = str;
        }

        // 默认情况下, Window form是不允许被非创建它的线程进行访问的, 所以要通过delegate进行安全调用
        // 可以通过设置 Control.CheckForIllegalCrossThreadCalls = false; 来取消跨线程调用的限制
        private void Invoke(string str)
        {
            var foo = new Do(Set);
            // 可以看到: 是label1 这个控件去调用foo, 是种主动行为. 这样就不会引用跨线程调用异常
            label1.Invoke(foo, str);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 创建一个新的线程去接收队列中的消息
            var ts = new ThreadStart(Fn);
            var th = new Thread(ts);
            MessageBox.Show(@"Start Receiving");
            th.Start();
        }
    }
}

有几个点可以说一下:

1. 首先得有队列, 在没有队列的情况下, new System.Messaging.MessageQueue(“.\\google”) 是会有异常的, 所以得先判断和创建队列通道:

            System.Messaging.MessageQueue mq;

            // 如果不存在google这个队列, 那么创建这个队列
            if (System.Messaging.MessageQueue.Exists(".\\google"))
            {
                mq = new System.Messaging.MessageQueue(".\\google");
            }
            else
                mq = System.Messaging.MessageQueue.Create(".\\google");

2. window form 默认情况下不可被非创建它的线程进行访问. 这个问题, 简单点的方法是允许它被其它线程访问:

Control.CheckForIllegalCrossThreadCalls = false;

但是这样其实会有一个跨线程访问的风险存在, 是非安全的. 建议的方法是通过delegate的方法, 让控件自己去invoke需要调用的方法, 是一种由被动修改变为主动出击的方式.

C# volatile

关于volatile关键字, 大家用的可能不多,  因为经常大家用的时候都是使用的lock, 或者其它的线程锁, 来避免并发时对变量的更改, 引用的不同步.

volatile 关键字, 是一个轻量级的lock, 它会进行更多的优化, 具体的优化哪些东西我也不清楚, 性能也要比lock方式好. 所以一般情况下, 使用volatile即可. 下面摘取msdn 上对于这个关键字的描述:

volatile 关键字指示一个字段可以由多个同时执行的线程修改。 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。

volatile 修饰符通常用于由多个线程访问但不使用 lock 语句对访问进行序列化的字段。

using System;
using System.Threading;

public class Worker
{
    // This method is called when the thread is started.
    public void DoWork()
    {
        while (!_shouldStop)
        {
            Console.WriteLine("Worker thread: working...");
        }
        Console.WriteLine("Worker thread: terminating gracefully.");
    }
    public void RequestStop()
    {
        _shouldStop = true;
    }
    // Keyword volatile is used as a hint to the compiler that this data
    // member is accessed by multiple threads.
    private volatile bool _shouldStop;
}

public class WorkerThreadExample
{
    static void Main()
    {
        // Create the worker thread object. This does not start the thread.
        Worker workerObject = new Worker();
        Thread workerThread = new Thread(workerObject.DoWork);

        // Start the worker thread.
        workerThread.Start();
        Console.WriteLine("Main thread: starting worker thread...");

        // Loop until the worker thread activates.
        while (!workerThread.IsAlive) ;

        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work.
        Thread.Sleep(1);

        // Request that the worker thread stop itself.
        workerObject.RequestStop();

        // Use the Thread.Join method to block the current thread 
        // until the object's thread terminates.
        workerThread.Join();
        Console.WriteLine("Main thread: worker thread has terminated.");
    }
    // Sample output:
    // Main thread: starting worker thread...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: working...
    // Worker thread: terminating gracefully.
    // Main thread: worker thread has terminated.
}

 

VIM 基本命令

VIM For windows默认安装后,在桌面上会有2个Shortlink, 但是这2个link都是带有参数的, 一个是/y, 进去以后就是insert模式, 非常不方便。 一个是/R, 进去以后永远是Readonly, 无法更改数据。 所以我们需要直接将gvim.exe建立快捷方式至桌面, 并且copy 一份到system32, 以便于命令行直接启动。

Linux下的vim命令, 大多在windows下也可以使用, 这里截取了一段来自网上的Vim commands.

Continue reading “VIM 基本命令”

x509

x509证书一般会用到三类文件,key,csr,crt。

Key是私用密钥,openssl格式,通常是rsa算法。

csr是证书请求文件,用于申请证书。在制作csr文件的时候,必须使用自己的私钥来签署申请,还可以设定一个密钥。

crt是CA认证后的证书文件(windows下面的csr,其实是crt),签署人用自己的key给你签署的凭证。

1.key的生成

openssl genrsa -des3 -out server.key 2048

这样是生成rsa私钥,des3算法,openssl格式,2048位强度。server.key是密钥文件名。为了生成这样的密钥,需要一个至少四位的密码。可以通过以下方法生成没有密码的key:

openssl rsa -in server.key -out server.key

server.key就是没有密码的版本了。

2.生成CA的crt

openssl req -new -x509 -key server.key -out ca.crt -days 3650

生成的ca.crt文件是用来签署下面的server.csr文件。

3.csr的生成方法:

openssl req -new -key server.key -out server.csr

需要依次输入国家,地区,组织,email。最重要的是,有一个common name,可以写你的名字或者域名。如果为了https申请,这个必须和域名吻合,否则会引发浏览器警报。生成的csr文件交给CA签名后形成服务端自己的证书。

4.crt生成方法

CSR文件必须有CA的签名才可形成证书. 可将此文件发送到verisign等地方由它验证,要交一大笔钱,何不自己做CA呢.

openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey server.key

-CAcreateserial -out server.crt

输入key的密钥后,完成证书生成。

-CA 选项指明用于被签名的csr证书,

-CAkey 选项指明用于签名的密钥。

-CAserial 指明序列号文件,而-CAcreateserial指明文件不存在时自动生成。

最后生成了私用密钥:server.key和自己认证的SSL证书:server.crt

Ubuntu+LAMP+Nginx

好久没有玩linux了, 下午的时候有点时间, 搭了一套环境.

使用的VMware workstation安装的Ubuntu 12 Server, 网络连接使用的NAT, Remove掉了Floppy.

Host是Intel Core i5-4570 3.2GHz的64Bit CPU, Memory 是4G. 配置还可以, 故给VMware配置了1G的Memory, 20G的Disk.

Ubuntu的安装过程现在是越来越简单了, 过程也记不清了, 这里就不说了.  总之顺利安装成功.

Host 的IP是 192.168.82.81, Client的IP是192.168.206.129.

因为Company里有Security Audit, 所以需要通过Proxy才能上外网, 故第一步给Ubuntu设置代理服务器.

editor /etc/profile

添加:

http_proxy=http://192.168.88.80:3128
https_proxy=http://192.168.88.80:3128
ftp_proxy=http://192.168.88.80:3128
export http_proxy https_proxy ftp_proxy

然后Reboot.

OK, 可以上外网了. 现在开始更新apt源, 由于默认安装后的源是Ubuntu在国外的服务器, 非常慢, 所以先更改源地址:

editor /etc/apt/sources.list

然后在所有的archive.ubuntu.com前加上cn., 即: http://cn.archive.ubuntu.com/ubuntu/ , cn.archive.ubuntu.com 是由阿里巴巴维护的, 放在阿里云上, 速度是很快的.

下面开始 update source list:

atp-get update

下面开始安装VIM, 使用Editor编辑器实在不顺手:

apt-get install vim

下面开始安装LAMP:

tasksel install lamp-server

下面开始安装Nginx:

apt-get install nginx

下面开始安装phpMyAdmin:

apt-get install phpmyadmin

至此nginx+lamp+phpmyadmin安装结束. 现在要开始进行配置.

因为nginx会占用80端口, 所以apache将无法启动, 因为nginx是进行对外公布的load blancing, 所以nginx会占用80和443 两个端口. 所以先来修改apache的端口, 以及启用apache对于SSL的支持.

vim /etc/apache/prots.conf

将apache的监听http协议的端口改为8080, SSL协议的端口改为4433

NameVirtualHost *:8080
Listen 8080
NameVirtualHost *:4433

<IfModule mod_ssl.c>
Listen 4433
</IfModule>
<IfModule mod_gnutls.c>
Listen 4433
</IfModule>

然后切换目录到/etc/apache2/mods-enabled/

cd /etc/apache2/mods-enabled/

执行命令下面命令, 以启用apache对于SSL的支持:

ln -s ../mods-available/ssl.conf
ln -s ../mods-available/ssl.load

然后将apache自带的ssl站点进行启用:

cd /etc/apache2/sites-enabled/
ln -s ../sites-available/default-ssl

然后对000-default和default-ssl这2个virtual host进行端口更改:

vi /etc/apache2/sites-enabled/000-default
vi /etc/apache2/sites-enabled/default-ssl

将000-default中的VirtualHost *:80改为VirtualHost *:8080, 将default-ssl中的VirtualHost *:443改为VirtualHost *:4433

在Host上使用IE打开Http://192.168.206.129:8080和https://192.168.206.129:4433, 如果都可以打开, 说明apache的配置成功.

现在来配置nginx, 使其进行反向代理和负载均衡:

vi /etc/nginx/sites-enabled/default

需要分别配置http和https的load blancing, 其中192.168.82.81是Host上安装的IIS:

upstream loadblancing{
server 192.168.82.81:80;
server 127.0.0.1:8080;
}

upstream loadblancingssl{
server 192.168.82.81:443;
server 127.0.0.1:4433;
}

然后在server节点里, 将location节点修改为:

location /{
proxy_pass http://loadblancing;
}

然后定位到文件末页, 将关于Https Server的注释符号”#” 全拿掉, 使其启用.

并且将其location节点修改为:

location / {
https://loadblancingssl;
}

另外, 需要将ssl证书和key的位置进行修改, 在后面我们需要创建open ssl 证书:

ssl_certificate /root/server.crt;
ssl_certificate_key /root/server.key;

现在来创建openssl证书, 首先来创建私钥, 创建私钥的过程中会要求输入密码:

openssl
genrsa -aes256 -out server.key 1024

创完私钥, 来创建证书的请求文件:

openssl
req -new -key server.key -out server.csr

请求文件创建成功后, 我们来把私钥做个备份:

cp server.key server.key.bak

我们把带密码的key取消掉口令, 以便Nginx可以正常使用:

openssl
rsa -in server.key.bak -out server.key

然后我们使用x509来颁发证书, 得到证书文件server.crt :

openssl
x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

现在我们来重启Nginx:

/etc/init.d/nginx restart

OK, 结束. 现在可以在host上打开IE, 浏览http://192.168.206.129和https://192.168.206.129, 会发现相同的URL在点击刷新后, 不断出现不同的页面, 这是因为一个是Client上的Apache首页, 一个是Host上的IIS首页.

 

统计信息, SQLServer自动更新和自动创建统计信息选项

统计信息是如何提高SQLServer查询性能的?统计直方图用作在查询执行计划中查询优化器的选择依据。如果一个查询谓词包含统计信息的列,那么查询优 化器不需要预测该查询中影响行数,因此,查询优化器有足够的信息去创建执行计划。SQLServer创建执行计划有一下几种不同的方式:

  • 统计信息会在每个新创建的索引中自动创建统计信息。
  • 如果数据库中AUTO_CREATE_STATISTICS被设置为ON,SQLServer将会自动对查询中用到的,且没有索引的列自动创建统计信息。

AUTO_CREATE_STATISTICS选项:

当把该选项设为ON时,查询优化器会对在谓词中使用的到列,如果这些列的统计信息不可用,则会单独对每列创建统计信息。这些统计信息对创建一个查询 计划非常必要。它们创建于那些现有统计对象中不存在直方图的列上,名字包括列名和对象ID的十六进制格 式:_WA_Sys_<column_name>_<XXXX>。这些统计信息用于查询优化器决定使用何种优化后的执行计划。

可以通过以下语句启用自动统计信息创建功能:

ALTER  DATABASE[你的库名]

SET AUTO_CREATE_STATISTICS ON

Auto Update Statistics选项:

统计信息会在查询编译或者执行缓存执行计划前被检查。当在以下情况下,统计信息会被认为过期:

1、  在一个空表中有数据的改动。

2、  当统计信息创建时,表的行数只有500或以下,且后来统计对象中的引导列的更改次数大于500.

3、  当表的统计信息收集时,超过了500行,且统计对象的引导列后来更改次数超过500+表总行数的20%时。

4、  在Tempdb中的表,少于6行且最少有6行被更改。 Continue reading “统计信息, SQLServer自动更新和自动创建统计信息选项”