- MySQL的執(zhí)行過程
mysql的簡單執(zhí)行過程.png
1. 索引是在 執(zhí)行引擎 中發(fā)揮的作用
2. MySQL查詢緩存(內(nèi)存中)的效率是最高的烁设,所以Redis應(yīng)運(yùn)而生委煤!
mysql的完整執(zhí)行過程.png
Redis简识、Memcached瓤摧、MySQL
mysql與memcached與redis.png
- 內(nèi)存管理機(jī)制
- Memcached默認(rèn)使用Slab Allocation機(jī)制管理內(nèi)存只洒,其主要思想是按照預(yù)先規(guī)定的大小斯棒,將分配的內(nèi)存切割成特定長度的塊盾致,存儲(chǔ)相應(yīng)長度的key-value數(shù)據(jù)記錄,以完全解決內(nèi)存碎片化問題荣暮;使用空閑列表(free-list)判斷存儲(chǔ)狀態(tài)庭惜,比如現(xiàn)有內(nèi)存塊為88 bytes、112 bytes穗酥、144 bytes护赊,當(dāng)有一條100 bytes的數(shù)據(jù)時(shí),會(huì)選擇112 bytes的塊存儲(chǔ)砾跃,所以會(huì)有12 bytes的碎片骏啰;
- Redis使用現(xiàn)場(chǎng)申請(qǐng)內(nèi)存的方式來存儲(chǔ)數(shù)據(jù),并且很少使用 free-list 等方式來優(yōu)化內(nèi)存分配抽高,會(huì)在一定程度上存在內(nèi)存碎片判耕;
- 主要在于CPU的內(nèi)存是連續(xù)的,所以容易出現(xiàn)內(nèi)存碎片厨内;
- Memcached和Redis的機(jī)制祈秕,類似JVM對(duì)象的分類:空閑列表、直接內(nèi)存分配(指針碰撞)
- 數(shù)據(jù)持久化
- Memcached不支持內(nèi)存數(shù)據(jù)的持久化操作雏胃,所有的數(shù)據(jù)以 in-memory 的形式存儲(chǔ)请毛;
- redis支持兩種數(shù)據(jù)持久化方案,RDB瞭亮、AOF
- RDB:全量數(shù)據(jù)備份方仿,備份的是數(shù)據(jù)
- AOF:增量支持化備份,備份的是指令,如set key value
- 數(shù)據(jù)過期機(jī)制
- Memcached在刪除失效主鍵時(shí)也是采用消極方法仙蚜,內(nèi)部不會(huì)監(jiān)視主鍵是否失效此洲,而是在通過 Get 訪問主鍵時(shí)才會(huì)檢查是否已經(jīng)失效;
- Redis采用定時(shí)委粉、定期等多種方式檢測(cè)失效主鍵呜师,減少內(nèi)存泄漏;
- 數(shù)據(jù)類型:Memcached支持單一數(shù)據(jù)類型贾节,而Redis支持多達(dá)5種數(shù)據(jù)類型汁汗;
- Redis與MySQL相比
- 優(yōu)點(diǎn):沒有Scheme(創(chuàng)建表、字段類型)約束栗涂,數(shù)據(jù)結(jié)構(gòu)變更相對(duì)容易知牌,抗壓能力強(qiáng),性能極高斤程,可以達(dá)到每秒10萬人次訪問角寸;
- 缺點(diǎn):沒有索引和外鍵,缺少int/date等基本數(shù)據(jù)類型忿墅,多條件查詢集合內(nèi)聯(lián)和連接間接實(shí)現(xiàn)扁藕,開發(fā)效率低,可維護(hù)性不佳球匕;
- 二者通常搭配使用纹磺,Redis也可以作為mybatis的二級(jí)緩存,而一級(jí)緩存則是SqlSession亮曹、進(jìn)程緩存橄杨,單次鏈接有效;
Redis
- centOS-7安裝redis-4.0.6
yum install wget //安裝wget命令
wget http://download.redis.io/releases/redis-4.0.6.tar.gz //下載redis-4.0.5
yum install gcc //安裝gcc環(huán)境
mv redis-4.0.6.tar.gz /usr/local/
tar -zxvf redis-4.0.6.tar.gz
cd redis-4.0.6
make MALLOC=libc // 編譯
cd src && make install //安裝
./redis-server // 啟動(dòng)redis照卦,測(cè)試 - 三種啟動(dòng)redis的方式
- 直接啟動(dòng):執(zhí)行 redis-server 命令式矫,進(jìn)程級(jí)別,Ctrl+C關(guān)閉進(jìn)程役耕,redis也隨之關(guān)閉
- 指定配置文件采转,后臺(tái)啟動(dòng):修改redis.conf中的 daemonize 為 yes
redis-server redis.conf //指定配置文件的方式啟動(dòng)
ps -ef | grep redis // 查看redis的進(jìn)程信息
kill -9 8341 // 根據(jù)進(jìn)程號(hào)殺死redis - 開機(jī)啟動(dòng)redis
- 啟動(dòng)腳本在 redis 的utils目錄下:redis_init_script
- Linux配置開機(jī)自啟動(dòng):/etc/init.d
mkdir /etc/redis
cp redis.conf /etc/redis/6379.conf // 拷貝 redis.conf 到 /etc/redis/6379.conf
cp utils/redis_init_script /etc/init.d/redisd // 通常以 d 結(jié)尾的表示后臺(tái)自啟動(dòng)服務(wù) - 修改啟動(dòng)腳本的級(jí)別
#!/bin/sh # # chkconfig: 2345 90 10
- 執(zhí)行腳本:chkconfig redisd on
- 重啟系統(tǒng),redis服務(wù)已經(jīng)啟動(dòng):ps -ef | grep redis
- 啟動(dòng)/關(guān)閉redis服務(wù):service redisd start/stop
- SSH連接瞬痘,則需要安裝SSH服務(wù)故慈,SSH存在的意義就是模擬操作Linux
- 檢查是否安裝了:yum list installed | grep openssh-server
- centOS-7系統(tǒng)上默認(rèn)已經(jīng)安裝了openssh-server,對(duì)應(yīng)的安裝命令:yum install openssh-server
- 配置:vi /etc/ssh/sshd_config
Port 22
ListenAddress 0.0.0.0
ListenAddress ::
PermiRootLogin yes
PasswordAuthentication yes - 開啟sshd服務(wù):service sshd start
- 查看IP地址:ip addr
- window系統(tǒng)上可以使用Xshell工具連接SSH:ssh root@192.168.103.230 --> 回車鍵 --> 輸入root的密碼
- 讓sshd開機(jī)自啟動(dòng):systemctl enable sshd.service
- Redis支持的5種數(shù)據(jù)結(jié)構(gòu):string框全、list察绷、set、sortset津辩、hash
- string:字符串?dāng)?shù)據(jù)拆撼,最常用的一種數(shù)據(jù)類型容劳,普通的key-value存儲(chǔ)都可以歸為此類;
- set闸度、get
- mget:批量獲取多個(gè)key的值
- incr竭贩、incrby:incr對(duì)key的值做++,incrby加指定的值莺禁,incrby key offset
- desr留量、desrby:desr對(duì)key的值做--,desrby減指定的值
- setnx:set if not exist睁宰,只有key不存在時(shí)才會(huì)設(shè)置肪获,如果key已存在,則返回0
- setex:設(shè)置key的值為string類型柒傻,并指定有效期,單位是秒较木,setex key value seconds
getrange:獲取value的子串红符,getrange key startIndex endIndex
mset:批量設(shè)置多個(gè)key的值,返回0 表示沒有任何值被設(shè)置伐债;
msetnx:同mset预侯,不存在則設(shè)置,不會(huì)覆蓋原有的key-value
getset:設(shè)置key的值峰锁,并返回舊值
append:給value追加字符串萎馅,并返回新字符串的長度 - redis string的存儲(chǔ)格式(C語言)
struct key {
int len; //buf種存儲(chǔ)的字符串長度
int free; //buf種空閑空間的長度
char buf[]; //buf用于存儲(chǔ)的字符串內(nèi)容
}
type 數(shù)據(jù)類型 int/string
- hash:一個(gè)string類型的field和value之間的映射表
- redis的Hash數(shù)據(jù)類型的key(hash表名)對(duì)應(yīng)的value,內(nèi)部存儲(chǔ)結(jié)構(gòu)是一個(gè)HashMap虹蒋,Map<String, Map<String, String>> --> Map<Key, Map<field, value>
- Hash特別適合存儲(chǔ)對(duì)象:相對(duì)于將對(duì)象的每個(gè)屬性存儲(chǔ)為string類型糜芳,將整個(gè)對(duì)象存儲(chǔ)在Hash類型中會(huì)占用更少的內(nèi)存;
- 存儲(chǔ)的成員較少時(shí)魄衅,數(shù)據(jù)存儲(chǔ)為zipmap峭竣,當(dāng)成員數(shù)量增大時(shí)會(huì)自動(dòng)轉(zhuǎn)為真正的HashMap,此時(shí)的encoding為ht
hset key field value
hset student name Jack
hset student age 30
hset student sex 男
hmset key field1 value1 field2 value2 field3 value3
hget key field
hset student name // Jack
hmget key field1 field2 field3
hgetall key
hgetall student
hdel key field // 刪除某個(gè)field
hlen key //field的數(shù)量
- list:有序晃虫、可重復(fù)皆撩,以棧的結(jié)構(gòu)存儲(chǔ),先進(jìn)后出
lpush/rpush key value1 value2 value3:向頭/尾部添加元素
lrange key [startIndex] [endIndex]:獲取一段value
lrange key 0 -1 //獲取所有的value
lpop/rpop key:從頭/尾部刪除一個(gè)元素哲银,并返回被刪除的元素
llen key:元素個(gè)數(shù)
lindex key [index]:根據(jù)索引index獲取對(duì)應(yīng)的元素
lrem key [count] value:根據(jù)指定的元素名稱扛吞,移除 count 個(gè)此元素 - Set:不允許重復(fù),無序荆责,HashMap結(jié)構(gòu)存儲(chǔ)滥比,Map<String, Map<String, null> --> Map<Key, Map<value, null>
sadd key value1 value2 value3:添加成員
smembers key:獲取所有成員
sdiff key1 key2:求兩個(gè)集合的差集,key1中有草巡,但key2中沒有
suion key1 key2:求并集
sinter key1 key2:求交集 - sortSet:在set的基礎(chǔ)上增加控制順序的score守呜,應(yīng)用場(chǎng)景如排行榜
zadd key score1 value1 score2 value2 score3 value3
zadd myzset 1 AA 3 BB 2 CC
zrange:根據(jù)索引范圍獲取value
zrange myzset 0 -1 //獲取所有的value
zrange myzset 0 -1 withscores //獲取所有的value及其score
zrem key value1 value2 value3:刪除元素
zrangebyscore //根據(jù)score的范圍獲取value
zrangebyscore myzset 1 6
zrangebyscore myzset 1 6 withscores
zrank key value:查看value的排名型酥,從 0 開始,score最小的value查乒,排名為 0
zcard key:元素個(gè)數(shù)- sortset也是HashMap結(jié)構(gòu)存儲(chǔ)的弥喉,另外還加了一層跳躍表
- 跳躍表相當(dāng)于雙向鏈表,在其基礎(chǔ)上添加了前往比當(dāng)前元素大的跳轉(zhuǎn)連接玛迄,分為若干層由境,性能與樹結(jié)構(gòu)差不多
- 發(fā)布訂閱:
publisher -> channel -> subscriber1、subscriber2蓖议、subscriber3- 消息發(fā)布者把消息發(fā)送到管道虏杰,由管道負(fù)責(zé)把消息準(zhǔn)確發(fā)送給每個(gè)訂閱者;
- 類似信息管道勒虾,用來進(jìn)行系統(tǒng)之間的消息解耦纺阔,降低失敗的風(fēng)險(xiǎn);
PUBLISH channel message // 向channel中發(fā)送message
SUBSCRIBE channel // 訂閱channel
UNSUBSCRIBE channel1 channe2 // 取消訂閱修然,如果不指定笛钝,則取消所有訂閱 - 與MQ相比,redis的發(fā)布訂閱功能比較薄弱愕宋、無后臺(tái)功能玻靡,但比較輕量級(jí);而MQ可以持久化消息中贝,但消息的可靠性較差囤捻;
事務(wù)
傳統(tǒng)關(guān)系型數(shù)據(jù)庫的事務(wù)
- 目的:
- 為數(shù)據(jù)庫操作序列(一組SQL命令)提供一個(gè)從失敗中恢復(fù)到正常的方法,即使數(shù)據(jù)庫處在異常狀態(tài)下邻寿,仍能保持一致性蝎土;
- 當(dāng)并發(fā)訪問數(shù)據(jù)庫時(shí),可以做到隔離老厌,彼此的操作互不干擾瘟则;
- 四大特性:ACID
- 原子性(Atomicity):事務(wù)作為一個(gè)整體被執(zhí)行,要么全部被執(zhí)行枝秤,要么都不執(zhí)行醋拧;
- 一致性(Consistency):保證數(shù)據(jù)庫從一個(gè)一致狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€(gè)一致狀態(tài),一致狀態(tài)指數(shù)據(jù)庫中的數(shù)據(jù)應(yīng)滿足完整性約束淀弹;
- 隔離性(Isolation):多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)丹壕,相互不應(yīng)該干擾;
- 持久性(Durability):已提交的事務(wù)對(duì)數(shù)據(jù)庫的修改應(yīng)該永久保存在數(shù)據(jù)庫薇溃;
- 事務(wù)隔離機(jī)制:read uncommintted菌赖、read committed、repeatable read沐序、serializable
select @@tx_isolation; // 查看當(dāng)前會(huì)話的事務(wù)隔離級(jí)別
select @@global.tx_isolation; // 查看系統(tǒng)隔離級(jí)別- 在舊版本的MySQL系統(tǒng)中琉用,tx_isolation是transaction_isolation的別名堕绩,新版已經(jīng)棄用了!
select @@transaction_isolation;
select @@global.transaction_isolation; - 設(shè)置隔離級(jí)別
set session transaction isolation level read committed; //設(shè)置當(dāng)前會(huì)話的隔離級(jí)別
set global transaction isolation level read committed; //設(shè)置系統(tǒng)的隔離級(jí)別
- 在舊版本的MySQL系統(tǒng)中琉用,tx_isolation是transaction_isolation的別名堕绩,新版已經(jīng)棄用了!
- innodb:MySQL的默認(rèn)引擎
- innodb實(shí)現(xiàn)repeatable read的方式:MVCC邑时,Multiversion Concurrency Control 多版本并發(fā)控制
innodb的MVCC原理.png
2. DATA_TRX_ID:一條記錄的一個(gè)事務(wù)ID奴紧,每處理一個(gè)事務(wù),ID值自動(dòng)加1
3. DATA_ROLL_PTR:存儲(chǔ)UNDO LOG的指針晶丘,undo log用于記錄事務(wù)執(zhí)行命令的記錄黍氮;
4. DELETE_BIT:標(biāo)識(shí)這條記錄是否被刪除,不是真正的刪除浅浮,只有事務(wù)被commit之后才會(huì)真刪除沫浆;
Redis事務(wù)
multi // 開啟事務(wù)
······ // 命令入隊(duì)
EXEC // 執(zhí)行事務(wù)
DISCARD // 取消事務(wù),清空命令隊(duì)列
WATCH // 監(jiān)視key
redis事務(wù)流程.png
Redis與ACID
原子性:單個(gè)Redis命令的執(zhí)行是原子性的滚秩,但Redis事務(wù)上沒有任何機(jī)制維持原子性专执,所以Redis事務(wù)并不是原子性;
-
一致性:比如A向B轉(zhuǎn)賬100叔遂,當(dāng)B賬戶加了100時(shí)他炊,A賬戶一定減了100,反之亦然已艰,絕不會(huì)出現(xiàn)中間狀態(tài)!
- 入隊(duì)錯(cuò)誤:在命令入隊(duì)過程中蚕苇,如果客戶端向服務(wù)器發(fā)送了錯(cuò)誤的命令哩掺,如參數(shù)出錯(cuò),那么服務(wù)器將向客戶端返回一個(gè)出錯(cuò)信息涩笤,并將客戶端的事務(wù)狀態(tài)設(shè)置為 REDIS_DIRTY_EXEC
- 執(zhí)行錯(cuò)誤:如果命令在事務(wù)執(zhí)行過程中發(fā)生錯(cuò)誤嚼吞,如對(duì)一個(gè)不同類型的key執(zhí)行了錯(cuò)誤的操作,那么Redis只會(huì)將錯(cuò)誤包含在事務(wù)的結(jié)果中蹬碧,不會(huì)引起事務(wù)中斷或整個(gè)失敗舱禽,不會(huì)影響已執(zhí)行命令的結(jié)果,也不會(huì)影響后面要執(zhí)行的事務(wù)命令恩沽,所以它對(duì)一致性也沒有影響誊稚;
隔離性:WATCH命令用于在事務(wù)開始之前監(jiān)視任何數(shù)量的key,事務(wù)執(zhí)行過程中罗心,如果任意一個(gè)被監(jiān)視的key被其他客戶端更改了里伯,那么整個(gè)事務(wù)不再執(zhí)行,直接返回失敗渤闷,所以對(duì)隔離性沒有影響疾瓮;
持久性:事務(wù)不過是用隊(duì)列包裹的一組Redis命令,并沒有提供任何額外的持久性功能飒箭,所以事務(wù)的持久性由Redis所使用的持久化模式?jīng)Q定狼电;
-
redis在分布式集群環(huán)境下的session共享
- SpringBoot的依賴:spring-session-data-redis
- @EnableRedisHttpSession:開啟Redis緩存Session蜒灰,屬性maxInactiveIntervalInSeconds 指定緩存時(shí)間
request.getSession().setAttribute(key, value):設(shè)置session
request.getSession().getAttribute(key):獲取session
request.getSession().getId():獲取session ID,首次建立會(huì)話并設(shè)置Session時(shí)肩碟,會(huì)自動(dòng)生成Session ID - Spring Session會(huì)保存兩個(gè)Session ID强窖,以防止其中一個(gè)過期
-
MySQL數(shù)據(jù)庫表的設(shè)計(jì)
- 更小的通常更好,控制字節(jié)長度
- 使用合適的數(shù)據(jù)類型腾务,如tinyint只占8個(gè)位毕骡,char存儲(chǔ)定長數(shù)據(jù)時(shí)比varchar更節(jié)省空間,因?yàn)関archar需要額外存儲(chǔ)數(shù)據(jù)長度岩瘦,如32位的UUID可以用char(32)
- 盡量避免NULL未巫,而選擇 NOT NULL、DEFAULT ''
NULL的列會(huì)讓索引統(tǒng)計(jì)和值比較更復(fù)雜启昧,可為NULL的列會(huì)占用更多的磁盤空間叙凡,MySQL處理起來也更復(fù)雜
-
MySQL索引設(shè)計(jì)
- 選擇唯一性索引:索引的值是唯一的,可以更快速的查詢密末,保證物理上的唯一
- 為經(jīng)常需要排序握爷、分組和聯(lián)合操作的字段建立索引,排序會(huì)浪費(fèi)很多時(shí)間
- 常作為查詢條件的字段建立索引
- 數(shù)據(jù)少的地方不必建立索引
-
SQL語句的優(yōu)化:explain查看執(zhí)行計(jì)劃
- 能用 between 就不用 in
- 能用 distinct 就不用 group by
- 避免數(shù)據(jù)強(qiáng)轉(zhuǎn)
-
緩存帶來的回報(bào)
- 高速讀寫
CPU L1/L2/L3 Catch严里、Linux page Cache加速硬盤讀寫新啼、瀏覽器緩存、Ehcache緩存數(shù)據(jù)庫結(jié)果 - 降低后端負(fù)載
后端服務(wù)器通過前端緩存降低負(fù)載刹碾,通過Redis降低MySQL負(fù)載
- 高速讀寫
-
緩存帶來的代價(jià)
- 數(shù)據(jù)不一致性:緩存層和數(shù)據(jù)層有時(shí)間窗口不一致燥撞,和更新策略有關(guān);
- 代碼維護(hù)成本增加:原本只需要讀寫數(shù)據(jù)庫就能實(shí)現(xiàn)的功能迷帜,在加入了緩存之后就要去維護(hù)緩存的數(shù)據(jù)物舒;
- 堆內(nèi)緩存可能帶來內(nèi)存溢出的風(fēng)險(xiǎn),影響用戶進(jìn)程戏锹,如ehCache冠胯、loadingCache
本機(jī)內(nèi)存分類:堆、JVM棧锦针、方法區(qū)荠察、本地方法棧、程序計(jì)數(shù)器 - 堆內(nèi)緩存與遠(yuǎn)程服務(wù)器(如Redis)緩存
堆內(nèi)緩存一般性能更好伞插,遠(yuǎn)程緩存則需要套接字傳輸割粮;
用戶級(jí)別的數(shù)據(jù)盡量采用遠(yuǎn)程緩存;
大數(shù)據(jù)量盡量采用遠(yuǎn)程緩存媚污,服務(wù)節(jié)點(diǎn)化原則舀瓢;
緩存雪崩
- 緩存集中在一段時(shí)間內(nèi)失敗,發(fā)生大量緩存穿透耗美,所有查詢都落在數(shù)據(jù)庫上京髓,造成緩存雪崩航缀;
- 由于原有緩存失效,在新緩存未到期間堰怨,所有原本應(yīng)該訪問緩存的請(qǐng)求都去查詢數(shù)據(jù)庫了芥玉,從而對(duì)數(shù)據(jù)庫CPU和內(nèi)存造成巨大壓力,嚴(yán)重的會(huì)導(dǎo)致數(shù)據(jù)庫宕機(jī)备图;
解決方案
- 加鎖排隊(duì):multex互斥鎖解決
Redis的 SETNX 去 set 一個(gè) mutex key灿巧,當(dāng)操作返回成功時(shí),再進(jìn)行l(wèi)oad db的操作揽涮,并回設(shè)緩存抠藕;否則就重試整個(gè)get緩存的方法; - 數(shù)據(jù)預(yù)熱
系統(tǒng)上線后蒋困,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)盾似,從而避免在用戶請(qǐng)求的時(shí)候,先查數(shù)據(jù)庫雪标、再將數(shù)據(jù)設(shè)置到緩存零院;
可以通過緩存reload機(jī)制,預(yù)先去更新緩存村刨,在即將發(fā)生大并發(fā)訪問前告抄,手動(dòng)觸發(fā)加載,緩存不同的key - 雙層緩存策略
C1為原始緩存嵌牺,C2為拷貝緩存玄妈,C1失效時(shí),可以訪問C2髓梅,C1緩存失效時(shí)間設(shè)置為短期,C2設(shè)置為長期 - 定時(shí)更新緩存策略
時(shí)效性要求不高的緩存绎签,容器啟動(dòng)初始化加載枯饿,采用定時(shí)任務(wù)更新緩存 - 設(shè)置不同的過期時(shí)間,讓緩存失效時(shí)間點(diǎn)盡量均勻
緩存穿透
- 用戶查詢數(shù)據(jù)诡必,如果在數(shù)據(jù)庫中沒有奢方,緩存中自然也不會(huì)有,這就導(dǎo)致每次用戶查詢時(shí)爸舒,在緩存中查不到蟋字,就要去數(shù)據(jù)庫中再查一遍,然后返回空扭勉!也就是進(jìn)行兩次無用的查詢鹊奖;
- 每次用戶請(qǐng)求,都會(huì)繞過緩存涂炎,直接查詢數(shù)據(jù)庫忠聚,這就是緩存穿透设哗;
解決方案
- 緩存空值
如果一個(gè)查詢返回的數(shù)據(jù)為空,不管數(shù)據(jù)是否存在两蟀,我們都把這個(gè)空結(jié)果進(jìn)行緩存网梢,但它的過期時(shí)間會(huì)很短,最長不超過5min
第二次查詢緩存時(shí)赂毯,就可以獲取到一個(gè)值了战虏,而不會(huì)繼續(xù)查詢數(shù)據(jù)庫; - 采用布隆過濾器 Bloom Filter
將所有可能存在的數(shù)據(jù) 哈希 到一個(gè)足夠大的bitmap中党涕,一個(gè)一定不存在的數(shù)據(jù)會(huì)被這個(gè)bitmap攔截掉烦感,從而避免對(duì)底層存儲(chǔ)系統(tǒng)造成壓力;
布隆過濾器占用內(nèi)存很小遣鼓,bit存儲(chǔ)啸盏,性能特別高