精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SQLite切換日志模式優化

運維 新聞
我們需要探究從默認日志模式rollback journal模式,直接切換至wal模式后是否安全呢?

SQLite是一款輕型的數據庫,SQLite 是一款輕量級數據庫,是一個 關系型數據庫管理系統,它包含在一個相對小的 C 庫中,實現了自給自足的、無服務器的、零配置的、事務性的 SQL 數據庫引擎。 它能夠支持 Windows/Linux/Unix/Android/iOS 等等主流的操作系統,占用資源非常的低,因此在移動端也有很廣泛的應用。

SQLIte有多種日志模式(具體見背景知識),在項目的開發迭代中,會遇見原本就版本app使用的SQLite日志模式是舊版默認的rpllback journal,當用戶設備存在待恢復的.journal文件,新版本app的SQLite又需要將日志模式切換至wal時,我們就需要探究從默認日志模式rollback journal模式,直接切換至wal模式后是否安全呢?

背景知識

#define PAGER_JOURNALMODE_QUERY     (-1)  /* Query the value of journalmode */
#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */

#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */

#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */

#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */

#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */

#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */

rollback journal: Atomic Commit In SQLite(地址:https://sqlite.org/atomiccommit.html)?

write-ahead logging: Write-Ahead Logging(地址:https://sqlite.org/wal.html)?

sqlite的鎖模型: 鏈接(地址:https://sqlite.org/lockingv3.html)?

sqlite的線程模型: 鏈接(地址:https://sqlite.org/threadsafe.html)?

Android sqlite的線程模式

參考:Using SQLite In Multi-Threaded Applications(地址:https://www.sqlite.org/threadsafe.html)?

sqlite的線程模式,有三種:

  1. 單線程 :該模式下sqlite不使用互斥體保護自己,假定用戶使用單線程訪問DB,如果用戶同時使用多線程訪問,則不安全。
  2. 多線程 :該模式下sqlite線程安全,但前提是一個數據庫連接只能被一個線程使用。(注:可以有多個數據庫連接同時使用,但每個連接只能同時被一個線程使用)
  3. 串行 :該模式下sqlite的操作完全串行,因此完全線程安全,不對用戶做任何限制。

? 可以在編譯時,開始時(初始化數據庫前),運行時(創建數據庫連接時)指定線程模式,后面指定的可以覆蓋前面的,但如果前面指定的是單線程模式,則無法被覆蓋。

根據android源碼,可知sqlite編譯指定的是串型模式:

?Android.bp配置文件(地址:https://cs.android.com/android/platform/superproject/+/master:external/sqlite/dist/Android.bp?hl=zh-cn)

cflags: [
...
"-DSQLITE_THREADSAFE=2",
...

],

然而從4.4.2開始,android源碼重寫了sqlite封裝的相關代碼,里面出現了如下文件:

?源碼:android_database_SQLiteGlobal.cpp(地址:https://www.sqlite.org/android/file?name=sqlite3/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp)?

將sqlite的線程模式改為多線程:

static void sqliteInitialize() {
// Enable multi-threaded mode. In this mode, SQLite is safe to use by multiple
// threads as long as no two threads use the same database connection at the same
// time (which we guarantee in the SQLite database wrappers).
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);<<====關鍵步驟======

...

// Initialize SQLite.

sqlite3_initialize();

}

Android sqlite的連接池

平時我們是經過android封裝的SqliteOpenHelper來訪問sqlite的,常用的room和ormlite等數據庫本質上是使用SqliteOpenHelper,android的封裝中有一個primary connection的概念,只有primary connecton可以寫,其他connection只能讀。

閱讀源碼可以發現,SQLiteStatement和SQLiteQuery都會根據自己要執行的sql語句提前判斷這個是不是readOnly的,只有非readOnly的才需要primary connection,若nonPrimaryConnecion拿不到,也會嘗試獲取primary connection。

跟蹤源碼可以發現android封裝了SQLiteConnectionPool,primary connection有且僅有一個,noPrimaryConnection可以有多個。

?源碼:SQLiteConnectionPool.java(地址:https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/database/sqlite/SQLiteConnectionPool.java)?

但是其中會有一個最大的nonPrimaryConnecton的邏輯,rollback journal模式下最大為1,WAL模式下最小為2。

private void setMaxConnectionPoolSizeLocked() {
if (!mConfiguration.isInMemoryDb()
&& (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();<<=====關鍵步驟===
} else {
// We don't actually need to always restrict the connection pool size to 1
// for non-WAL databases. There might be reasons to use connection pooling
// with other journal modes. However, we should always keep pool size of 1 for in-memory
// databases since every :memory: db is separate from another.
// For now, enabling connection pooling and using WAL are the same thing in the API.
mMaxConnectionPoolSize = 1;
}
}

/**
* Gets the connection pool size when in WAL mode.
*/
public static int getWALConnectionPoolSize() {
int value = SystemProperties.getInt("debug.sqlite.wal.poolsize",
Resources.getSystem().getInteger(
com.android.internal.R.integer.db_connection_pool_size));
return Math.max(2, value);
}

項目中,正常使用的數據庫模式不是內存db,沒有進行日志模式優化前,也不是WAL日志模式,所以走的是else里面的邏輯,nonPrimaryConnection最大值為1。

WAL模式下,系統性默認配置的是最大4個nonPrimaryConnection。

?源碼:config.xml(地址:https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/res/res/values/config.xml)

<!-- Maximum number of database connections opened and managed by framework layer
to handle queries on each database when using Write-Ahead Logging. -->

<integer name="db_connection_pool_size">4</integer>

sqlite切換至WAL的優點

首先,WAL比rollback journal的并發性更好,因為WAL寫不阻塞讀,而rollback journal下,寫會阻塞讀。

其次,若業務中DatabaseManager通常會配置的是1寫多讀的連接池,實際android封裝的sqlite使用的是1寫1讀的連接池,會導致讀線程池存在一些競爭。

如果切換到WAL,理論上android封裝的sqlite會變成1寫4讀的連接池,讀線程池不再存在競爭。

基于sqlite的數據庫,如room,是如何開啟WAL的

?源碼:FrameworkSQLiteOpenHelper.java(地址:https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:sqlite/sqlite-framework/src/main/java/androidx/sqlite/db/framework/FrameworkSQLiteOpenHelper.java;l=32?q=Framework&ss=androidx)

當android版本高于4.1(jellyBean),sqlite會自動開啟WAL日志模式。

private OpenHelper getDelegate() {
// getDelegate() is lazy because we don't want to File I/O until the call to
// getReadableDatabase() or getWritableDatabase(). This is better because the call to
// a getReadableDatabase() or a getWritableDatabase() happens on a background thread unless
// queries are allowed on the main thread.
// We defer computing the path the database from the constructor to getDelegate()

// because context.getNoBackupFilesDir() does File I/O :(
synchronized (mLock) {
if (mDelegate == null) {
final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& mName != null
&& mUseNoBackupDirectory) {
File file = new File(mContext.getNoBackupFilesDir(), mName);
mDelegate = new OpenHelper(mContext, file.getAbsolutePath(), dbRef, mCallback);
} else {
mDelegate = new OpenHelper(mContext, mName, dbRef, mCallback);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

<<============關鍵步驟==================>>
mDelegate.setWriteAheadLoggingEnabled(mWriteAheadLoggingEnabled);
}
}
return mDelegate;
}
}

? 源碼:SupportSQLiteCompat.java(地址:https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:sqlite/sqlite/src/main/java/androidx/sqlite/db/SupportSQLiteCompat.java)

public static void setWriteAheadLoggingEnabled(@NonNull SQLiteOpenHelper sQLiteOpenHelper,
boolean enabled) {
sQLiteOpenHelper.setWriteAheadLoggingEnabled(enabled);
}

理論上,如果切換到WAL,一個是存取并發性能提高,另一個是讀線程池可以充分利用。

日志模式從journal模式切換至WAL模式是否安全

對于一個已經是rollback journal模式的sqlite數據庫,可不可以切換為WAL模式?切換后會不會導致一個hot journal被忽略,進而導致數據庫損壞呢?

追蹤源碼如下:

SQLiteOpenHelper打開db最終會調用的是 sqlite3_open_v2 方法,位于sqlite的main.c中。

默認情況下,sqlite使用的日志模式是DELETE(rollback journal delete)

#define PAGER_JOURNALMODE_DELETE      0   /* Commit by deleting journal file */

當調用 enableWriteAheadLogging ,實際會通過 nativeExecuteForString 執行PRAGMA指令。

private void setJournalMode(String newValue) {
String value = executeForString("PRAGMA journal_mode", null, null);
if (!value.equalsIgnoreCase(newValue)) {
try {
<<=======關鍵步驟=========>>
String result = executeForString("PRAGMA journal_mode=" + newValue, null, null);
if (result.equalsIgnoreCase(newValue)) {
return;
}
// PRAGMA journal_mode silently fails and returns the original journal
// mode in some cases if the journal mode could not be changed.
} catch (SQLiteDatabaseLockedException ex) {
// This error (SQLITE_BUSY) occurs if one connection has the database
// open in WAL mode and another tries to change it to non-WAL.
}
...
}
}

最終調用到:

?源碼:android_database_SQLiteConnection.cpp(地址:https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android_database_SQLiteConnection.cpp;l=559?q=executeOne)

static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
int err = sqlite3_step(statement);<<======關鍵步驟==========
if (err != SQLITE_ROW) {
throw_sqlite3_exception(env, connection->db);
}
return err;
}

跟隨代碼進度走到sqlite3VdbeExec,在里面可以找到 case_OP_JournalMode ,就能看到相關的處理邏輯。

最關鍵的地方就是調用了 sqlite3PageSetJournalMode 這個方法里會嘗試調用 sqlite3PageSharedLock 這個方法來判斷是否 hasHotJouenal ,有的話會嘗試獲取EXECLUSIVE_LOCK,進行回滾。因此,在打開數據庫時切換日志模式是安全的。

int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
u8 eOld = pPager->journalMode; /* Prior journalmode */

...

if( eMode!=eOld ){

/* Change the journal mode. */
assert( pPager->eState!=PAGER_ERROR );
pPager->journalMode = (u8)eMode;

...
if( !pPager->exclusiveMode && (eOld & 5)==1 && (eMode & 1)==0 ){

...

sqlite3OsClose(pPager->jfd);
if( pPager->eLock>=RESERVED_LOCK ){
sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
}else{
int rc = SQLITE_OK;
int state = pPager->eState;
assert( state==PAGER_OPEN || state==PAGER_READER );
if( state==PAGER_OPEN ){
rc = sqlite3PagerSharedLock(pPager);<<=====關鍵步驟==============
}
...
assert( state==pPager->eState );
}
}else if( eMode==PAGER_JOURNALMODE_OFF ){
sqlite3OsClose(pPager->jfd);
}
}

/* Return the new journal mode */
return (int)pPager->journalMode;
}

sqlite3PagerShareLock 中會判斷是否有hot journal,執行 pagerSyncJournal ,進行hot journa文件的回滾。

int sqlite3PagerSharedLock(Pager *pPager){
int rc = SQLITE_OK; /* Return code */

/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
** be OPEN or READER. READER is only possible if the pager is or was in
** exclusive access mode. */
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
assert( assert_pager_state(pPager) );
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
assert( pPager->errCode==SQLITE_OK );

if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
int bHotJournal = 1; /* True if there exists a hot journal-file */

assert( !MEMDB );
assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );

rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
goto failed;
}

/* If a journal file exists, and there is no RESERVED lock on the
** database file, then it either needs to be played back or deleted.
*/
if( pPager->eLock<=SHARED_LOCK ){
rc = hasHotJournal(pPager, &bHotJournal);<<=========關鍵步驟=============
}
if( rc!=SQLITE_OK ){
goto failed;
}
if( bHotJournal ){
if( pPager->readOnly ){
rc = SQLITE_READONLY_ROLLBACK;
goto failed;
}

/* Get an EXCLUSIVE lock on the database file. At this point it is
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
** database file, detect the RESERVED lock, and conclude that the
** database is safe to read while this process is still rolling the
** hot-journal back.*/

...
if( isOpen(pPager->jfd) ){
assert( rc==SQLITE_OK );
rc = pagerSyncHotJournal(pPager); <<============關鍵步驟==============
if( rc==SQLITE_OK ){
rc = pager_playback(pPager, !pPager->tempFile);
pPager->eState = PAGER_OPEN;
}
}else if( !pPager->exclusiveMode ){

??HasHotJournal?? :的代碼如下:

static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3_vfs * const pVfs = pPager->pVfs;
int rc = SQLITE_OK; /* Return code */
int exists = 1; /* True if a journal file is present */

int jrnlOpen = !!isOpen(pPager->jfd);
assert( pPager->useJournal );
assert( isOpen(pPager->fd) );
assert( pPager->eState==PAGER_OPEN );

assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
));

*pExists = 0;
if( !jrnlOpen ){
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
}
if( rc==SQLITE_OK && exists ){
int locked = 0; /* True if some process holds a RESERVED lock */

/* Race condition here: Another process might have been holding the
** the RESERVED lock and have a journal open at the sqlite3OsAccess()
** call above, but then delete the journal and drop the lock before
** we get to the following sqlite3OsCheckReservedLock() call. If that
** is the case, this routine might think there is a hot journal when
** in fact there is none. This results in a false-positive which will
** be dealt with by the playback routine. Ticket #3883.
*/
rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
if( rc==SQLITE_OK && !locked ){
Pgno nPage; /* Number of pages in database file */

assert( pPager->tempFile==0 );
rc = pagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){
/* If the database is zero pages in size, that means that either (1) the
** journal is a remnant from a prior database with the same name where
** the database file but not the journal was deleted, or (2) the initial
** transaction that populates a new database is being rolled back.
** In either case, the journal file can be deleted. However, take care
** not to delete the journal file if it is already open due to
** journal_mode=PERSIST.
*/
if( nPage==0 && !jrnlOpen ){
sqlite3BeginBenignMalloc();
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
if( !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
}
sqlite3EndBenignMalloc();
}else{
/* The journal file exists and no other connection has a reserved
** or greater lock on the database file. Now check that there is
** at least one non-zero bytes at the start of the journal file.
** If there is, then we consider this journal to be hot. If not,
** it can be ignored.
*/
if( !jrnlOpen ){
int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
}
if( rc==SQLITE_OK ){
u8 first = 0;
rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
if( !jrnlOpen ){
sqlite3OsClose(pPager->jfd);
}
*pExists = (first!=0);
}else if( rc==SQLITE_CANTOPEN ){
/* If we cannot open the rollback journal file in order to see if
** it has a zero header, that might be due to an I/O error, or
** it might be due to the race condition described above and in
** ticket #3883. Either way, assume that the journal is hot.
** This might be a false positive. But if it is, then the
** automatic journal playback and recovery mechanism will deal
** with it under an EXCLUSIVE lock where we do not need to
** worry so much with race conditions.
*/
*pExists = 1;
rc = SQLITE_OK;
}
}
}
}
}

return rc;
}

綜上探究的過程,我們可以得知道,默認日志模式rollback journal模式,直接切換至wal模式后是安全的,并能帶來更好的并發性能。

團隊介紹

我們來自淘寶逛逛客戶端團隊,“逛逛”主入口位于淘寶首頁的一級入口,菜單欄的第二欄,是淘寶的中心化內容平臺。

逛逛客戶端團隊追求極致的性能體驗,炫酷的動效,先進的多媒體技術,用最前沿的技術給用戶提供更好的內容社區體驗。

責任編輯:張燕妮 來源: 大淘寶技術
相關推薦

2019-09-26 08:40:27

SqliteTips優化

2010-10-29 15:07:33

oracle日志

2023-03-28 08:21:20

2010-05-07 16:01:21

Oracle歸檔模式

2009-08-06 19:50:03

linux命令行模式切linux命令行模式linux命令行

2014-10-08 10:37:41

SQLite

2014-02-26 11:01:28

日志優化系統日志

2010-05-25 17:30:36

移動IPv6切換

2020-10-30 12:37:42

日志系統

2010-03-29 14:55:18

Nginx日志

2016-10-31 20:42:11

日志運維IT運維

2019-05-27 13:30:25

UbuntuSlimbook Ba電源模式

2012-07-19 18:24:20

Emotion UI華為

2024-12-12 08:55:25

CSS代碼模式

2021-11-22 09:56:13

FedoraLinux

2012-07-25 15:17:00

IT運維架構

2009-05-26 16:41:19

廣域網優化托管服務

2022-06-20 08:16:42

享元模式優化系統內存

2009-05-08 08:49:17

微軟Windows 7操作系統

2025-09-28 01:50:00

點贊
收藏

51CTO技術棧公眾號

国产999久久久| 五月天精品视频| av白虎一区| 久久精品亚洲精品国产欧美kt∨| 国产欧美日韩亚洲精品| 精品一级少妇久久久久久久| 精品成av人一区二区三区| 欧美久久久久久久久| 欧美成人三级在线视频| 色开心亚洲综合| av电影在线观看不卡| 成人妇女免费播放久久久| 日韩黄色精品视频| 久久久9色精品国产一区二区三区| www.一区二区| 国产欧美日韩丝袜精品一区| 少妇一级淫片免费放中国 | 欧美日韩成人一区二区三区 | 日韩av黄色片| 91欧美在线| 亚洲视频日韩精品| v天堂中文在线| 国产日韩欧美中文在线| 欧美伊人久久大香线蕉综合69 | 日韩国产在线一区| 天堂在线观看免费视频| 国产精品一区不卡| 成人国产亚洲精品a区天堂华泰| 欧美一区二区激情视频| 影音国产精品| 久久久久久久久久久国产| 日本a级片视频| 欧美国产一区二区三区激情无套| 亚洲图片欧美日产| 无码国产69精品久久久久同性| 久久精品福利| 欧美一区二区三区在线| 粉色视频免费看| 精品欧美一区二区三区在线观看| 精品国产福利在线| 无码日本精品xxxxxxxxx| 国产区在线观看| 国产精品不卡在线| 在线观看国产一区| 免费网站成人| 久久免费偷拍视频| 欧美一区二区三区在线播放| 天堂中文在线观看视频| av在线不卡免费看| 国产在线一区二区三区欧美| 成人免费观看在线视频| 成人毛片老司机大片| 国产精品久久久久久久久婷婷| 亚洲美女综合网| 粉嫩绯色av一区二区在线观看| 999视频在线观看| 亚洲国产精品久久人人爱潘金莲| 国产成人在线免费观看| 国精产品一区二区| 午夜成人鲁丝片午夜精品| 91丨九色丨国产丨porny| 久久久久久国产精品一区| 涩涩视频在线观看免费| 久久婷婷一区二区三区| 天堂av一区二区| 在线观看美女网站大全免费| 亚洲色图欧美激情| 中文精品无码中文字幕无码专区| 玖玖在线播放| 欧美亚洲自拍偷拍| 在线免费黄色小视频| 97成人在线| 国产丝袜一区视频在线观看| 九一在线免费观看| 欧美1区3d| 97免费中文视频在线观看| 亚洲s码欧洲m码国产av| 久久精品国产77777蜜臀| 亚洲精品欧美极品| 亚洲欧洲综合在线| 国产精品免费免费| 屁屁影院ccyy国产第一页| 亚洲综合第一| 三区在线观看| 国产精品入口麻豆九色| 一二三四中文字幕| 欧美裸体视频| 6080午夜不卡| 国产老熟女伦老熟妇露脸| 欧美三级伦理在线| 精品中文字幕在线2019| 色一情一乱一伦| 国产精品亚洲视频| 日韩精品欧美在线| 九色91在线| 欧美综合天天夜夜久久| 亚洲一区二区三区黄色| 成人激情诱惑| 午夜美女久久久久爽久久| 男女视频在线观看网站| 亚洲av鲁丝一区二区三区| 亚洲国产美女| 国产精品嫩草影院一区二区| 亚洲黄色一级大片| 国产欧美视频在线观看| av久久久久久| 91伊人久久| 亚洲第一视频在线观看| 免费看一级黄色| 男女污污视频在线观看| 91精品综合久久久久久久久久久| 国内外成人免费激情在线视频网站 | 日本精品一区二区三区在线观看视频| 亚洲美女福利视频网站| 久久久久久久久精| 精品一区二区三区久久| 欧洲精品久久| 色一区二区三区| 日韩欧美电影一区| 黑人狂躁日本娇小| 日韩国产欧美在线观看| 影音先锋国产资源| 欧美激情四色| 国产精品久久久久久超碰| 日本人妻熟妇久久久久久| 亚洲色图19p| 在线观看高清免费视频| 蜜桃国内精品久久久久软件9| 欧美黄色性视频| 99久久免费看精品国产一区| 97精品国产| 国产精品高清在线| 国产在线电影| 91高清视频在线| www.久久国产| 性欧美长视频| 欧美中日韩免费视频| 亚洲欧美韩国| 精品伊人久久97| 探花视频在线观看| 91免费在线视频观看| 熟女少妇在线视频播放| 国语一区二区三区| 国模吧一区二区三区| 欧美一级淫片aaaaaa| 亚洲成a人v欧美综合天堂| 四虎精品一区二区| 最新成人av网站| 精品亚洲欧美日韩| xxxxxx欧美| 亚洲色图五月天| 波多野结衣激情视频| 国产欧美日韩麻豆91| 狠狠热免费视频| 久久久影院免费| 亚洲一区二区在线| 欧美14一18处毛片| 亚洲韩国欧洲国产日产av| 欧美三级午夜理伦| 国产亲近乱来精品视频 | 男女做暖暖视频| 国产老女人精品毛片久久| 成人av在线不卡| 日韩精品免费一区二区三区竹菊| 国产91精品视频在线观看| 国产在线视频网站| 欧美巨大另类极品videosbest | eeuss影院一区二区三区| 成人软件在线观看| 欧美激情一区二区三区不卡 | 美女视频黄 久久| 一区二区视频在线播放| 日本精品视频| 日本成人黄色片| 永久av在线| 日韩精品自拍偷拍| 国产免费观看av| 中文字幕一区二区三区在线观看 | 国产精品22p| 日本三级久久久| 精品美女在线观看视频在线观看| 搞黄网站在线看| 欧美日韩激情在线| 久久久久久久久艹| 久久久亚洲精品一区二区三区| 午夜免费高清视频| 韩国久久久久| 亚洲 日韩 国产第一区| 日韩成人久久| 日本精品视频在线播放| 国产在线69| 亚洲欧美一区二区三区情侣bbw| 国产精品伦一区二区三区| 精品久久久久久久久久久久| 国产精品suv一区二区88| 粉嫩av亚洲一区二区图片| 日本新janpanese乱熟| 欧美日韩国产探花| 日韩精品国内| www.亚洲一二| 国产欧美中文字幕| 高清在线视频不卡| 久久成年人视频| 久久综合九色综合久| 精品国产免费人成在线观看| 精品一区二三区| 欧美日韩国产在线看| 日韩影院一区二区| 欧美激情一区二区三区四区| 黄色片视频免费观看| 国产精品一二一区| 亚洲一级片网站| 久久亚洲图片| 欧美日韩二三区| 影音先锋中文字幕一区二区| 亚洲成年人专区| 久久一区91| 神马影院我不卡午夜| 天堂资源在线亚洲| 国产精品v欧美精品v日韩精品| 宅男噜噜噜66国产精品免费| 国产91色在线|| 蜜桃在线视频| 97碰在线观看| 黄视频免费在线看| 性欧美激情精品| 第一av在线| 欧美精品18videosex性欧美| 超碰人人在线| 久久亚洲精品视频| 毛片av在线| 在线日韩精品视频| 国产精品视频二区三区| 亚洲欧美一区二区三区在线| 天天av综合网| 国产精品初高中害羞小美女文| 国产熟妇久久777777| 99re这里只有精品首页| 欧美熟妇精品一区二区蜜桃视频 | 欧美精选午夜久久久乱码6080| 九九九免费视频| 亚洲精品欧美专区| www青青草原| 亚洲欧美一区二区三区极速播放 | 亚洲一区二区三区中文字幕 | 成人动漫视频在线观看完整版| 国产精品一站二站| 999久久久| 国产精品中文字幕制服诱惑| 国产精品一区二区三区免费| 大伊香蕉精品在线品播放| 国产亚洲欧美一区二区 | 一区二区三区国产福利| 久久一级电影| 男女啪啪免费观看| 亚洲一本视频| 播放灌醉水嫩大学生国内精品| 美女日韩在线中文字幕| 中文字幕第80页| 国产中文字幕精品| 97精品人人妻人人| 99国产精品国产精品毛片| 亚洲最大成人网站| 国产精品人人做人人爽人人添| 四虎884aa成人精品| 一区二区三区在线不卡| 国产精品500部| 在线这里只有精品| 国产精品丝袜黑色高跟鞋| 欧美mv和日韩mv的网站| 偷拍自拍在线| 在线日韩精品视频| 青草影视电视剧免费播放在线观看| 国外成人性视频| 国产精成人品2018| 不卡视频一区二区| 亚洲日本三级| 欧美性受xxxx黑人猛交88| 9国产精品视频| 天天色综合天天色| 成人国产视频在线观看| 在线免费观看视频| 一区二区激情视频| 免费黄色小视频在线观看| 日韩一区二区在线播放| 欧美孕妇性xxxⅹ精品hd| 久久好看免费视频| 午夜激情电影在线播放| 成人自拍性视频| 香蕉国产成人午夜av影院| 中文字幕日韩一区二区三区不卡| 国产一区二区三区久久久久久久久 | 蜜桃91丨九色丨蝌蚪91桃色| 黑人玩弄人妻一区二区三区| 国产欧美视频一区二区| 四虎永久在线精品| 欧美精品自拍偷拍| 九一国产在线| 久久久久国产一区二区三区| 国产综合色激情| 草莓视频一区| 91亚洲成人| 爆乳熟妇一区二区三区霸乳| 成人免费毛片嘿嘿连载视频| 成人欧美一区二区三区黑人一 | 国产精品高精视频免费| 精品无人区一区二区| 中文字幕日韩一区二区三区| 视频一区国产视频| 日韩精品人妻中文字幕有码 | 欧美日韩黄色| 亚洲va久久久噜噜噜久久狠狠| 一本不卡影院| 手机看片国产精品| 国产精品久久国产精麻豆99网站| 国内精品福利视频| 欧美精品一区二区高清在线观看| 国产丝袜在线| 国产一区私人高清影院| 欧洲三级视频| 最近免费中文字幕中文高清百度| 99久久精品情趣| 久久精品国产亚洲AV无码麻豆| 制服丝袜激情欧洲亚洲| av在线电影网| 国产精品久久一区主播| 亚洲丝袜啪啪| 久草青青在线观看| 91麻豆国产在线观看| 日韩精品视频免费看| 欧美精品一区在线观看| 免费影视亚洲| 91精品国产高清久久久久久91裸体 | 久久综合色8888| 日韩成人免费观看| 亚洲成色999久久网站| 超碰97国产精品人人cao| 国产精品v欧美精品v日韩| 精品96久久久久久中文字幕无| 日本中文字幕精品| 亚洲综合丝袜美腿| 欧美性受xxxx狂喷水| 91成人在线播放| 亚洲电影一级片| 九色porny91| 国产精品视频线看| 91高潮大合集爽到抽搐| 久久精品电影网| 精品伊人久久| a天堂资源在线观看| 99re这里只有精品视频首页| 天天做天天爱夜夜爽| 亚洲免费高清视频| 日韩漫画puputoon| 一区二区三区偷拍| 韩日精品视频一区| 久久激情免费视频| 日韩av一区在线观看| 激情图片在线观看高清国产| 精品国产综合区久久久久久| 久久久久一区| 免费黄色国产视频| 日韩欧美电影在线| 无码小电影在线观看网站免费 | 37p粉嫩大胆色噜噜噜| 色噜噜狠狠一区二区三区果冻| 午夜小视频在线| 91亚色免费| 亚洲欧美日韩国产综合精品二区| 手机免费看av| 91精品婷婷国产综合久久竹菊| 蜜乳av一区| 欧美日韩精品免费观看视一区二区| 日韩成人免费看| 麻豆精品一区二区三区视频| 日韩精品免费视频| 欧美黄色a视频| 人妻av无码专区| 国产日韩一级二级三级| 一区二区日韩在线观看| 久久免费精品视频| 成人动漫免费在线观看| 欧美一级片在线免费观看| 成人网男人的天堂| 婷婷激情五月网| 久久艳片www.17c.com | 免费观看黄色的网站| 99久久婷婷国产综合精品| 在线观看毛片网站| 高清一区二区三区四区五区| 成人影视亚洲图片在线| 亚洲欧洲国产视频| 欧洲精品一区二区| xxxx成人| 免费观看中文字幕| 久久久久久久综合狠狠综合| www.xxx国产| 国产精品麻豆va在线播放| 亚洲啪啪91| 一级黄色片日本|