進程、線程、協(xié)程
進程
進程是系統(tǒng)分配資源的最小單位,一個應用程序就是一個進程,每個進程都是相互獨立的
線程
線程是程序運行的不同路徑,每個進程至少有一個線程,可以有多個線程。線程有自己獨立的堆棧、寄存器和共享進程資源。線程的多并發(fā)是系統(tǒng)對線程調(diào)度的上下文切換,CPU在不同時間片段執(zhí)行不同的線程,每一次上下文切換都會中斷當前線程,保留當前線程狀態(tài),切換到其它線程執(zhí)行,過程是由用戶態(tài)-> 內(nèi)核態(tài)->用戶態(tài)。相對協(xié)程而言會耗費更多內(nèi)存和時間
協(xié)程
協(xié)程是一個輕量級的線程,它是一個特殊的函數(shù),函數(shù)可以被掛起后執(zhí)行。在Go中,由關(guān)鍵字go定義的函數(shù)就是一個協(xié)程,可以實現(xiàn)高并發(fā),由系統(tǒng)調(diào)度切換,但不需要由用戶態(tài)到內(nèi)核態(tài),協(xié)程的通訊不是共享內(nèi)存,而是消息傳遞,在Go中協(xié)程的通訊是通過channel管道實現(xiàn)的。進程和線程上下文切換過程中,切換內(nèi)容保存在內(nèi)核棧中,協(xié)程則保存在自己的變量中(用戶?;蛘叨眩?/p>
用戶態(tài)和內(nèi)核態(tài)
image.png
image.png
image.png
用戶態(tài)到內(nèi)核態(tài)怎樣切換?
image.png
image.png
Go的協(xié)程
Go的channel
Go的select
Go的切片
- new(T) 為每個新的類型T分配一片內(nèi)存,初始化為 0 并且返回類型為*T的內(nèi)存地址:這種方法 返回一個指向類型為 T,值為 0 的地址的指針,它適用于值類型如數(shù)組和結(jié)構(gòu)體;它相當于 &T{};
- make(T) 返回一個類型為 T 的初始值,它只適用于3種內(nèi)建的引用類型:slice、map 和 channel;
數(shù)組:arr := [...]int{1,2,3}
切片:slice := []int{1,2,3}
slice := make([]int,5,10)
len(slice)長度 cap(slice)容量
nil切片 var silce[]int
空切片 slice := []{}
對于底層數(shù)組容量是k的切片slice[i:j]來說
長度:j-i
容量:k-j
slice[1:3],長度就是3-1=2,容量是5-1=4
slice := []int{1, 2, 3, 4, 5}
newSlice := slice[1:3]
fmt.Printf("newSlice長度:%d,容量:%d",len(newSlice),cap(newSlice))
Redis
遠程字典服務(Remote Dictionary Server),C語言寫的,支持網(wǎng)絡,可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫。
5種數(shù)據(jù)類型:String、Lists、hashes、Set、Zset
還有范圍查詢:bitmaps、hyperloglogs和地理空間(geospatial)索引半徑查詢
KV鍵值對,每秒10萬+查詢
作用場景:內(nèi)容緩存,主要用于處理大量數(shù)據(jù)的高訪問負載,也用于一些日志系統(tǒng)。
優(yōu)點:查找速度快
缺點:數(shù)據(jù)無結(jié)構(gòu)化,通常只被當作字符串或者二進制數(shù)據(jù)
特性:
1、多樣的數(shù)據(jù)類型
2、 持久化,定期把內(nèi)存中的數(shù)據(jù)同步到磁盤中
3、 集群
4、 事物
redis是單線程的,redis的瓶頸是機器內(nèi)存和網(wǎng)絡帶寬。
作用:
- 數(shù)據(jù)庫
- 緩存
- 消息中間件MQ
字符串
微信公眾號文章瀏覽量應用
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> incrby views 10
(integer) 11
127.0.0.1:6379>
#GETRANGE
127.0.0.1:6379> keys *
1) "views"
127.0.0.1:6379> set key1 "hello,123"
OK
127.0.0.1:6379> get key1
"hello,123"
127.0.0.1:6379> get range key 0 3
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379> getrange key 0 3
""
127.0.0.1:6379> getrange key1 0 3
"hell"
127.0.0.1:6379> getrange key1 0 -1
"hello,123"
127.0.0.1:6379> getrange key1 0 3
#setex (set with expire) #設置過期時間
127.0.0.1:6379> setex key3 30 'hello'
OK
127.0.0.1:6379> ttl key3
(integer) 22
127.0.0.1:6379> ttl key3
(integer) 19
127.0.0.1:6379>
#setnx (set if not exist) #如果key不存在 在分布式鎖中會常常使用
127.0.0.1:6379> setnx mykey 'redis'
(integer) 1
127.0.0.1:6379> setnx mykey 'mongodb'
(integer) 0
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379>
#########################
#批量設置,批量獲取 mset mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "mykey"
4) "k1"
5) "views"
6) "key1"
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4
(integer) 0 #原子性操作,失敗一個都會失敗
127.0.0.1:6379>
##########################
#對象
127.0.0.1:6379> set user:1 {name:zhangsan,age:lisi} #設置一個user:1對象 值為json字符串來保存對象
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:lisi}"
127.0.0.1:6379>
##########################
#getset
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb
"redis"
127.0.0.1:6379> get db
"mongodb"
127.0.0.1:6379>
List
棧、隊列、阻塞
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379> rpush list 1
(integer) 4
127.0.0.1:6379> rpush list 0 -1
(integer) 6
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "1"
5) "0"
6) "-1"
127.0.0.1:6379> lpop list
"three"
127.0.0.1:6379> rpop list
"-1"
127.0.0.1:6379>
127.0.0.1:6379> lindex list 1
"one"
127.0.0.1:6379>
127.0.0.1:6379> llen list
(integer) 4
127.0.0.1:6379>
#移除指定數(shù)量的值
127.0.0.1:6379> lrem list 1 one
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "1"
3) "0"
127.0.0.1:6379>
#trim 修剪
127.0.0.1:6379> ltrim list 1 2
OK
127.0.0.1:6379> lrange list 0 -1
1) "1"
2) "0"
127.0.0.1:6379>
#####################
#rpoplpush 移除列表最后一個元素并添加新的元素
127.0.0.1:6379> lset list 0 'ww'
OK
127.0.0.1:6379> lrange list 0 -1
1) "ww"
2) "0"
127.0.0.1:6379>
127.0.0.1:6379> linsert list before "ww" "nn"
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "nn"
2) "ww"
3) "0"
127.0.0.1:6379>
- List實際上是鏈表結(jié)構(gòu),before Note after ,left, right都可以插入
- 如果key不存在,創(chuàng)建新的鏈表
- 如果key存在,創(chuàng)建新的內(nèi)容
Set
set中的值不能重復
kuangdeMacBook-Pro:~ kuang$ redis-cli
127.0.0.1:6379> sadd myset "kaung"
(integer) 1
127.0.0.1:6379> sadd myset 'xiao'
(integer) 1
127.0.0.1:6379> smembers myset
1) "xiao"
2) "kaung"
127.0.0.1:6379> ismember myset xiao
(error) ERR unknown command 'ismember'
127.0.0.1:6379> sismember myset 'xiao'
(integer) 1
127.0.0.1:6379>
#獲取集合中值的數(shù)量
127.0.0.1:6379> scard myset
(integer) 2
127.0.0.1:6379>
#移除
127.0.0.1:6379> srem myset 'xiao'
(integer) 1
127.0.0.1:6379>
#隨機抽選出一個元素
127.0.0.1:6379> srandmember myset
"kaung"
127.0.0.1:6379>
#隨機移除集合中的元素
127.0.0.1:6379> sadd myset 'k1'
(integer) 1
127.0.0.1:6379> sadd myset 'k2'
(integer) 1
127.0.0.1:6379> smembers myset
1) "k2"
2) "k1"
3) "kaung"
127.0.0.1:6379> spop myset
"kaung"
127.0.0.1:6379> spop myset
"k1"
127.0.0.1:6379> smembers myset
1) "k2"
127.0.0.1:6379>
#將指定的值移動到另外一個集合中
127.0.0.1:6379> sadd myset 2
(integer) 1
127.0.0.1:6379> sadd myset2 'v1'
(integer) 1
127.0.0.1:6379> smove v1 myset
(error) ERR wrong number of arguments for 'smove' command
127.0.0.1:6379> smove myset2 'v1' myset
(integer) 0
127.0.0.1:6379> smove myset2 myset 'v1'
(integer) 1
127.0.0.1:6379> smembers myset2
(empty list or set)
127.0.0.1:6379> smove myset myset2 'v1'
(integer) 1
127.0.0.1:6379> smembers myset2
1) "v1"
127.0.0.1:6379>
# 差集、交集、并集 如微博共同關(guān)注
127.0.0.1:6379> sadd key1 a
(integer) 1
127.0.0.1:6379> sadd key1 b
(integer) 1
127.0.0.1:6379> sadd key1 c
(integer) 1
127.0.0.1:6379> sadd key2 c
(integer) 1
127.0.0.1:6379> sadd key2 d
(integer) 1
127.0.0.1:6379> sadd key2 e
(integer) 1
127.0.0.1:6379> SDIFF key1 key2
1) "b"
2) "a"
127.0.0.1:6379> SDIFF key2 key1
1) "e"
2) "d"
127.0.0.1:6379> SINTER key1 key2
1) "c"
127.0.0.1:6379> SUNION key1 key2
1) "e"
2) "a"
3) "c"
4) "b"
5) "d"
127.0.0.1:6379>
Hash(哈希)
Map集合,key-map,值是map集合 key field->value;
hash變更的數(shù)據(jù)user name age,尤其是用戶信息之類的,經(jīng)常變動的信息!hash更適合于對象的存儲,String更加適合于字符串存儲;
127.0.0.1:6379> hset myhash field1 kuang
(integer) 1
127.0.0.1:6379> hget myhash field1
"kuang"
127.0.0.1:6379> hmset myhash field1 hello field2 world
OK
127.0.0.1:6379> hmget myhash field1 field2
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash
1) "field1"
2) "hello"
3) "field2"
4) "world"
127.0.0.1:6379> hdel myhash field1
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "field2"
2) "world"
#長度
127.0.0.1:6379> hlen myhash
(integer) 1
#是否存在
127.0.0.1:6379> hexists myhash field1
(integer) 0
127.0.0.1:6379> hexists myhash field2
(integer) 1
#獲取所有的keys
127.0.0.1:6379> hkeys myhash
1) "field2"
#獲取所有的值
127.0.0.1:6379> hvals myhash
1) "world"
#自增
127.0.0.1:6379> hset myhash field3 5
(integer) 1
127.0.0.1:6379> hincrby myhash field3 1
(integer) 6
#如果不存在添加
127.0.0.1:6379> hsetnx myhash field4 hello
(integer) 1
127.0.0.1:6379> hsetnx myhash field4 world
(integer) 0
127.0.0.1:6379>
Zset(有序集合)
在set的基礎上加了值
127.0.0.1:6379> zadd myzset 1 one
(integer) 1
127.0.0.1:6379> zadd myzset 2 two 3 three
(integer) 2
127.0.0.1:6379> zrange myzset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379>
# 實現(xiàn)排序
127.0.0.1:6379> zadd salary 100 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 200 lisi
(integer) 1
127.0.0.1:6379> zadd salary 300 wangwu
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores
1) "zhangsan"
2) "100"
3) "lisi"
4) "200"
5) "wangwu"
6) "300"
127.0.0.1:6379>
127.0.0.1:6379> zrangebyscore salary -inf 250 withscores
1) "zhangsan"
2) "100"
3) "lisi"
4) "200"
127.0.0.1:6379>
#移除 zrem
127.0.0.1:6379> zrem salary lisi
(integer) 1
127.0.0.1:6379>
127.0.0.1:6379> zcard salary
(integer) 2
127.0.0.1:6379>
#從大到小排序 反轉(zhuǎn)
127.0.0.1:6379> zrevrange salary 0 -1
1) "wangwu"
2) "zhangsan"
127.0.0.1:6379>
#獲取指定區(qū)間的成員數(shù)量
127.0.0.1:6379> zcount salary 100 200
(integer) 2
127.0.0.1:6379>
Geosptial地理位置
3.2版本以后,可以推算地理位置的信息,兩地之間的距離,方圓幾里的人
#添加地理位置
#規(guī)則:兩級無法直接添加,我們一般會下載城市數(shù)據(jù),直接通過程序一次性寫入
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geodist china:city beijing shanghai
"1067378.7564"
127.0.0.1:6379> georadius china:city 110 30
(error) ERR wrong number of arguments for 'georadius' command
127.0.0.1:6379> georadius china:city 110 30 1000 km
(empty list or set)
127.0.0.1:6379> georadius china:city 110 39 1000 km
1) "beijing"
127.0.0.1:6379>
#將二位的經(jīng)緯度轉(zhuǎn)為一維的字符串
127.0.0.1:6379> geohash china:city beijing
1) "wx4fbxxfke0"
#移除
127.0.0.1:6379> zrem china:city beijing
(integer) 1
127.0.0.1:6379>
hyperloglog基數(shù)統(tǒng)計的算法
優(yōu)點:占用的內(nèi)存固定,2^64不同元素的技術(shù),只需要128KB內(nèi)存
應用:網(wǎng)頁的UV(一個人訪問一個網(wǎng)站多次,但算作一個人)
傳統(tǒng)方式:set保存用戶id,然后統(tǒng)計set中元素的數(shù)量作為判斷標準
127.0.0.1:6379> PFadd mykey a b c d e f g h i j
(integer) 1
127.0.0.1:6379> PFcount mykey
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
127.0.0.1:6379>
Bitmaps
位存儲
統(tǒng)計用戶信息,活躍,不活躍,登錄,未登錄,打卡,365打卡。兩個狀態(tài)的都可以使用Bitmaps
Bitmaps位圖,數(shù)據(jù)結(jié)構(gòu),都是操作二進制位來進行記錄,就只有0和1兩個狀態(tài)
365天=365bit 1字節(jié)=8bit 46個字節(jié)左右
使用bitmap來記錄周一到周日到打卡
周一:1 周二:0周三:0 周四:1 ...
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379>
#查看某一天是否打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
#統(tǒng)計打卡的天數(shù)
27.0.0.1:6379> bitcount sign
(integer) 3
127.0.0.1:6379>
Hyperloglog基數(shù)統(tǒng)計算法
特點:占用內(nèi)存固定,2^64個不同元素的技術(shù),只需要12KB內(nèi)存。
什么是基數(shù)
A{1,3,5,7,9} B{1,3,,7,8}
基數(shù):不重復的元素 = 5
2.8.9就更新了Hyperlogolog數(shù)據(jù)結(jié)構(gòu)
網(wǎng)頁的UV(一個人訪問一個網(wǎng)站多次,但是還是算作一個人)
傳統(tǒng)方式:set保存用戶的id,然后統(tǒng)計set中元素的數(shù)量作為標準。這種方式需要保存大量的用戶ID。我們的目的是為了計數(shù)而不是保存用戶ID。
有0.81%的錯誤率!統(tǒng)計UV,可以忽略不計。
127.0.0.1:6379> PFadd mykey a b c d e f g h i j
(integer) 1
127.0.0.1:6379> PFcount mykey
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
127.0.0.1:6379>
統(tǒng)計時,只統(tǒng)計不重復的數(shù)據(jù),適合用作UV統(tǒng)計
127.0.0.1:6379> pfadd key a b c a d
(integer) 1
127.0.0.1:6379> pfcount key
(integer) 4
127.0.0.1:6379> pfadd a a a a a f g d
(integer) 1
127.0.0.1:6379> pf count
(error) ERR unknown command 'pf'
127.0.0.1:6379> pfcount count
(integer) 0
127.0.0.1:6379> pfcount key
(integer) 4
127.0.0.1:6379>
事務
Redis事務本質(zhì):一組命令的集合!一個事務中的所有命令都會被序列化,在事務執(zhí)行過程中,會按照順序執(zhí)行!
一次性、順序性、排他性!
Redis事物沒有隔離級別的概念
所有的命令在事務中,并沒有直接被執(zhí)行,只有發(fā)起執(zhí)行命令的時候才會執(zhí)行
- Redis單條命令是原子性的,但事務不保證原子性
redis事務 - 開啟事務(multi)
- 命令入隊
- 執(zhí)行事務 (exce)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379>
#放棄事務
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k2
(nil)
127.0.0.1:6379> get k2
#異常
- 運行時異常,如果事務隊列中存在語法性,那么執(zhí)行命令的時候,其它命令是可以正常執(zhí)行的,錯誤命令拋出異常!
- 編譯型異常(代碼有問題!命令有錯?。?,事務中的所有命令都不會被執(zhí)行
悲觀鎖
- 很悲觀,認為什么時候都會出現(xiàn)問題,無論做什么都會加鎖,效率低
樂觀鎖 - 很樂觀,認為什么時候都不會出現(xiàn)問題,所有不會上鎖!更新數(shù)據(jù)的時候去判斷一下,在此期間是否有人修改過數(shù)據(jù)。Watch監(jiān)控.
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec #事務正常結(jié)束,數(shù)據(jù)期間沒有發(fā)生變動,這個時候就正常執(zhí)行
1) (integer) 80
2) (integer) 20
127.0.0.1:6379>
#測試多線程修改值,使用watch監(jiān)視money,可以當作redis的樂觀鎖
如第一條線程還未執(zhí)行exec時,又開啟一個線程,直接修改money的值,再次執(zhí)行exec就會失敗
使用watch可以當作redis的樂觀鎖(面試常問)
redis內(nèi)存滿了的策略
配置環(huán)境中設置
LRU是Least Recently Used的縮寫,即最近最少使用;
- volatile-lru:只對設置了過期時間的key進行LRU
- alleys-lru:刪除lru算法的key
- volatile-random: 隨機刪除即將過期的key
- allkeys-random:隨機刪除
- volatile-ttl:刪除即將過期的
- noeviction: 永不過期,返回錯誤
持久化
快照配置:
如果900s內(nèi),如果至少有一個key進行了修改,我們及時進行持久化
save 900 1
默認使用RDB,可以在配置文件中開啟AOF(appendonly)
APPEND ONLY 模式 aof配置
appendonly no #默認不是開啟aof模式的,默認是使用rdb方式持久化,在大部分情況下,rdb完全夠用。
appendfilename 'appendonly.aof' #持久化的文件名字
appendsysnc always #每次修改都會sync 消耗性能
appendfsync everysec #每秒執(zhí)行一次sync,可能會丟失這1s的數(shù)據(jù)
appendfsync no #不執(zhí)行sync,這個時候系統(tǒng)會自己同步數(shù)據(jù),速度最快
RDB(Redis DataBase)
優(yōu)點:
- 適合大規(guī)模數(shù)據(jù)的恢復!
- 對數(shù)據(jù)完整性的要求不高!
缺點: - 需要一定的時間間隔進行操作,如果意外宕機了,這個最后一次修改的數(shù)據(jù)就沒有了。
- fork進程的時候,會占用一定的空間。
有時候在生產(chǎn)環(huán)境會對rdb數(shù)據(jù)進行備份
配置:save 900 1 默認配置不要隨便修改
在指定的時間內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入硬盤,也就是行話講的S napshot快照,它恢復時將快照文件直接讀到內(nèi)存。
Redis會單獨創(chuàng)建(fork)一個子進程來進行持久化,會先將數(shù)據(jù)寫入一個臨時文件中,待持久化過程都結(jié)束了,再用這個臨時文件替換上次持久化好的文件,整個過程中,主進程是不進行任何IO操作的。這就確保了極高的性能。如果需要進行大規(guī)模數(shù)據(jù)的恢復,且對數(shù)據(jù)恢復的完整性不是非常敏感,那RDB方式比AOF方式更加高效,RDB的缺點時最好一次持久化后的數(shù)據(jù)可能丟失。
rdb保存的文件是dump.rdb ;
如何恢復rdb文件
- 只需要將rdb文件放在我們redis啟動目錄就可以,redis啟動的時候會自動檢查dump.rdb恢復其中的數(shù)據(jù)
- 查看需要存在的位置
127.0.0.1:6379> config get dir
1) "dir"
2) "/Users/kuang/lnmp1"
127.0.0.1:6379>
AOF(Append Only File)
優(yōu)點:
- 每一次修改都會同步;
- 每秒同步一次,可能會丟失一秒的數(shù)據(jù)
- 從不同步,效率最高
缺點: - 相對于數(shù)據(jù)文件來說,aof遠遠大于rdb,恢復的速度比rdb慢
- aof運行的效率也比rdb慢,所以我們redis默認的配置就是rdb持久化!
將我們的所有命令都記錄下來。
以日志的形式來記錄每一個寫操作,將redis執(zhí)行過的所有指令記錄下來(讀操作不記錄),只允許追加文件但不可以改寫文件,redis啟動之初會讀取該文件重新構(gòu)建數(shù)據(jù),換言之,redis重啟的話就會根據(jù)日志文件的內(nèi)容將寫指令從前到后執(zhí)行一次以完成數(shù)據(jù)的恢復工作。
AOF保存的是appendonly.aof文件;
如果aof文件有錯誤,這個時候redis是啟動不起來的。redis提供了一個修復工具redis-check-aof
kuangdeMacBook-Pro:~ kuang$ redis-check-aof --fix appendonly.aof
#線程一
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> set out 0
QUEUED
127.0.0.1:6379> exec #線程二執(zhí)行之后執(zhí)行
(nil) #沒有成功
127.0.0.1:6379>
#線程二
kuangdeMacBook-Pro:~ kuang$ redis-cli
127.0.0.1:6379> set money 10
OK
127.0.0.1:6379>
#解除觀察
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> watch money
如果事務修改失敗,解鎖,獲取最新的值,再次觀察,然后執(zhí)行新的事務

