sql
来源:互联网 发布:c语言实验时间格式转换 编辑:程序博客网 时间:2024/05/16 17:11
链接数据库
打开数据库
chromium数据库使用的是sqlite进行存储数据的,必然在使用的时候需要一个包装,这个项目是sql,其位于chromium的一级目录中,内部项目并不十分复杂,慢慢的进行分析。
首先我们要分析的是链接数据库。在一开始,我们要了解,数据库的访问是在数据库线程中的,而不是在UI线程的,这取决于chromium的多线程机制。
Connection类是数据库sql的链接类,用来创建数据库sqlite的链接,初始化数据库,设置数据库等相关操作。
0:022> kL # Child-SP RetAddr Call Site00 00000000`0662dc18 000007fe`ee26a8fe chrome_7feed330000!sql::Connection::OpenInternal01 00000000`0662dc20 000007fe`ef53ad74 chrome_7feed330000!sql::Connection::Open+0x10e02 00000000`0662dcc0 000007fe`ef53c12f chrome_7feed330000!WebDatabase::Init+0xa003 00000000`0662de60 000007fe`ef53c05f chrome_7feed330000!WebDatabaseBackend::LoadDatabaseIfNecessary+0xb304 00000000`0662dfa0 000007fe`ed3d2ead chrome_7feed330000!WebDatabaseBackend::InitDatabase+0x1305 (Inline Function) --------`-------- chrome_7feed330000!base::Callback<void __cdecl(void)>::Run+0x806 00000000`0662dfd0 000007fe`ed36eb95 chrome_7feed330000!base::debug::TaskAnnotator::RunTask+0x13d07 00000000`0662e0b0 000007fe`ed36f6d4 chrome_7feed330000!base::MessageLoop::RunTask+0x3f508 (Inline Function) --------`-------- chrome_7feed330000!base::MessageLoop::DeferOrRunPendingTask+0x14709 00000000`0662f190 000007fe`ed3d0e86 chrome_7feed330000!base::MessageLoop::DoWork+0x4840a 00000000`0662f360 000007fe`ed3bdd83 chrome_7feed330000!base::MessagePumpDefault::Run+0x1f60b (Inline Function) --------`-------- chrome_7feed330000!base::MessageLoop::RunHandler+0x150c 00000000`0662f5c0 000007fe`ed394381 chrome_7feed330000!base::RunLoop::Run+0x830d (Inline Function) --------`-------- chrome_7feed330000!base::MessageLoop::Run+0x350e 00000000`0662f610 000007fe`ee3a8076 chrome_7feed330000!base::Thread::Run+0x410f 00000000`0662f670 000007fe`ee3a8d3e chrome_7feed330000!content::BrowserThreadImpl::DBThreadRun+0x3610 00000000`0662f7c0 000007fe`ed3946d8 chrome_7feed330000!content::BrowserThreadImpl::Run+0xca11 00000000`0662f7f0 000007fe`ed3a7e9d chrome_7feed330000!base::Thread::ThreadMain+0x33812 00000000`0662f860 00000000`77a4652d chrome_7feed330000!base::`anonymous namespace'::ThreadFunc+0x15d13 00000000`0662f8d0 00000000`77ccc521 kernel32!BaseThreadInitThunk+0xd14 00000000`0662f900 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
上面的一个堆栈情况,是打开webdata数据库的函数调用情况,这个线程专门用来处理数据库数据。
bool Connection::OpenInternal(const std::string& file_name, Connection::Retry retry_flag) { AssertIOAllowed(); if (db_) { DLOG(FATAL) << "sql::Connection is already open."; return false; } // Make sure sqlite3_initialize() is called before anything else. InitializeSqlite(); // Setup the stats histograms immediately rather than allocating lazily. // Connections which won't exercise all of these probably shouldn't exist. if (!histogram_tag_.empty()) { stats_histogram_ = base::LinearHistogram::FactoryGet( "Sqlite.Stats." + histogram_tag_, 1, EVENT_MAX_VALUE, EVENT_MAX_VALUE + 1, base::HistogramBase::kUmaTargetedHistogramFlag); // The timer setup matches UMA_HISTOGRAM_MEDIUM_TIMES(). 3 minutes is an // unreasonable time for any single operation, so there is not much value to // knowing if it was 3 minutes or 5 minutes. In reality at that point // things are entirely busted. commit_time_histogram_ = GetMediumTimeHistogram("Sqlite.CommitTime." + histogram_tag_); autocommit_time_histogram_ = GetMediumTimeHistogram("Sqlite.AutoCommitTime." + histogram_tag_); update_time_histogram_ = GetMediumTimeHistogram("Sqlite.UpdateTime." + histogram_tag_); query_time_histogram_ = GetMediumTimeHistogram("Sqlite.QueryTime." + histogram_tag_); } // If |poisoned_| is set, it means an error handler called // RazeAndClose(). Until regular Close() is called, the caller // should be treating the database as open, but is_open() currently // only considers the sqlite3 handle's state. // TODO(shess): Revise is_open() to consider poisoned_, and review // to see if any non-testing code even depends on it. DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; poisoned_ = false; int err = sqlite3_open(file_name.c_str(), &db_); if (err != SQLITE_OK) { // Extended error codes cannot be enabled until a handle is // available, fetch manually. err = sqlite3_extended_errcode(db_); // Histogram failures specific to initial open for debugging // purposes. UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.OpenFailure", err); OnSqliteError(err, NULL, "-- sqlite3_open()"); bool was_poisoned = poisoned_; Close(); if (was_poisoned && retry_flag == RETRY_ON_POISON) return OpenInternal(file_name, NO_RETRY); return false; } // TODO(shess): OS_WIN support?#if defined(OS_POSIX) if (restrict_to_user_) { DCHECK_NE(file_name, std::string(":memory")); base::FilePath file_path(file_name); int mode = 0; // TODO(shess): Arguably, failure to retrieve and change // permissions should be fatal if the file exists. if (base::GetPosixFilePermissions(file_path, &mode)) { mode &= base::FILE_PERMISSION_USER_MASK; base::SetPosixFilePermissions(file_path, mode); // SQLite sets the permissions on these files from the main // database on create. Set them here in case they already exist // at this point. Failure to set these permissions should not // be fatal unless the file doesn't exist. base::FilePath journal_path(file_name + FILE_PATH_LITERAL("-journal")); base::FilePath wal_path(file_name + FILE_PATH_LITERAL("-wal")); base::SetPosixFilePermissions(journal_path, mode); base::SetPosixFilePermissions(wal_path, mode); } }#endif // defined(OS_POSIX) // SQLite uses a lookaside buffer to improve performance of small mallocs. // Chromium already depends on small mallocs being efficient, so we disable // this to avoid the extra memory overhead. // This must be called immediatly after opening the database before any SQL // statements are run. sqlite3_db_config(db_, SQLITE_DBCONFIG_LOOKASIDE, NULL, 0, 0); // Enable extended result codes to provide more color on I/O errors. // Not having extended result codes is not a fatal problem, as // Chromium code does not attempt to handle I/O errors anyhow. The // current implementation always returns SQLITE_OK, the DCHECK is to // quickly notify someone if SQLite changes. err = sqlite3_extended_result_codes(db_, 1); DCHECK_EQ(err, SQLITE_OK) << "Could not enable extended result codes"; // sqlite3_open() does not actually read the database file (unless a // hot journal is found). Successfully executing this pragma on an // existing database requires a valid header on page 1. // TODO(shess): For now, just probing to see what the lay of the // land is. If it's mostly SQLITE_NOTADB, then the database should // be razed. err = ExecuteAndReturnErrorCode("PRAGMA auto_vacuum"); if (err != SQLITE_OK) UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.OpenProbeFailure", err);#if defined(OS_IOS) && defined(USE_SYSTEM_SQLITE) // The version of SQLite shipped with iOS doesn't enable ICU, which includes // REGEXP support. Add it in dynamically. err = sqlite3IcuInit(db_); DCHECK_EQ(err, SQLITE_OK) << "Could not enable ICU support";#endif // OS_IOS && USE_SYSTEM_SQLITE // If indicated, lock up the database before doing anything else, so // that the following code doesn't have to deal with locking. // TODO(shess): This code is brittle. Find the cases where code // doesn't request |exclusive_locking_| and audit that it does the // right thing with SQLITE_BUSY, and that it doesn't make // assumptions about who might change things in the database. // http://crbug.com/56559 if (exclusive_locking_) { // TODO(shess): This should probably be a failure. Code which // requests exclusive locking but doesn't get it is almost certain // to be ill-tested. ignore_result(Execute("PRAGMA locking_mode=EXCLUSIVE")); } // http://www.sqlite.org/pragma.html#pragma_journal_mode // DELETE (default) - delete -journal file to commit. // TRUNCATE - truncate -journal file to commit. // PERSIST - zero out header of -journal file to commit. // TRUNCATE should be faster than DELETE because it won't need directory // changes for each transaction. PERSIST may break the spirit of using // secure_delete. ignore_result(Execute("PRAGMA journal_mode = TRUNCATE")); const base::TimeDelta kBusyTimeout = base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); if (page_size_ != 0) { // Enforce SQLite restrictions on |page_size_|. DCHECK(!(page_size_ & (page_size_ - 1))) << " page_size_ " << page_size_ << " is not a power of two."; const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h DCHECK_LE(page_size_, kSqliteMaxPageSize); const std::string sql = base::StringPrintf("PRAGMA page_size=%d", page_size_); ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout)); } if (cache_size_ != 0) { const std::string sql = base::StringPrintf("PRAGMA cache_size=%d", cache_size_); ignore_result(ExecuteWithTimeout(sql.c_str(), kBusyTimeout)); } if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) { bool was_poisoned = poisoned_; Close(); if (was_poisoned && retry_flag == RETRY_ON_POISON) return OpenInternal(file_name, NO_RETRY); return false; } // Set a reasonable chunk size for larger files. This reduces churn from // remapping memory on size changes. It also reduces filesystem // fragmentation. // TODO(shess): It may make sense to have this be hinted by the client. // Database sizes seem to be bimodal, some clients have consistently small // databases (<20k) while other clients have a broad distribution of sizes // (hundreds of kilobytes to many megabytes). sqlite3_file* file = NULL; sqlite3_int64 db_size = 0; int rc = GetSqlite3FileAndSize(db_, &file, &db_size); if (rc == SQLITE_OK && db_size > 16 * 1024) { int chunk_size = 4 * 1024; if (db_size > 128 * 1024) chunk_size = 32 * 1024; sqlite3_file_control(db_, NULL, SQLITE_FCNTL_CHUNK_SIZE, &chunk_size); } // Enable memory-mapped access. The explicit-disable case is because SQLite // can be built to default-enable mmap. GetAppropriateMmapSize() calculates a // safe range to memory-map based on past regular I/O. This value will be // capped by SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and // 64-bit platforms. size_t mmap_size = mmap_disabled_ ? 0 : GetAppropriateMmapSize(); std::string mmap_sql = base::StringPrintf("PRAGMA mmap_size = %" PRIuS, mmap_size); ignore_result(Execute(mmap_sql.c_str())); // Determine if memory-mapping has actually been enabled. The Execute() above // can succeed without changing the amount mapped. mmap_enabled_ = false; { Statement s(GetUniqueStatement("PRAGMA mmap_size")); if (s.Step() && s.ColumnInt64(0) > 0) mmap_enabled_ = true; } DCHECK(!memory_dump_provider_); memory_dump_provider_.reset( new ConnectionMemoryDumpProvider(db_, histogram_tag_)); base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( memory_dump_provider_.get(), "sql::Connection", nullptr); return true;}
这块代码是整个数据库链接类中最值得分析的一块代码,这里面是各种sqlite的配置,以求最大优化的使用数据,得到更高的效率。下面我们开始分析这个过程。
- 初始化sqlite。
- 设置统计信息,使得可以通过浏览器chrome://histograms/获得当前的统计数据
- 使用sqlite3_open打开数据库。
- 设置SQLITE_DBCONFIG_LOOKASIDE,这是为了提升小量存储申请的效率优化
- 设置locking_mode和journal_mode,page-size, cache_size, secure_delete。
- 使用SQLITE_FCNTL_CHUNK_SIZE设置文件系统chunk_size.
- 设置 mmap_size.
- 注册memory_dump_provider_
预载数据库
预载数据库的原理是获得sqlite3的文件系统接口,调用系统接口读取一定长度的文件。
void Connection::Preload() { AssertIOAllowed(); if (!db_) { DLOG_IF(FATAL, !poisoned_) << "Cannot preload null db"; return; } // Use local settings if provided, otherwise use documented defaults. The // actual results could be fetching via PRAGMA calls. const int page_size = page_size_ ? page_size_ : 1024; sqlite3_int64 preload_size = page_size * (cache_size_ ? cache_size_ : 2000); if (preload_size < 1) return; sqlite3_file* file = NULL; sqlite3_int64 file_size = 0; int rc = GetSqlite3FileAndSize(db_, &file, &file_size); if (rc != SQLITE_OK) return; // Don't preload more than the file contains. if (preload_size > file_size) preload_size = file_size; scoped_ptr<char[]> buf(new char[page_size]); for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) { rc = file->pMethods->xRead(file, buf.get(), page_size, pos); // TODO(shess): Consider calling OnSqliteError(). if (rc != SQLITE_OK) return; }}
数据操作
数据库操作由Statement类具体承担,包含执行数据库语句,绑定数据,检查是否执行成功等操作。
int Statement::StepInternal(bool timer_flag) { ref_->AssertIOAllowed(); if (!CheckValid()) return SQLITE_ERROR; const bool was_stepped = stepped_; stepped_ = true; int ret = SQLITE_ERROR; if (!ref_->connection()) { ret = sqlite3_step(ref_->stmt()); } else { if (!timer_flag) { ret = sqlite3_step(ref_->stmt()); } else { const base::TimeTicks before = ref_->connection()->Now(); ret = sqlite3_step(ref_->stmt()); const base::TimeTicks after = ref_->connection()->Now(); const bool read_only = !!sqlite3_stmt_readonly(ref_->stmt()); ref_->connection()->RecordTimeAndChanges(after - before, read_only); } if (!was_stepped) ref_->connection()->RecordOneEvent(Connection::EVENT_STATEMENT_RUN); if (ret == SQLITE_ROW) ref_->connection()->RecordOneEvent(Connection::EVENT_STATEMENT_ROWS); } return CheckError(ret);}
这是数据库语句的执行函数,首先判断语句是否有效,如果有效,则判断数据库是否连接,如果连接则执行该条语句。如果设置了执行时间统计,那么要调用接口记录单条语句执行的时间。
0 0
- SQL
- SQL
- SQL
- sql
- sql
- SQL
- sql
- SQL (-)
- sql
- SQL
- SQL
- SQL
- sql
- sql
- sql
- sql
- SQL
- sql
- 手把手教你在springMVC中不用框架写表格
- 直方图均衡化
- A - 数塔
- 微信 Tinker 负责人张绍文关于 Android 热修复直播分享记录
- 贪心 2016.10.25
- sql
- iOS微信支付的参考网址
- NSDate 相关整理(OC)
- Android Volley完全解析
- 详解python linecache模块读取文件的方法
- Toast工具类
- regularization 规范化(L1,L2等等):加惩罚函数降低过拟合
- Java初学
- hi