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的配置,以求最大优化的使用数据,得到更高的效率。下面我们开始分析这个过程。

  1. 初始化sqlite。
  2. 设置统计信息,使得可以通过浏览器chrome://histograms/获得当前的统计数据
  3. 使用sqlite3_open打开数据库。
  4. 设置SQLITE_DBCONFIG_LOOKASIDE,这是为了提升小量存储申请的效率优化
  5. 设置locking_mode和journal_mode,page-size, cache_size, secure_delete。
  6. 使用SQLITE_FCNTL_CHUNK_SIZE设置文件系统chunk_size.
  7. 设置 mmap_size.
  8. 注册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