拓展
- RDB 持久化方式能夠在指定的時間間隔內(nèi)對你的數(shù)據(jù)進行快照存儲
- AOF持久化方式記錄每次對服務器的寫操作,當服務器重啟的時候會重新執(zhí)行這些命令來恢復原始數(shù)據(jù),AOF命令以Redis協(xié)議追加保存每次寫的操作到文件的末尾,Redis還能對AOF文件進行后臺重寫,使得AOF文件的提及不至于太大
- 只做緩存,如果你只希望你的數(shù)據(jù)在服務器運行的時候存在,你可以不使用任何持久化
- 同時開啟兩種持久化方式
- 在這種情況下,當redis重啟的時候會優(yōu)先載入AOF文件來恢復原始的數(shù)據(jù),因為在通常情況下AOF文件保存的數(shù)據(jù)集要比RDB文件保存的數(shù)據(jù)集完整
- RDB的數(shù)據(jù)不實時,同時使用兩者服務器重啟也只會找到AOF文件,那要不要只使用AOF文件呢?不建議,因為RDB更適合用于備份數(shù)據(jù)庫(AOF在不斷變化不好備份),快速重啟,而且不會有AOF可能潛在的Bug,留作一個萬一的手段
- 性能建議
- 因為RDB文件只用作后備用途,建議在Slave上持久化RDB文件,而且只要15分鐘備份一次就夠了,只保留save 900 1這條規(guī)則。
- 如果Enable AOF,好處是在最惡劣的情況下也只會丟失不超過2秒的數(shù)據(jù),啟動腳本簡單只load自己的AOF文件就可以了,代價一是帶來了持續(xù)的IO,二是AOF rewrite 過程中產(chǎn)生的新數(shù)據(jù)寫到新文件造成的阻塞幾乎是不可避免的,要是硬盤許可,盡可能減少rewrite頻率。AOF重寫基礎文件大小默認64M太小,可以設置到5G。
- 如果不Enable AOF,僅靠Master-Slave Replication實現(xiàn)高可用性也可以,代價是如果Master/Slave同時倒掉,會丟失幾十分鐘的數(shù)據(jù),啟動腳本也要比較兩個Master/Slave中的RDB文件,載入教新的那個。微博就是這種架構(gòu)。
發(fā)布訂閱
Redis發(fā)布訂閱(pub/sub)是一種消息通訊模式:發(fā)送者發(fā)生消息,訂閱者接收消息。微博的關(guān)注系統(tǒng)。
Redis客戶端可以訂閱任何數(shù)量的頻道。
第一個:消息發(fā)送者 第二個:頻道 第三個:消息訂閱在
場景:
- 實時消息系統(tǒng)!
- 實時聊天(頻道當作聊天室,將信息回顯給所有人即可)
-
訂閱,關(guān)注系統(tǒng)
稍微復雜的場景就會用消息中間件MQ()
image.png
測試
#訂閱頻道 訂閱端
127.0.0.1:6379> subscribe kuang
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuang"
3) (integer) 1
#等待讀取推送信息
1) "message"
2) "kuang"
3) "hello"
1) "message" # 消息
2) "kuang" # 哪個頻道的消息
3) "redis" # 消息內(nèi)容
#向頻道發(fā)送消息 發(fā)送端
kuangdeMacBook-Pro:~ kuang$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> publish kuang "hello"
(integer) 1
127.0.0.1:6379> publish kuang "redis"
(integer) 1
127.0.0.1:6379>
主從復制
概念:主從復制,是指將一臺Redis服務的數(shù)據(jù),復制到其他的Redis服務器中,前者稱為主節(jié)點Master以寫為主,后者稱為從節(jié)點Slave以讀為主。數(shù)據(jù)復制是單向的,只能從主到從。
主從復制的主要作用包括:
1、數(shù)據(jù)冗余:主從復制實現(xiàn)了數(shù)據(jù)的熱備份
2、故障恢復:主節(jié)點出現(xiàn)問題時,可以由從節(jié)點提供服務,實現(xiàn)快速的故障恢復
3、 負責均衡:主負責寫,從負責讀
4、高可用的基石:主從復制是哨兵和集群的基礎。
單臺redis最大使用內(nèi)存不應該超過20G。
默認情況下,每臺redis服務器都是主節(jié)點;且一個主節(jié)點可以有多個從節(jié)點,但一個從節(jié)點只能有一個主節(jié)點。

