InnoDB定义的Mutex
InnoDB在ib0mutex.h文件中定义了六种自定义的Mutex如下:
q OSBasicMutex:OS的Event定义的Mutex,依赖于具体的OS;施加锁只尝试一次。如下面的初始化代码段等。
q OSTrackMutex:继承自OSBasicMutex,依赖于OS;施加锁尝试函数参数指定的次数(max_spins参数指定尝试的次数)。
q TTASFutexMutex:利用OS的CAS原子指令实现锁的施加尝试,利用TAS实现解锁尝试。适用于Linux的Futex机制[1]。
q TTASMutex: 利用OS的TAS原子指令实现锁的施加和解锁尝试,在尝试不成的情况下,进行忙等待。
q TTASEventMutex:利用OS的TAS原子指令实现锁的施加和解锁尝试,在尝试不成的情况下,每次尝试之间需要调用ut_delay()进行睡眠延迟,尝试指定的次数(max_spins参数指定尝试的次数),且最终还要等到锁为止。
q PolicyMutex:获得锁,且获得施加锁者,主要用于Performance schema监控。
OSBasicMutex的初始化init()函数的代码段需要区分操作系统,相应的enter()、destroy()、exit()等函数也需要区分操作系统,根据不同的操作系统调用不同操作系统的相应函数:
#ifdef _WIN32
InitializeCriticalSection((LPCRITICAL_SECTION) &m_mutex); //Mutex的定义依赖于Windows的InitializeCriticalSection()函数
#else
{
int ret;
ret = pthread_mutex_init(&m_mutex, MY_MUTEX_INIT_FAST); //Mutex的定义依赖于Posix Thread的pthread_mutex_init()函数
ut_a(ret == 0);
}
#endif /* _WIN32 */
OSBasicMutex的测试锁是否施加try_lock()函数的代码段如下:
/** @return true if locking succeeded */
bool try_lock() UNIV_NOTHROW
{
ut_ad(innodb_calling_exit || !m_freed);
#ifdef _WIN32
return(TryEnterCriticalSection(&m_mutex) != 0); //Mutex的trylock依赖于Windows函数
#else
return(pthread_mutex_trylock(&m_mutex) == 0); //Mutex的trylock依赖于Posix Thread
#endif /* _WIN32 */
}
TAS和CAS原子操作的定义如下:
#ifdef _WIN32
# define TAS(l, n) os_atomic_test_and_set_u32((l), (n)) //Windows下利用Test And Set原子操作实现TAS操作
#else
# define TAS(l, n) os_atomic_test_and_set_ulint((l), (n)) //非Windows下利用Test And Set原子操作实现TAS操作(如Linux下调用__sync_lock_test_and_set()系统函数实现自旋锁,PostgreSQL也同样地调用了类似的函数)
#endif /* _WIN32 */
#define CAS(l, o, n) os_val_compare_and_swap_ulint((l), (o), (n)) //不同OS下利用Compare And Exchange原子操作实现CAS操作
InnoDB主要定义了如下类型的系统锁:
typedef ib_mutex_t RsegMutex; //保护回滚段的系统锁
typedef ib_mutex_t TrxMutex; //保护事务块的系统锁,这是单个事务的事务块的内容保护
typedef ib_mutex_t UndoMutex; //保护UNDO日志的系统锁
typedef ib_mutex_t PQMutex; //保护PURGE QUERY的系统锁
typedef ib_mutex_t TrxSysMutex; //保护整个InnoDB系统级的全局事务信息的系统锁,这是全局系统内所有在运行的事务的整体信息保护
typedef ib_mutex_t LockMutex; //保护锁表的系统锁
“ib_mutex_t”如下又分别有多种可能,可能被前面描述的六种Mutex的其中之一根据宏定义[2]进行选择而定义。
# ifdef HAVE_IB_LINUX_FUTEX
typedef PolicyMutex<TTASFutexMutex<DebugPolicy> > FutexMutex;
# endif /* HAVE_IB_LINUX_FUTEX */
typedef PolicyMutex<TTASMutex<DebugPolicy> > SpinMutex;
typedef PolicyMutex<TTASEventMutex<DebugPolicy> > SyncArrayMutex;
typedef PolicyMutex<OSTrackMutex<DebugPolicy> > SysMutex;
typedef PolicyMutex<OSBasicMutex<DebugPolicy> > EventMutex;
#ifdef MUTEX_FUTEX
/** The default mutex type. */
typedef FutexMutex ib_mutex_t;
#define MUTEX_TYPE "Uses futexes"
#elif defined(MUTEX_SYS)
typedef SysMutex ib_mutex_t;
#define MUTEX_TYPE "Uses system mutexes"
#elif defined(MUTEX_EVENT)
typedef SyncArrayMutex ib_mutex_t;
#define MUTEX_TYPE "Uses event mutexes"
#else
#error "ib_mutex_t type is unknown"
#endif /* MUTEX_FUTEX */
[1] Futex是Fast Userspace muTexes的缩写,被Linux 2.5.7支持。Futex是用户态与内核态混合的同步机制,同步的进程间通过mmap共享一段内存,futex变量位于这段共享的内存中且操作是原子的,当进程尝试进入互斥区或者退出互斥区的时候,先查看共享内存中的futex变量,如果没有竞争发生,则只修改futex,而不用再执行系统调用,这样会比没有Futex机制总是需要进入内核态做判断更高效。
[2] 在innodb.cmake文件中定义如下内容:
IF(MUTEXTYPE MATCHES "event")
ADD_DEFINITIONS(-DMUTEX_EVENT)
ELSEIF(MUTEXTYPE MATCHES "futex" AND DEFINED HAVE_IB_LINUX_FUTEX)
ADD_DEFINITIONS(-DMUTEX_FUTEX)
ELSE()
ADD_DEFINITIONS(-DMUTEX_SYS)
ENDIF()