最近研究了一下使用.netcore的Lucene.Net應用,整理一下研究內容。
一、研究原因
1、系統環境:多終端、多服務、達夢數據庫8、.netcore7.0、多機負載。
2、業務數量:單表1000萬條數據,有7張的業務表或功能表有千萬數據。
3、問題描述:業務表的字段較多,后臺管理系統上,對數據查詢的準確度與速度有要求,查詢的組合字段較多,數據表上創建的索引無法完全覆蓋所有的組合情況。
二、預期目標
1、有效提高查詢速度。
2、持久化數據庫與索引庫保持一致。
3、減少對當前系統結構的影響,盡量降低改造的工作量。
4、可以進行索引庫的數據量、待入索引庫的數據、索引庫查詢速度等監控。
三、應用結構
1、因索引庫只能單線程寫入,可以多線程查詢,所以在系統外新增索引寫入服務,使多業務并發的狀態形成單線程處理,根據業務表的類型,可以創建多個線程存入不同的索引庫,同一索引庫只有一個服務寫入。
2、數據庫增加觸發器,當業務表增、刪、改的時候,自動將業務表ID的操作存入隊列表,使用觸發器的方式避免多終端多服務的數據處置,保證業務表和索引庫的數據一致。
四、實驗結果
1、使用存儲不分詞索引導入1000萬數據索引,檢測多組合查詢速度為1秒內。
2、業務表數據和索引庫的數據可以保持一致。
五、代碼樣例
1、添加NuGet
1、達夢數據庫 FreeSql.Provider.Dameng 3.5.213 2、索引 Lucene.Net 3.0.3 3、配置 System.Configuration.ConfigurationManager 9.0.9
2、寫入索引庫
using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Store; using Lucene.Net.Util; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Reflection.Metadata; using System.Data; using Dm; using static FreeSql.Internal.GlobalFilter; using Lucene.Net.Search; using Lucene.Net.QueryParsers; using System.Runtime.ConstrainedExecution; using System.Reflection.PortableExecutable; class Program { static void Main(string[] args) { Analyzer analyzer = null; IndexWriter writer = null; Lucene.Net.Store.Directory indexDirectory = null; try { indexDirectory = FSDirectory.Open(new System.IO.DirectoryInfo("D:\\LuceneIndex")); analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30); bool iscreate = !Lucene.Net.Index.IndexReader.IndexExists(indexDirectory); writer = new IndexWriter(indexDirectory, analyzer, iscreate, IndexWriter.MaxFieldLength.UNLIMITED); string connectionString = "Server=LOCALHOST;Database=SYSDBA;User Id=SYSDBA;Password=123456;"; using (DmConnection connection = new DmConnection(connectionString)) { connection.Open(); using (DmCommand command = new DmCommand("SELECT reg_id,reg_name,reg_sex,reg_datetime,reg_status FROM reg_infor", connection)) { using (DmDataAdapter adapter = new DmDataAdapter(command)) { DataTable table = new DataTable(); adapter.Fill(table); int i = 0; foreach (DataRow row in table.Rows) { i++; Lucene.Net.Documents.Document doc = new Lucene.Net.Documents.Document(); doc.Add(new Field("reg_id", row["reg_id"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,不分詞索引 doc.Add(new Field("reg_name", row["reg_name"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,不分詞索引 doc.Add(new Field("reg_sex", row["reg_sex"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,分詞索引 doc.Add(new Field("reg_datetime", row["reg_datetime"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,分詞索引 doc.Add(new Field("reg_status", row["reg_status"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); writer.AddDocument(doc); writer.Commit();//.Optimize(); } } } connection.Close(); } } catch (Exception ex) { throw; } finally { if (analyzer != null) analyzer.Close(); if (writer != null) writer.Dispose();//之前為:writer.Close(); if (indexDirectory != null) indexDirectory.Dispose();//之前使用Close(); } Console.WriteLine("索引創建成功!"); Console.ReadLine(); } }
3、數據查詢
using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Store; using Lucene.Net.Util; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.IO; using System.Reflection.Metadata; using System.Data; using Dm; using static FreeSql.Internal.GlobalFilter; using Lucene.Net.Search; using Lucene.Net.QueryParsers; using System.Runtime.ConstrainedExecution; using System.Reflection.PortableExecutable; class Program { static void Main(string[] args) { Analyzer analyzer = null; IndexWriter writer = null; Lucene.Net.Store.Directory indexDirectory = null; try { indexDirectory = FSDirectory.Open(new System.IO.DirectoryInfo("D:\\LuceneIndex")); analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30); var directory = FSDirectory.Open("D:\\LuceneIndex"); var reader = DirectoryReader.Open(directory, true); var searcher = new IndexSearcher(reader); int numDocs = reader.NumDocs(); var booleanQuery = new BooleanQuery(); booleanQuery.Add(new TermQuery(new Term("reg_status", "1")), Occur.MUST); booleanQuery.Add(new TermQuery(new Term("reg_sex", "1")), Occur.MUST); var hits = searcher.Search(booleanQuery, 10); foreach (var hit in hits.ScoreDocs) { var doc = searcher.Doc(hit.Doc); Console.WriteLine($"ID: {doc.Get("reg_id")}, Name: {doc.Get("reg_name")}"); } } catch (Exception ex) { throw; } finally { if (analyzer != null) analyzer.Close(); if (writer != null) writer.Dispose();//之前為:writer.Close(); if (indexDirectory != null) indexDirectory.Dispose();//之前使用Close(); } Console.WriteLine("索引查詢成功!"); Console.ReadLine(); } }
4、觸發器
---新增數據觸發器---
CREATE TRIGGER trg_after_insert_reginfor AFTER INSERT ON reg_infor FOR EACH ROW BEGIN insert into SYSDBA.ACTION_LOG("LOG_ID", "BIN_ID", "ACTION_TYPE", "ACTION_STATUS", "CREATE_TIME", "ACTION_TIME") VALUES (REPLACE(SYS_GUID(), '-', ''),NEW."reg_id", 1, 0,TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'),''); END;
---修改數據觸發器---
CREATE TRIGGER trg_after_update_reginfor AFTER UPDATE ON reg_infor FOR EACH ROW BEGIN insert into SYSDBA.ACTION_LOG("LOG_ID", "BIN_ID", "ACTION_TYPE", "ACTION_STATUS", "CREATE_TIME", "ACTION_TIME") VALUES (REPLACE(SYS_GUID(), '-', ''),NEW."reg_id", 2, 0,TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS'),''); END;
浙公網安備 33010602011771號