配置:
#info 查看信息
127.0.0.1:6379> info
# Server
redis_version:4.0.9
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:7b8134d888556d22
redis_mode:standalone
os:Darwin 20.4.0 x86_64
arch_bits:64
multiplexing_api:kqueue
atomicvar_api:atomic-builtin
gcc_version:4.2.1
process_id:31972
run_id:64c18b1f659df291d920871f2dcf598f1523378d
tcp_port:6379
uptime_in_seconds:3649
uptime_in_days:0
hz:10
lru_clock:14057410
executable:/Users/kuang/redis-server
config_file:
# Clients
connected_clients:2
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
# Memory
used_memory:1470864
used_memory_human:1.40M
used_memory_rss:1245184
used_memory_rss_human:1.19M
used_memory_peak:1470864
used_memory_peak_human:1.40M
used_memory_peak_perc:100.06%
used_memory_overhead:1048576
used_memory_startup:981040
used_memory_dataset:422288
used_memory_dataset_perc:86.21%
total_system_memory:8589934592
total_system_memory_human:8.00G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:0.85
mem_allocator:libc
active_defrag_running:0
lazyfree_pending_objects:0
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1624670098
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
# Stats
total_connections_received:2
total_commands_processed:11
instantaneous_ops_per_sec:0
total_net_input_bytes:403
total_net_output_bytes:20500
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:1
pubsub_patterns:0
latest_fork_usec:1188
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
# Replication
role:master
connected_slaves:0
master_replid:ea9f90513a821a4d5997e2b28a531e158a0b8da7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# CPU
used_cpu_sys:2.29
used_cpu_user:0.78
used_cpu_sys_children:0.00
used_cpu_user_children:0.00
# Cluster
cluster_enabled:0
# Keyspace
db0:keys=19,expires=0,avg_ttl=0
127.0.0.1:6379>

