笔记
随缘笔记
模糊查询 like %a 全表扫描 效率低 匹配程度差
站内搜索 用Lucene.Net实现
1、只是一个全文检索开发包,不是一个成型的搜索引擎
2、数据(文本类型)扔给Lucene.Net,查询数据的时候从Lucene.Net查询数据(可以看作是提供了全文检索功能的一个数据库)
文章-->分词(可选)-->索引库<--提取字符串中的关键词(分词)(可选)--<用户输入字符串
分词存储 查找 性能高、速度快
默认 集成了 一元分词
其他分词算法:二元分词、盘古分词(基于词库的分词算法,效率低,需要查词库)等
使用盘古分词:引用dll + 词库(始终复制到输出目录/较新则复制)(默认读取bin下的Dict文件夹)(可以xml更改配置)
创建索引把数据写入Lucene.Net,可指定存入磁盘的位置,分词算法)(IndexWriter)
1、拿到文本数据
2、写入Document(Lucene.Net特有对象)---document.Add(new Field("字段名", "内容", 是否能Get, 是否分词保存, 词间距)) 可以有多个字段
(分词保存,效率会很高,但是匹配度不能保证,与分词算法有关)
比如说 面向12345对象 面向对象的词间距就是5
搜索(IndexReader和IndexSearcher)
1、读取索引库,并依次new出reader、searcher
2、拿到用户输入的字符串s
3、新建搜索条件(PhraseQuery query)(query.Add(new Term("字段名", s)))//这样就会搜索字段名对应的内容中含有s的文章
可限制词间距范围query.SetSlop(5);
4、searcher查询 把查询结果放到一个容器
5、通过容器得到所有符合查询条件的文档数组docs(此时的文档对象并没有具体内容,但是有ID)
(这样就不会把所有文档一次性加载到内存中,降低内存压力) 联想SqlDataReader
6、遍历文档数组(doc.Get("字段名"))
int docID = docs[i].doc;//得到查询结果文档的id(Lucene内部分配的id)
Document doc = searcher.Doc(docID);//找到文档id对应的文档详细信息(此时的文档是原汁原味的文档)
doc.Get("字段名"); //添加文档的时候,字段要设置成允许Get(Field.Store.Yes)
并发解决
操作(抽出一个类)放到队列,另开线程执行队列里的操作
将路径写到配置文件中~~~
从数据库更新模型(记得转换所有T4模板)
PhraseQuery这个类 只能搜索单一字段 可以添加多个关键词
query.Add(new Term("字段名AAA", s1)
query.Add(new Term("字段名AAA", s2)
ViewBag.UserInfo = userinfo;
return View("Index") 返回Index视图并且ViewBag.UserInfo也会传递过去
搜索用get请求 这样url地址就有搜索条件了~~
public class ViewModelContent
{
id、标题、摘要、标记(枚举类型:Add/Delete)
}
1、Queue<ViewModelContent> queue
2、插入操作 queue.Enqueue
3、开启线程(while true)(判断queue.Count(),没有东西则Sleep(3000))
4、若有 while queue.Count() > 0
queue.Dequeue()
5、删除(即使删除不存在的索引,也不报错,所以才能先删后奏)
6、判断ViewModelContent的标记,若为删除则continue;若为添加则继续
7、添加,即创建索引
8、在Global.asax中开启线程
注意有两个地方都判断queue.Count();
第一次是判断队列是否为空(是否能消费)(还没打开索引库)
第二次是清空队列(消费者,一直消费)(已经打开索引库了)
当点击文章表单的提交按钮时,同时调用Lucene添加方法,添加到Lucene.Net索引库(添加到数据库-->添加到索引库)
还有一个亮点是:更新 = 删除 + 插入 (所以编辑文章后,也是调用添加方法,不用写Update方法)
打标记 此时又想到了 XMLHttpRequest的readyState 01234
这里用单例模式就行了,不管是谁,都是写到同一个队列
饿汉模式
public sealed class Singleton
private static readonly Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton GetInstance()
{
return instancs;
}
在这种模式下,无需自己解决线程安全性问题,CLR会给我们解决。
一)使用static和readonly关键字构造单例的原由
使用这两个关键字的原因主要基于它们有以下几个特征,虽然它们还有其他很多特征,但是理解以下几个特征会更容易理解如何构造单例。
1.static关键字的几个重要特征:
1)静态成员只被创建一次,静态成员是属于类而不是实例对象
2)一个类即使被创建多个实例,它的静态成员都只有一个
3)静态方法只能被重载,而不能被重写
2.readonly关键字的重要特征
1)当用readonly标记引用类型的字段时,不可改变字段的引用,而引用的对象是可以被改变的
所以使用static和readonly修饰一个成员就可以理解为:
这个成员只被创建一次,并且它指向的地址被固定不可被更改,但地址上的对象是可以被更改的。
懒汉模式
if null-->lock-->if null-->new
使用双重锁方式解决了多线程下的单例模式实现(怎么感觉这句话怪怪的,是双重if null吗?)
为什么要sealed呢?
1、单例模式说明外界(包括子类)不能new,那么就没有public/protected的构造函数
2、没有以上的构造函数,那子类很明显调用不到父类的构造函数,那么就会报错
3、所以用sealed修饰,避免无意继承,出现报错
i++不是原子操作
i++,这语句实际上是执行了三个步骤 ,
1、从内存中读取i的值,
2、i加上1
3、将i保存到内存.
如果第一个线程还没有把加一之后的i值保存到内存,这时候第二个线程进来,读取的值,也是第一线程读取的值,这时候就出问题.
线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线
程都可以正常且正确的执行,不会出现数据污染等意外情况。
线程安全问题大多是由全局变量及静态变量引起的,局部变量逃逸也可能导致线程安全问题。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考
虑线程同步,否则的话就可能影响线程安全。
"原子操作(atomic operation)是不需要synchronized",这是多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作
一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
串行:必须吃完饭,才去接电话;
并行:一边吃饭,一边打电话;
并发:先吃饭,电话来了接电话(不一定吃完饭),电话结束再去吃饭。
1.5非阻塞同步
非阻止同步:不阻止其他线程的情况下实现同步。就是利用原子性操作实现线程间的同步,不刻意阻塞线程,减少相应线程的开销,interlocked类
便是c#中非阻止同步的理念所产生的线程同步技术。
1.6阻塞同步
阻止同步:阻止其他线程,同一时间只允许单个线程访问临界资源。其实阻止同步也是基元内核模式的特点之一。
5.3 volatile关键字
这是为简化VolatileWrite和VolatileRead的编程而提供的关键字。对于标注为volatile的变量:就是表明该变量可能会被多线程访问,每次访问该变量
都应从内存中读取新值,而不应该用寄存器中保留的值。对于多cpu的机器而言,每个cpu的寄存器可能都需要刷新,在一定程度上会损害部分性能。(
上面的几个方法同样会有这个问题)
Quaryz.Net定时任务框架
计划者(IScheduler)、工作(IJop)、触发器(Trigger)
因为无需用户请求,最好另建一个控制台程序/Web服务
热词统计
1、明细表和统计表
2、插入明细表
3、Quartz.Net定时工作(在服务器空闲的时间段,如:凌晨)
清空统计表-->group by 明细表.关键词-->插入统计表(这里简单用一下,每隔5分钟就触发一次)
4、接收用户搜索词并返回统计表集合(这里用到了jquery-ui的AutoComplete,会自动发送用户输入的内容,?term=内容,返回的是Json数组)
5、用户点击搜索,返回搜索结果的时候,记得把关键词插入明细表
truncate table 统计表 + 插入(可以筛选,where DateDiff(day, 明细表.插入时间, geydate()) <= 3 group by 明细表.关键词)
insert into KeyWordsRank select newid(), SearchDetails.KeyWords, count(*) from SearchDetails where DateDiff(day,
SearchDetails.SearchDateTime, getdate()) <= 7 group by SearchDetails.KeyWords
return EF.Database.EexcuteSqlCommand(sql语句, params 参数); //对数据库执行给定的DDL/DML命令,返回影响行数
return EF.Database.SqlQuery<T>(sql语句, params参数).ToList();//这样就能返回List<T>集合 再.ToArray()返回数组
主键ID设置成GUID类型 性能比自增高~ 插入自增列的时候,会加锁
SqlServer 生成GUID newid()
new SqlParameter("@4", SqlDbType.Int) { Direction = ParameterDirection.Output }