MSDN.WhiteKnight - Stack Overflow answers
Ответ на "OleDb откуда происходит чтение при OleDBCommand.Read()"
Answer 949545
Метод Read может считывать данные как из промежуточного буфера в памяти, так и напрямую из файла. Конкретные детали определяются используемой СУБД. Так как OLE DB чаще всего используют для работы с MS Access, имеет смысл рассмотреть именно его.
Для отслеживания обращений к файлу можно использовать механизм Event Tracing for Windows: Kernel tracing provider предоставляет событие FileIo_ReadWrite. Для использования ETW в C# подключим библиотеку Microsoft.Diagnostics.Tracing.TraceEvent. Напишем такой код для считывания 10 строк из БД и логирования событий чтения из файла (так как ETW выдает события с задержкой около секунды, между отдельными этапами работы с БД добавлены паузы, чтобы можно было отличить, какие события к чему относятся):
using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.Threading; using Microsoft.Diagnostics.Tracing.Parsers; using Microsoft.Diagnostics.Tracing.Session; using System.Data.OleDb; namespace ConsoleApp1 { class Program { struct Timestamp : IComparable { public Timestamp(long t, string s) { tvalue = t; descr = s; } public long tvalue { get; set; } public string descr { get; set; } public int CompareTo(object obj) { return (int)(this.tvalue - ((Timestamp)obj).tvalue ); } public override string ToString() { return tvalue.ToString()+": "+descr; } } static string filename = "C:\\Test\\Database1.mdb"; static TraceEventSession m_EtwSession; static Stopwatch sw; static List<Timestamp> timestamps = new List<Timestamp>(100000); static object sync = new object(); static ManualResetEvent mre = new ManualResetEvent(false); static void AddTimestamp(long t, string s) { lock (sync) { timestamps.Add(new Timestamp( t,s) ); } } static void AddTimestamp( string s) { lock (sync) { timestamps.Add(new Timestamp(sw.ElapsedMilliseconds, s)); } } static void ThreadProc() { int pid = Process.GetCurrentProcess().Id; //Создаем сессию ETW и подписываемся на события чтения файлов using (m_EtwSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName)) { m_EtwSession.StopOnDispose = true; m_EtwSession.EnableKernelProvider(KernelTraceEventParser.Keywords.FileIOInit | KernelTraceEventParser.Keywords.DiskFileIO); m_EtwSession.Source.Kernel.FileIORead += data => { //сохраняем все события чтения файла БД текущим процессом if (data.ProcessID == pid && data.FileName.ToLower().Trim() == filename.ToLower()) { AddTimestamp((long)data.TimeStampRelativeMSec, "Bytes read: " + data.IoSize.ToString()); } }; lock (sync) { sw = new Stopwatch(); sw.Start(); } //сигнализируем основному потоку о готовности mre.Set(); //запускаем Event Tracing m_EtwSession.Source.Process(); } } public static void Main(string[] argv) { //создаем поток для Event Tracing Thread th = new Thread(ThreadProc); th.IsBackground = true; th.Start(); //ждем готовности второго потока mre.WaitOne(); string constr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+ filename+"; "; string sql = "SELECT * FROM Table1"; //подключаемся к БД OleDbConnection conn = new OleDbConnection(constr); AddTimestamp( "Start"); Console.Write("Connecting...\r"); conn.Open(); AddTimestamp( "Connection.Open"); Thread.Sleep(1000); //выполняем запрос и читаем первые 10 строк using (conn) { OleDbCommand cmd = new OleDbCommand(sql, conn); Console.Write("Executing SQL...\r"); OleDbDataReader reader = cmd.ExecuteReader(); AddTimestamp( "OleDbCommand.ExecuteReader"); Thread.Sleep(1000); int i = 0; int n = 10; using (reader) while (reader.Read()) { AddTimestamp( "OleDbDataReader.Read "); string s = reader[0].ToString(); Console.Write("Reading data... "+ s.PadLeft(5)+" ("+ i.ToString().PadLeft(4)+"/"+n.ToString()+") rows\r"); i++; if (i > n) break; Thread.Sleep(1000); } } AddTimestamp( "End"); //завершаем сессию ETW m_EtwSession.Dispose(); //выводим результаты Console.Clear(); lock (sync) { timestamps.Sort(); foreach (var ts in timestamps) { Console.WriteLine(ts.ToString()); } } Console.WriteLine("Press any key to exit ..."); Console.ReadKey(); } } }
Результат выглядит так:
7: Start 34: Connection.Open 154: Bytes read: 65536 154: Bytes read: 4096 154: Bytes read: 4096 154: Bytes read: 4096 1037: OleDbCommand.ExecuteReader 1170: Bytes read: 4096 1170: Bytes read: 4096 1170: Bytes read: 4096 1170: Bytes read: 4096 2038: OleDbDataReader.Read 2172: Bytes read: 4096 3040: OleDbDataReader.Read 4041: OleDbDataReader.Read 5042: OleDbDataReader.Read 5153: Bytes read: 512 5175: Bytes read: 4096 5175: Bytes read: 4096 6042: OleDbDataReader.Read 7043: OleDbDataReader.Read 8044: OleDbDataReader.Read 9044: OleDbDataReader.Read 10045: OleDbDataReader.Read 10153: Bytes read: 512 11045: OleDbDataReader.Read 12081: End
По результатам видно, что Jet считывает данные блоками по 4096 байт (размер страницы в Access) и помещает в промежуточный буфер в памяти. Если требуемая строка находится в буфере, она берется из него, в противном случае осуществляется чтение следующего блока. События считывания по 512 байт являются, по видимому, периодическим опросом какой-то служебной информации (возможно, состояния блокировки записей), так как они наблюдаются постоянно, пока открыто соединение с БД (даже если ничего не считывать).
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.