復制3個配置文件,然后修改對應的信息
1、 端口
2、 pid名字
3、 log文件名字
4、 dump.rdb名字
啟動

配置從機:
臨時配置:
127.0.0.1:6379> slaveof 127.0.0.1 6379 #找誰當作自己的主機

永久配置:
在config中修改主機ip和端口
>細節(jié)
- 主機可寫,從機不可寫,寫報錯
- 主機斷開連接,從機依舊連接到主機,但是沒有寫操作。如果主機恢復正常后,依舊可以讀取主機信息。
>復制原理
- slave啟動成功連接到master后會發(fā)送一個sync同步命令
- Master接到命令,啟動后臺的存盤進程,同時收集所有接收到的用于修改數(shù)據(jù)的命令,在后臺進程執(zhí)行完畢后,master將傳送整個數(shù)據(jù)文件到slave,并完成一次完全同步
- 全量復制:slave服務在收到數(shù)據(jù)庫文件后,將其存盤并加載到內(nèi)存中
- 增量復制: Master繼續(xù)將新的所有收集到的修改命令依次傳給slave,完成同步
- 但是只要是重新連接master,一次完全同步(全量復制)將被自動執(zhí)行
>如果主機斷開了,可以使用SLAVEOF on one 使從節(jié)點變成主節(jié)點
哨兵模式

哨兵模式能夠后臺監(jiān)控主機是否故障,如果故障了根據(jù) 投票計數(shù)自動將從苦轉(zhuǎn)化為主庫,當主庫恢復時,主庫自動變?yōu)閺膸臁?br> 哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個獨立的進程,作為進程,它會獨立運行,其原理是哨兵通過發(fā)送命令,等待Redis服務器響應,從而監(jiān)控運行多個redis實例。
哨兵的作用:
- 通過發(fā)送命令,讓redis服務器返回監(jiān)控其運行狀態(tài),包括主機和從服務器
- 當哨兵監(jiān)測到master宕機,會自動將slave切換成master,然后通過發(fā)布訂閱模式通知其他的從服務器,修改配置文件,讓它們切換主機
然爾一個哨兵進程對redis服務器進行監(jiān)控,可能出現(xiàn)問題,為此,我們可以使用多個哨兵進行監(jiān)控,各個哨兵之間還會進行監(jiān)控,這就形成了多哨兵模式。

假設主服務器宕機,哨兵1先檢測到這個結(jié)果,系統(tǒng)并不會馬上進行failover過程,僅僅是哨兵1主觀的認為主服務器不可用,這個現(xiàn)象稱為主觀下線。當后面的哨兵也檢測到主服務器不可用,并且數(shù)量達到一定值時,那么哨兵之間就會進行一次投票,投票的結(jié)果由一個哨兵發(fā)起,進行failover故障轉(zhuǎn)移操作,切換成功后,就會通過發(fā)布訂閱模式,讓各個哨兵把自己監(jiān)控的從服務器實現(xiàn)切換主機,這個過程稱為客觀下線。
配置哨兵
目前狀態(tài)是一主二從
1、 配置哨兵配置文件sentinel.conf
#被監(jiān)控的名稱 host port 1 后面的這個數(shù)字1,代表主機掛了,slave投票看讓誰來接替主機,票數(shù)最多的,就會成為主機
kuangdeMacBook-Pro:~ kuang$ sentinel monitor myredis 127.0.0.1 6379 1
2、 啟動哨兵
redis-sentinel monitor kconfig/sentinel.conf
如果過Master節(jié)點斷開,會自動選擇以一個從機作為主機(有自己的算法)
優(yōu)點:
- 哨兵集群,基于主從復制,所有主從配置的優(yōu)點,它全有
- 主從可以切換,故障可以轉(zhuǎn)移,系統(tǒng)的可用性會更好
- 哨兵模式就是主從模式的升級,手動到自動,更加健壯
缺點: - redis不好在線擴容,集群容量一旦到達上限,在線擴容就十分麻煩
- 實現(xiàn)哨兵模式的配置其實是很麻煩的,里面有很多選擇
全部配置
1、端口 2、工作目錄 3、主機節(jié)點 4、主機密碼 5、故障轉(zhuǎn)移時間 6、通知腳本(如主機宕機了,發(fā)送郵件通知)
緩存穿透和雪崩
[圖片上傳中...(image.png-bbefaf-1624679561120-0)]
緩存穿透:用戶查詢數(shù)據(jù),緩存和數(shù)據(jù)庫中都沒能命中,查詢失敗,下次查詢依舊需要訪問數(shù)據(jù)庫,導致數(shù)據(jù)庫壓力變大
解決方案:
-
布隆過濾器--布隆過濾器是一種數(shù)據(jù)結(jié)構(gòu),對所有可能查詢的參數(shù)以hash形式存儲,在控制層先進行校驗,不符合則丟棄,從而避免了對底層存儲系統(tǒng)的查詢壓力;
image.png - 緩存空對象--如果空值能夠被緩存起來,這就意味著緩存需要更多的空間存儲更多的鍵,因為這當中可能會有很多的空鍵; 即使對空值設置過了過期時間,還是會存在緩存層和存儲空間層會有一段時間窗口額度不一致,這對于需要保持一致性的業(yè)務會有影響。
緩存擊穿
這里需要注意和緩存擊穿的區(qū)別,緩存擊穿,是指一個key非常熱點,在不停的扛著大并發(fā),大并發(fā)集中對著個點進行訪問,當這個key在失效瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請求數(shù)據(jù)庫。
解決方案:
- 設置熱點永不過期;
- 加互斥鎖:使用分布式鎖(setnx),保證對于每一個key同時只有一個線程去查詢后端服務,其他線程沒有獲得分布式鎖的權(quán)限,因此只需要等待即可,這種方式將高并發(fā)的壓力轉(zhuǎn)移到了分布式鎖,因此對分布式鎖的考驗較大。
緩存雪崩
在某個時間段 ,緩存集中過期失效 或者是緩存服務器某個節(jié)點出現(xiàn)宕機或者斷網(wǎng)。因為自然形成的緩存雪崩,一定是在某個時間段集中創(chuàng)建緩存,這個時候,數(shù)據(jù)庫也是可以頂住壓力的,無非就是對數(shù)據(jù)庫產(chǎn)生周期性的壓力而已。而緩存服務節(jié)點的宕機,對數(shù)據(jù)庫服務器造成的壓力是不可預知的,很可能瞬間就把數(shù)據(jù)庫壓垮
解決方案:
- redis高可用:即搭建集群;
- 限流降級:加鎖或者隊列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量,比如對某個key只允許一個線程查詢和寫數(shù)據(jù),其他線程等待
- 數(shù)據(jù)預熱:在正式部署前,手動觸發(fā)預先訪問一遍 加載緩存不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻。
MySQL
流程
連接器->查詢緩存->分析器->優(yōu)化器->執(zhí)行器
慢查詢
-- 查詢超過多少秒才記錄
show VARIABLES like 'long_query_time';
-- 慢查詢開啟狀態(tài),慢查詢?nèi)罩敬娣盼恢?show VARIABLES like 'slow_query%';
-- 打開慢日志記錄開關(guān)
set GLOBAL slow_query_log = on;
-- 設置慢查詢?nèi)罩?set GLOBAL slow_query_log_file = '';
-- 設置超過2秒慢日志
set GLOBAL long_query_time = 2;
--查看事務
SELECT * FROM information_schema.INNODB_LOCKs;
--查看鎖
SELECT * FROM information_schema.INNODB_LOCK_waits;
--查看鎖等待
SELECT * FROM information_schema.INNODB_TRX;
kill trx_mysql_thread_id
關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫
關(guān)系型:(SQL)
- 通過表和表之間,行和列之間的關(guān)系進行數(shù)據(jù)的存儲
非關(guān)系型:(NOT Noly SQL NoSQL) - redis,mongodb
- 非關(guān)系型數(shù)據(jù)庫,key-value形式,對象存儲,通過對象的自身的屬性來決定
死鎖
多個事務同時占有同一個資源并相互作用,并請求鎖定其他事務所占有的資源,從而導致相互依賴,相互等待出現(xiàn)死鎖;
解決辦法:
a.Innodb引擎提供了檢測死鎖的方法,能檢測到死鎖的相互依賴,系統(tǒng)直接報錯,否則會出現(xiàn)非常慢的查詢;
b.如果查詢時間超過設定的等待時間放棄鎖請求;
c.Innodb采用的死鎖處理方式是,將持有最少行級排它鎖的事務進行回滾。
死鎖配置:
Innodb_lock_wait_timeout 超時時間設置
行鎖的本質(zhì)是鎖定主鍵索引
如果只鎖定的是非主鍵索引,會先鎖定非主鍵索引,最終找到主鍵索引。
如果沒有索引會表鎖定;
查詢表被鎖定的方法
show open TABLES where In_use >0;
show processlist;
InnoDB和MyISAM的區(qū)別
InnoDB:
- 支持事務
- 外鍵約束
- 數(shù)據(jù)行鎖定
MyISAM
- 全文索引
- 表空間較小
物理空間位置
所有的數(shù)據(jù)庫文件都存在data目錄下,一個文件夾對應一個數(shù)據(jù)庫;
本質(zhì)還是文件的存儲
MySQL引擎在物理文件上的區(qū)別
- InnoDB在數(shù)據(jù)庫表中只有一個.frm文件,以及上級目錄下的ibdata1文件
- MYISAM 對應文件
- .frm 表結(jié)構(gòu)對應的文件
- .MYD 數(shù)據(jù)文件(data)
- *.MYI 索引文件(Index)
設置數(shù)據(jù)庫表的字符集編碼
支持中文的utf8
修改表
修改表名
ALTER TABLE teacher RENAME AD teacher1;
增加表的字段 字段名 列屬性
ALTER TABLE teacher1 ADD age INT(11);
修改表的字段(修改約束)
ALTER TABLE teacher1 MODIFY age VARCHAR(11)
字段重命名
ALTER TABLE teacher1 CHANGE age age1 INT(11);
刪除表的字段
ALTER TABLE teacher1 DROP age1
刪除表
DROP TABLE IF EXISTS teacher1
** MySQL數(shù)據(jù)管理**
外鍵(了解即可)
刪除有外鍵關(guān)系的表的時候,必須要先刪除引用別人的表(從表),再刪除被引用的表(主表);
JION連表查詢(7種查詢)
inner join: 如果表中至少有一個匹配,就返回行 (交集為基準)
left join: 會從左表中返回所有的值,即使右表中沒有匹配 (左表為基準)
right join:會從右表中返回所有的值,即使左表中沒有匹配 (右表為基準)
自連接及查詢

-- 常用函數(shù)
-- 數(shù)學運算
SELECT ABS(-8) -- 絕對值
SELECT CEILING(9.4) -- 向上取整
SELECT FLOOR(9.4) -- 向下取整
SELECT RAND() -- 返回一個 0-1 之間的隨機數(shù)
SELECT SIGN() -- 判斷一個數(shù)的符號 0-0 負數(shù)返回-1 正數(shù)返回 1
-- 字符串函數(shù)
SELECT CHAR LENGTH('你好') -- 長度
SELECT CONCAT('你','在','哪') -- 拼接字符串
SELECT INSERT('我的編程',i,2,'php') -- 查詢,從某個位置開始替換某個長度
SELECT LOWER('Str')
SELECT UPPER('str')
SELECT INSTR('kaung','a') -- 查找位置a
SELECT REPLACE('str','from_str','to_str')
SELECT SUBSTR(str FROM pos FOR len)
SELECT REVERSE(str)
-- 日期函數(shù)
SELECT CURRENT_DATE() -- 獲取當前日期
SELECT CURDATE() -- 獲取當前日期
SELECT NOW() -- 獲取當前日期
SELECT LOCALTIME() -- 獲取本地時間
SELECT SYSDATE()
SELECT YEAR(now())
SELECT DAY(NOW())
DML語言
聚合函數(shù)
SELECT COUNT('name'); -- count(字段),會忽略所有的null值
SELECT COUNT(1); -- count(1),會統(tǒng)計表中的所有數(shù)據(jù),包含null,不會忽略所有的null值
SELECT COUNT(*); -- count(*),會統(tǒng)計表中的所有數(shù)據(jù),包含null,不會忽略所有的null值 如果多個列且沒有主鍵 效率 count(1) > count(*)
-- 執(zhí)行效率COUNT(列) > count(1) > count(*) 如果只有一個字段 COUNT(*)最優(yōu)
SELECT SUM([DISTINCT] expr)
SELECT AVG([DISTINCT] expr)
SELECT MIN([DISTINCT] expr)
SELECT MAX([DISTINCT] expr)
-- having 用于過濾 group 分組
SELECT SubjectName,AVG(studentResult) as 平均分,MAX(StudentResult) as 最高分,MIN(StudentResult) from result r
INNER JOIN subject sub on r.SubjectNo = sub.SubjectNo GROUP BY r.SubjectNo HAVING 平均分 > 80
事務(ACID)
要么都成功,要么都失敗
將一組SQL放在一個批次中去執(zhí)行
原子性(一起執(zhí)行不可分割)、一致性(前后狀態(tài)一致)、隔離線、持久性(事務如果沒有體檢恢復到原來的狀態(tài),提交后不可逆)
- 原子性:要么都成功,要么都失敗
- 一致性:事務前后的數(shù)據(jù)完整性要保持一致,如1000塊轉(zhuǎn)賬,最終還是1000,不會出現(xiàn)A轉(zhuǎn)出200B沒有收到200的情況
- 持久性: 事務一旦提交不可逆,被持久化到數(shù)據(jù)庫中
- 隔離性:事務的隔離性是多個用戶并發(fā)訪問的數(shù)據(jù)庫時,數(shù)據(jù)庫為每個用戶開啟的事務,不能被其他事務的操作數(shù)據(jù)所干擾,事務之間要相互隔離
隔離所導致的問題
臟讀:指一個 事務 讀取了另一個 事務 未提交的數(shù)據(jù)
不可重復讀:在一個事務內(nèi)讀取表中某一行數(shù)據(jù),多次讀取結(jié)果不同
幻讀:在一個事務內(nèi)讀取到了別人的事務插入的數(shù)據(jù),導致前后讀取數(shù)據(jù)不一致
1、臟數(shù)據(jù)所指的就是未提交的數(shù)據(jù)。也就是說,一個事務正在對一條記錄做修改,在這個事務完成并提交之前,這條數(shù)據(jù)是處于待定狀態(tài)的(可能提交也可能回滾),這時,第二個事務來讀取這條沒有提交的數(shù)據(jù),并據(jù)此做進一步的處理,就會產(chǎn)生未提交的數(shù)據(jù)依賴關(guān)系。這種現(xiàn)象被稱為臟讀。
2、不可重復讀(Non-Repeatable Reads):一個事務先后讀取同一條記錄,而事務在兩次讀取之間該數(shù)據(jù)被其它事務所修改,則兩次讀取的數(shù)據(jù)不同,我們稱之為不可重復讀。
3、幻讀(Phantom Reads):一個事務按相同的查詢條件重新讀取以前檢索過的數(shù)據(jù),卻發(fā)現(xiàn)其他事務插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象就稱為幻讀。
4、幻讀是指當事務不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務對一個表中的數(shù)據(jù)進行了修改,比如這種修改涉及到表中的“全部數(shù)據(jù)行”。同時,第二個事務也修改這個表中的數(shù)據(jù),這種修改是向表中插入“一行新數(shù)據(jù)”。那么,以后就會發(fā)生操作第一個事務的用戶發(fā)現(xiàn)表中還存在沒有修改的數(shù)據(jù)行,就好象發(fā)生了幻覺一樣.一般解決幻讀的方法是增加范圍鎖RangeS,鎖定檢索范圍為只讀,這樣就避免了幻讀。
MySQL備份
數(shù)據(jù)庫備份的方式
- 直接拷貝物理文件(data文件)
- 在可視化數(shù)據(jù)庫工具中,手動導出
- mysqldump在命令行導出
#導出
kuangdeMacBook-Pro:~ kuang$ mysqldump -hlocalhost -uroot -p123456 school student > D:/a.sql
#導入
進入mysql
執(zhí)行:source D:/a.sql
主從復制
- 主數(shù)據(jù)庫將改變的數(shù)據(jù)記錄在本地的二進制日志中(binary log);
- 從數(shù)據(jù)庫開啟IO現(xiàn)程,將主庫中的log,拷貝到自己的relay log(中繼日志文件)中
- 從數(shù)據(jù)庫通過SQL線程將日志文件數(shù)據(jù)讀取到數(shù)據(jù)庫中
MYSQL主從復制,異步,串行,延遲
master:slave = 1:n
配置: 主機
1.關(guān)閉防火墻,
2.允許遠程訪問
3.conf中配置server-id=1 唯一標識
- 配置二進制文件路徑log-bin,放到data/mysql-bin目錄下
- 錯誤記錄文件路徑log-err 放到data/mysql-err
- 主從同步時 忽略的數(shù)據(jù)庫
binlog-ingore-db=mysql - (可選)指定主從同步時,同步哪些數(shù)據(jù)
binlog-do-db=test -
主機授權(quán)哪臺計算機中的數(shù)據(jù)庫是自己的從數(shù)據(jù)庫
image.png
從機
1.conf中配置server-id=2 唯一標識
- log-bin=mysql-bin 路徑
- replicate-do-db=test
-
linux中的數(shù)據(jù) 授權(quán)哪臺計算機是自己的的主機
5.進入從機的MySQL中配置主機相關(guān)信息
如圖:
image.png


開啟主從同步
開啟后查詢:
show master status \G
show slave status \G
觀察:Slave_IO_Running和Slave_SQL-Running是否正常
讀寫分離(阿里云Mysql服務現(xiàn)在支持讀寫分離功能)
主從同步的基礎上,實現(xiàn)讀寫分離,需要中間件(MyCat)
mysql中解決并發(fā)問題(臟讀、幻讀、不可重復讀)方法:讀寫分離,因為讀操作沒有并發(fā)問題。如果不用讀寫分離需要加鎖處理并發(fā)問題,效率低下
mycat的作用
MyCat實現(xiàn)MySQL讀寫分離,以及主庫出現(xiàn)故障的時候從庫自動變?yōu)閷憥臁V鲙旎謴秃?,建議方案是讓之前的主庫變?yōu)閺膸臁?/h3>
關(guān)于讀寫分離數(shù)據(jù)一致性問題,MyCat提供了一個slaveThreshold延遲配置,延遲時間大于配置時間后,從庫會被忽略掉,直接查詢主庫數(shù)據(jù),防止讀到舊數(shù)據(jù)。
數(shù)據(jù)一致性問題,還有種方案,就是半同步復制,即主庫在提交事務前會等待從庫是否以及成功復制日志,若成功則提交。這樣的話寫效率會低
- 讀寫分離
- 分表分庫
- 水平拆分(MyCat):訂單數(shù)據(jù)(訂單數(shù)據(jù)1、訂單數(shù)據(jù)2、訂單數(shù)據(jù)3)
- 垂直拆分 (微服務):系統(tǒng)(訂單數(shù)據(jù)庫、用戶數(shù)據(jù)庫)
防止MyCat單點故障,MyCat集群部署;
haproxy:入口分流,搭建多個MyCat集群;
haproxy 也需要集群部署,防止單點故障
集群:防止單點故障
去中心化:
- 多個節(jié)點之間彼此發(fā)送心跳 (感知對方是否存活)
- 維護一個虛擬ip
keepalived:發(fā)送心跳,虛擬IP和haproxy的IP綁定,外界訪問虛擬IP時,實際指向?qū)嶋H綁定IP,若綁定的失效,自動綁定另外一個IP

完整實現(xiàn),需要6臺服務器
MyCat配置



分庫分表主鍵自增id問題
文章:https://blog.csdn.net/bjweimengshu/article/details/80162731?ivk_sa=1024320u
方案:生成全局唯一ID
UUID 32位 不推薦 無序
數(shù)據(jù)庫自增ID,每一次生成ID的時候,訪問數(shù)據(jù)庫,執(zhí)行語句插入一條記錄,生成一個ID,這樣一來,每次都可以生成一個遞增的ID。在分布式系統(tǒng)中可以利用DBproxy請求不同的分庫,每個分庫設置不同的初始值,步長和分庫數(shù)量相等。這樣一來DB1生成的ID是1、4、7、10,DB2生成的ID是2、5、8、11 DB3生成 3、6、9、12...
缺點:ID的生成對數(shù)據(jù)庫嚴重依賴,不但影響性能,而且一旦數(shù)據(jù)庫掛點,服務將變的不可用snowflake 雪花算法(推薦)
id=0+41bit(時間戳)+10bit(工作機器id)+12bit(序列號)
同一毫秒的ID數(shù) = 1024*4096 = 4194304
能夠支持絕大場景下的高并發(fā)
優(yōu)點:
- 生成ID時不依賴于DB,完全在內(nèi)存中生成,高性能高可用
- ID呈趨勢遞增,后續(xù)插入索引樹的時候性能較好
缺點: - 依賴于系統(tǒng)時鐘的一致性。如果某臺機器的系統(tǒng)時鐘回撥,有可能造成ID沖突,或者ID亂序。
snowflake 算法是 twitter 開源的分布式 id 生成算法,采用 Scala 語言實現(xiàn),是把一個 64 位的 long 型的 id,1 個 bit 是不用的 + 用其中的 41 bit 作為毫秒數(shù) + 用 10 bit 作為工作機器 id + 12 bit 作為序列號。
1 bit:不用,為啥呢?因為二進制里第一個 bit 為如果是 1,那么都是負數(shù),但是我們生成的 id 都是正數(shù),所以第一個 bit 統(tǒng)一都是 0。
41 bit:表示的是時間戳,單位是毫秒。41 bit 可以表示的數(shù)字多達 2^41 - 1,也就是可以標識 2^41 - 1 個毫秒值,換算成年就是表示69年的時間。
10 bit:記錄工作機器 id,代表的是這個服務最多可以部署在 2^10臺機器上哪,也就是1024臺機器。但是 10 bit 里 5 個 bit 代表機房 id,5 個 bit 代表機器 id。意思就是最多代表 2^5個機房(32個機房),每個機房里可以代表 2^5 個機器(32臺機器)。
12 bit:這個是用來記錄同一個毫秒內(nèi)產(chǎn)生的不同 id,12 bit 可以代表的最大正整數(shù)是 2^12 - 1 = 4096,也就是說可以用這個 12 bit 代表的數(shù)字來區(qū)分同一個毫秒內(nèi)的 4096 個不同的 id。
常見報錯:
Invalid DataSource:0 解決方案:防火墻、IP、端口、權(quán)限問題:臨時開放全部權(quán)限

MongoDB
NoSQL(not only sql)
- 文檔型數(shù)據(jù)庫(bson格式和json一樣)
- 機于分布式文件存儲的數(shù)據(jù)庫,C++編寫,主要用來處理大量的文檔。
- 介于關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫的中間產(chǎn)品,是非關(guān)系型數(shù)據(jù)庫中功能最豐富的,最像關(guān)系型數(shù)據(jù)庫。
應用場景:Web應用(key-value類似,Value是結(jié)構(gòu)化的,不同的是數(shù)據(jù)庫可以了解Value的內(nèi)容)
優(yōu)點:數(shù)據(jù)結(jié)構(gòu)要求不嚴格,表結(jié)構(gòu)可變,不需要像關(guān)系型數(shù)據(jù)庫一樣需要預先定義表結(jié)構(gòu)
缺點:查詢性能不高,缺乏統(tǒng)一的查詢語句
Cookie和Session
Docker
- 概念:容器、鏡像、倉庫;容器是鏡像的實例話對象
- Dockerfile 自定義鏡像文件
FROM centos
RUN yum install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
#以上執(zhí)行會創(chuàng)建 3 層鏡像。
#build成鏡像
$ docker build -t centos:v3 .
#在 Dockerfile 文件的存放目錄下,執(zhí)行構(gòu)建動作。
#以下示例,通過目錄下的 Dockerfile 構(gòu)建一個 centos:v3(鏡像名稱:#鏡像標簽)。注:最后的 . 代表本次執(zhí)行的上下文路徑,下一節(jié)會介紹。
#上下文路徑,是指 docker 在構(gòu)建鏡像,有時候想要使用到本機的文件(比如復制),docker build 命令得知這個路徑后,會將路徑下的所有內(nèi)容打包。
#如 COPY hom* /mydir/
- 自定義網(wǎng)絡,使用自定義網(wǎng)絡,可以讓容器間可以互ping聯(lián)通
- 端口映射
- docker-compose
Compose 是用于定義和運行多容器 Docker 應用程序的工具。通過 Compose,您可以使用 YML 文件來配置應用程序需要的所有服務。然后,使用一個命令,就可以從 YML 文件配置中創(chuàng)建并啟動所有服務。
Compose 使用的三個步驟:
1. 使用 Dockerfile 定義應用程序的環(huán)境。
2. 使用 docker-compose.yml 定義構(gòu)成應用程序的服務,這樣它們可以在隔離環(huán)境中一起運行。
3. 最后,執(zhí)行 docker-compose up 命令來啟動并運行整個應用程序。
# yaml 配置實例
version: "2.1"
services:
nginx:
image: nginx
ports:
- "8086:80"
volumes:
- /Users/kuang/lnmp/nginx/www:/usr/share/nginx/html
- /Users/kuang/lnmp/nginx/conf:/etc/nginx/conf.d
- /Users/kuang/lnmp/nginx/logs:/var/log/nginx
networks:
- lnmp-network
php:
image: php:5.6-fpm-alpine3.8
volumes:
- /Users/kuang/lnmp/nginx/www:/www
networks:
- lnmp-network
mysql:
image: mysql
ports:
- "3309:3306"
environment:
- MYSQL_ROOT_PASSWORD=123456
networks:
- lnmp-network
networks:
lnmp-network:
- 容器數(shù)據(jù)卷,容器期間共享數(shù)據(jù),容器和宿主機共享數(shù)據(jù)。如:共享nginx的配置文件,共享PHP的項目目錄到宿主機,在宿主機上修改代碼就會同步
-
容器的鏡像是一層一層疊加上去的,使用的是聯(lián)合文件系統(tǒng)技術(shù),boosfs(內(nèi)核)->rootfs(基礎鏡像centos/ubuntu)->鏡像->鏡像->容器
image.png
上傳本地鏡像
dockerhub賬戶
xiaoxaio 密碼:a656842722
image.png
流程圖
image.png

自定義網(wǎng)路
kuangdeMacBook-Pro:~ kuang$ docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
kuangdeMacBook-Pro:~ kuang$ docker network inspect mynet
# 使用自定義網(wǎng)路
kuangdeMacBook-Pro:/ kuang$ docker run -d -P --name nginx-net-01 --net mynet nginx
4595c1a1007c192d3879062e1d342a58b823708c2ffae0aec0c138b2e75e1a1b
kuangdeMacBook-Pro:/ kuang$ docker run -d -P --name nginx-net-02 --net mynet nginx
4e541782f0b9782894521cf43a9f426e5fc7b8a6063b9c4692592dc998ee157e
#mynet中自動創(chuàng)建網(wǎng)絡關(guān)聯(lián)
uangdeMacBook-Pro:~ kuang$ docker network inspect mynet
[
{
"Name": "mynet",
"Id": "668c48e5875c3c74f7d44697d7ccc8f4d1301530f8351dfaeb344d16a83a7edb",
"Created": "2021-06-22T12:34:07.8664049Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.0.0/16",
"Gateway": "192.168.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"4595c1a1007c192d3879062e1d342a58b823708c2ffae0aec0c138b2e75e1a1b": {
"Name": "nginx-net-01",
"EndpointID": "373eba4c55a9f0df3ef2e06766a75875aa7446a05d6e681bfbc6175abe35f0c1",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"4e541782f0b9782894521cf43a9f426e5fc7b8a6063b9c4692592dc998ee157e": {
"Name": "nginx-net-02",
"EndpointID": "b609b3bdfa75f4fb9cd2eb06bf0978f65ab2af805de3d506ac657dec631b7d53",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
Nginx
nginx啟動的時候會創(chuàng)建一個master主現(xiàn)程和多個worker線程,主線程負責調(diào)度子線程,當有客戶端訪問時,子線程是爭搶機制去獲取資源。nginx支持熱更新,當熱更新時當前在用的子線程是不會停止更新的,當資源釋放后會重新去更新。
- 反向代理
作為服務器的代理,隱藏真實服務器IP地址,對外暴露的是代理服務器地址。客戶端只能訪問代理服務器的地址,從代理服務器獲取信息,至于代理服務器從哪一臺真實服務器獲取信息,客戶端不知情。反向代理可以配置負載均衡
正向代理,客服端代理,客戶端需要配置代理服務器地址,客戶端訪問真實的服務器地址??蛻舳嗽L問代理服務器,代理服務器轉(zhuǎn)發(fā)請求,將數(shù)據(jù)返回給代理服務器,然后代理服務器將數(shù)據(jù)返回給客戶端。如翻墻,客戶端無法直接訪問Google,通過代理可以訪問。正向代理也叫轉(zhuǎn)發(fā)代理,理論上可以訪問任何域名。反向代理只能訪問代理服務器域名。 - 負載均衡
- 輪循
upstream myserver {
server 127.0.0.1:8086;
server 127.0.0.1:8087;
}
server {
listen 80;
server_name 127.0.0.1 localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /Applications/MxSrvs/www/esound_vip;
index index.html index.php;
proxy_pass http://myserver;
proxy_connect_timeout 10;
}
- 權(quán)重
upstream myserver {
server 127.0.0.1:8086 weight=1;
server 127.0.0.1:8087 weight=10;
}
server {
listen 80;
server_name 127.0.0.1 localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /Applications/MxSrvs/www/esound_vip;
index index.html index.php;
proxy_pass http://myserver;
proxy_connect_timeout 10;
}
- ip_hash
upstream myserver {
ip_hash;
server 127.0.0.1:8086 weight=1 down;
server 127.0.0.1:8087 weight=1;
}
server {
listen 80;
server_name 127.0.0.1 localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /Applications/MxSrvs/www/esound_vip;
index index.html index.php;
proxy_pass http://myserver;
proxy_connect_timeout 10;
}
客戶端請求過來的ip,映射成hash值,然后分配到一個特定的服務器里面。用戶下次請求時都會固定訪問該服務器,它的session不會跨域到其他的服務中。
地址分配:hash(ip)%服務器數(shù)量 = 模值 每個模值對應一臺服務器
如:如3臺服務器 t1、t2、t3 對應 0 1 2 5%3=2 這個hash值的客戶端會分配到t3服務器。
如何在nginx里面使用ip_hash?
image.png
直接添加ip_hash關(guān)鍵字即可,后續(xù)同一ip的訪問將只會請求同一個服務器。
注意事項
1. 一旦使用了ip_hash,當我們需要移除一臺服務器的時候,不能直接刪除這個配置項,而是需要在這臺服務器配置后面加上關(guān)鍵字down,表示不可用;
2. 因為如果直接移除配置項,會導致hash算法發(fā)生更改,后續(xù)所有的請求都會發(fā)生混亂;
- 高并發(fā),高可用集群(keepalive)備份綁定虛擬IP機制
兩臺nginx服務器,一個主服務器,一個備份服務器,當主服務器宕機的時候,自動切換到備份服務器。
keepalived插件。主服務器IP綁定虛擬服務器IP,當主服務器宕機后,備份服務器IP綁定虛擬IP。
1. 兩臺nginx服務
2. 需要keepalived
3. 需要一個虛擬ip
keepalived安裝配置

-
腳本文件,檢查nginx是否宕機
[圖片上傳中...(image.png-a7b2cc-1624457694839-0)]
image.png -
虛擬ip配置
image.png

- 動靜分離
nginx可以在該服務器下創(chuàng)建文件目錄,存放圖片,CSS,JS等靜態(tài)資源,配置好后可以直接訪問,提高效率
upstream myserver {
ip_hash;
server 127.0.0.1:8086 weight=1 down;
server 127.0.0.1:8087 weight=1;
}
server {
listen 80;
server_name 127.0.0.1 localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root /Applications/MxSrvs/www/esound_vip;
index index.html index.php;
proxy_pass http://myserver;
proxy_connect_timeout 10;
}
location /images/ {
root /Applications/MxSrvs/www/esound_vip/tp6/public/static/;
autoindex on;
}
#請求地址
http://localhost/images/home_cooperation_banner.png
PHP-FPM

PHP內(nèi)置PHP-FPM功能,需要手動設置開啟
PHP-FPM 負責管理一個進程池來處理來自 Web 服務器的 HTTP 動態(tài)請求,在 PHP-FPM 中,master 進程負責與 Web 服務器進行通信,接收 HTTP 請求,再將請求轉(zhuǎn)發(fā)給 worker 進程進行處理,worker 進程主要負責動態(tài)執(zhí)行 PHP 代碼,處理完成后,將處理結(jié)果返回給 Web 服務器,再由 Web 服務器將結(jié)果發(fā)送給客戶端。這就是 PHP-FPM 的基本工作原理。
最大請求數(shù):最大處理請求數(shù)是指一個php-fpm的worker進程在處理多少個請求后就終止掉,master進程會重新respawn一個新的。
這個配置的主要目的是避免php解釋器或程序引用的第三方庫造成的內(nèi)存泄露。
pm.max_requests = 10240














