21.1 nosql介紹
什麼是NoSQL
•非關係型數據庫就是NoSQL,關係型數據庫代表MySQL 對於關係型數據庫來說,是需要把數據存儲到庫、表、行、字段裏,查詢的時候根據條件一行一行地去匹配,當量非常大的時候就很耗費時間和資源,尤其是數據是需要從磁盤裏去檢索
•NoSQL數據庫存儲原理非常簡單(典型的數據類型爲k-v),不存在繁雜的關係鏈,比如mysql查詢的時候,需要找到對應的庫、表(通常是多個表)以及字段。
•NoSQL數據可以存儲在內存裏,查詢速度非常快
•NoSQL在性能表現上雖然能優於關係型數據庫,但是它並不能完全替代關係型數據庫
•NoSQL因爲沒有複雜的數據結構,擴展非常容易,支持分佈式
常見NoSQL數據庫
•k-v形式的:memcached、redis 適合儲存用戶信息,比如會話、配置文件、參數、購物車等等。這些信息一般都和ID(鍵)掛鉤,這種情景下鍵值數據庫是個很好的選擇。
•文檔數據庫:mongodb 將數據以文檔的形式儲存。每個文檔都是一系列數據項的集合。每個數據項都有一個名稱與對應的值,值既可以是簡單的數據類型,如字符串、數字和日期等;也可以是複雜的類型,如有序列表和關聯對象。數據存儲的最小單位是文檔,同一個表中存儲的文檔屬性可以是不同的,數據可以使用XML、JSON或者JSONB等多種形式存儲。
•列存儲 Hbase
•圖 Neo4J、Infinite Graph、OrientDB
NoSQL數據庫的四大分類
•鍵值(Key-Value)存儲數據庫
這一類數據庫主要會使用到一個哈希表,這個表中有一個特定的鍵和一個指針指向特定的數據。Key/value模型對於IT系統來說的優勢在於簡單、易部署。但是如果DBA只對部分值進行查詢或更新的時候,Key/value就顯得效率低下了。舉例如:Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB. 列存儲數據庫。
這部分數據庫通常是用來應對分佈式存儲的海量數據。鍵仍然存在,但是它們的特點是指向了多個列。這些列是由列家族來安排的。如:Cassandra, HBase, Riak. 文檔型數據庫
文檔型數據庫的靈感是來自於Lotus Notes辦公軟件的,而且它同第一種鍵值存儲相類似。該類型的數據模型是版本化的文檔,半結構化的文檔以特定的格式存儲,比如JSON。文檔型數據庫可 以看作是鍵值數據庫的升級版,允許之間嵌套鍵值。而且文檔型數據庫比鍵值數據庫的查詢效率更高。如:CouchDB, MongoDb. 國內也有文檔型數據庫SequoiaDB,已經開源。
•圖形(Graph)數據庫
圖形結構的數據庫同其他行列以及剛性結構的SQL數據庫不同,它是使用靈活的圖形模型,並且能夠擴展到多個服務器上。NoSQL數據庫沒有標準的查詢語言(SQL),因此進行數據庫查詢需要制定數據模型。許多NoSQL數據庫都有REST式的數據接口或者查詢API。如:Neo4J, InfoGrid, Infinite Graph.
因此,我們總結NoSQL數據庫在以下的這幾種情況下比較適用:1、數據模型比較簡單;2、需要靈活性更強的IT系統;3、對數據庫性能要求較高;4、不需要高度的數據一致性;5、對於給定key,比較容易映射覆雜值的環境。
21.2 memrcached介紹
Memcached是國外社區網站LiveJournal團隊開發,目的是爲了通過緩存數據庫查詢結果,減少數據庫訪問次數,從而提高動態web站點性能。
官方站點http://www.memcached.org/
數據結構簡單(k-v),數據存放在內存裏
多線程
基於c/s架構,協議簡單
基於libevent的事件處理
自主內存存儲處理(slab allowcation)
數據過期方式:Lazy Expiration 和LRU
Memcached的數據流向
Slab Allocation的原理:
將分配的內存分割成各種尺寸的塊(chunk), 並把尺寸相同的塊分成組(chunk的集合),每個chunk集合被稱爲slab。
Memcached的內存分配以Page爲單位,Page默認值爲1M,可以在啓動時通過-I參數來指定。
Slab是由多個Page組成的,Page按照指定大小切割成多個chunk。
Growth factor:
Memcached在啓動時通過-f選項可以指定 Growth Factor 增長因子。該值控制 chunk 大小的差異。默認值爲1.25。
通過 memcached-tool 命令查看指定 Memcached 實例的不同 slab 狀態,可以看到各 Item 所佔大小( chunk 大小)差距爲1.25
命令:# memcached-tool 127.0.0.1:11211 display
Memcached的數據過期方式:
•Lazy Expiration :
Memcached 內部不會監視記錄是否過期,而是在get時查看記錄的時間戳,檢查記錄是否過期。這種技術被稱爲lazy(惰性)expiration。因此,Memcached不會在過期監視上耗費CPU時間。
•LRU:
Memcached會優先使用已超時的記錄的空間,但即使如此,也會發生追加新記錄時空間不足的情況,此時就要使用名爲Least Recently Used(LRU)機制來分配空間。顧名思義,這是刪除“最近最少使用”的記錄的機制。因此,當內存空間不足時(無法從slab class獲取到新的空間時),就從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。從緩存的實用角度來看,該模型十分理想。
21.3 安裝memcached
memcached可以通過yum安裝,當然也可以去官網下載相應的源碼包進行編譯安裝,如果是初次接觸的話,先用最簡單的安裝方式或許會比較好一點,先來看看yum中可以安裝的memcached版本:
[root@localhost ~]# yum list |grep memcached
libmemcached.x86_64 1.0.16-5.el7 @base
memcached.x86_64 1.4.15-10.el7_3.1 @base
libmemcached.i686 1.0.16-5.el7 base
libmemcached-devel.i686 1.0.16-5.el7 base
libmemcached-devel.x86_64 1.0.16-5.el7 base
memcached-devel.i686 1.4.15-10.el7_3.1 base
memcached-devel.x86_64 1.4.15-10.el7_3.1 base
opensips-memcached.x86_64 1.10.5-3.el7 epel
php-ZendFramework-Cache-Backend-Libmemcached.noarch
php-pecl-memcached.x86_64 2.2.0-1.el7 epel
python-memcached.noarch 1.48-4.el7 base
uwsgi-router-memcached.x86_64 2.0.16-1.el7 epel
安裝包:
yum install -y memcached libmemcached libevent
啓動memcached服務:
systemctl start memcached
查看進程:
[root@localhost ~]# ps aux|grep memcached
memcach+ 1245 0.0 0.1 344080 1664 ? Ssl 19:37 0:00 /usr/bin/memcached -u memcached -p 11211 -m 64 -c 1024
root 1252 0.0 0.0 112720 972 pts/0 R+ 19:38 0:00 grep --color=auto memcached
•-u指定運行該服務的用戶,-p指定監聽端口,-m指定分配給這個進程的內存大小,單位是m,-c指定最大併發數量
查看端口:
[root@localhost ~]# netstat -lntp|grep memcached
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 1245/memcached
tcp6 0 0 :::11211 :::* LISTEN 1245/memcached
有兩種方式可以更改memcached的服務參數,第一種是使用絕對路徑來啓動,然後通過選項來指定參數,例如:
/usr/bin/memcached -u memcached -p XXXX -m 32 -c 2024
第二種方式是修改配置文件:
[root@localhost ~]# vim /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS=""
如果需要加上監聽的ip,可以把OPTIONS="" 改爲OPTIONS="127.0.0.1"
21.4 查看memcached狀態
三種方式查看memcached狀態:
自帶工具memcached-tool 命令:memcached-tool 127.0.0.1:11211 stats
或者echo stats |nc 127.0.0.1 11211 需要安裝nc工具yum install -y nc
若安裝libmemcached後,可以使用命令
memstat --servers=127.0.0.1:11211 查看memcached服務狀態
memcached 有個自帶的工具memcached-tool,可以用來查看memcached的狀態
主要關注curr_items 和 get_hits 用get_hits 除以 curr_items 查看命中率有多高
[root@localhost ~]# memcached-tool 127.0.0.1:11211 stats
#127.0.0.1:11211 Field Value
accepting_conns 1
auth_cmds 0
auth_errors 0
bytes 0
bytes_read 7
bytes_written 0
cas_badval 0
cas_hits 0
cas_misses 0
cmd_flush 0
cmd_get 0
cmd_set 0
cmd_touch 0
conn_yields 0
connection_structures 11
curr_connections 10
curr_items 0
decr_hits 0
decr_misses 0
delete_hits 0
delete_misses 0
evicted_unfetched 0
evictions 0
expired_unfetched 0
get_hits 0
get_misses 0
hash_bytes 524288
hash_is_expanding 0
hash_power_level 16
incr_hits 0
incr_misses 0
libevent 2.0.21-stable
limit_maxbytes 67108864
listen_disabled_num 0
pid 1245
pointer_size 64
reclaimed 0
reserved_fds 20
rusage_system 0.006359
rusage_user 0.006359
threads 4
time 1534851778
total_connections 11
total_items 0
touch_hits 0
touch_misses 0
uptime 300
version 1.4.15
我們平時需要關注get_hits(命中數量)以及curr_items(存在memcached中的項目)的值,使用get_hits的值除以curr_items的值,可以計算出命中率。這是爲了檢測memcached是否有緩存了數據以及是否能被正常的訪問這些緩存的數據
除了memcached-tool之外,還可以使用nc來查看memcached的狀態,安裝命令如下:
yum install -y nc
查看狀態命令:
[root@localhost ~]# echo stats|nc 127.0.0.1 11211
STAT pid 1245
STAT uptime 603
STAT time 1534852081
STAT version 1.4.15
STAT libevent 2.0.21-stable
STAT pointer_size 64
STAT rusage_user 0.014568
STAT rusage_system 0.007284
STAT curr_connections 10
STAT total_connections 12
STAT connection_structures 11
STAT reserved_fds 20
STAT cmd_get 0
STAT cmd_set 0
STAT cmd_flush 0
STAT cmd_touch 0
STAT get_hits 0
STAT get_misses 0
STAT delete_misses 0
STAT delete_hits 0
STAT incr_misses 0
STAT incr_hits 0
STAT decr_misses 0
STAT decr_hits 0
STAT cas_misses 0
STAT cas_hits 0
STAT cas_badval 0
STAT touch_hits 0
STAT touch_misses 0
STAT auth_cmds 0
STAT auth_errors 0
STAT bytes_read 13
STAT bytes_written 1024
STAT limit_maxbytes 67108864
STAT accepting_conns 1
STAT listen_disabled_num 0
STAT threads 4
STAT conn_yields 0
STAT hash_power_level 16
STAT hash_bytes 524288
STAT hash_is_expanding 0
STAT bytes 0
STAT curr_items 0
STAT total_items 0
STAT expired_unfetched 0
STAT evicted_unfetched 0
STAT evictions 0
STAT reclaimed 0
END
安裝libmemcached包後,還可以使用如下命令查看memcached服務狀態:
[root@localhost ~]# memstat --servers=127.0.0.1:11211
Server: 127.0.0.1 (11211)
pid: 1245
uptime: 752
time: 1534852230
version: 1.4.15
libevent: 2.0.21-stable
pointer_size: 64
rusage_user: 0.019268
rusage_system: 0.007284
curr_connections: 10
total_connections: 13
connection_structures: 11
reserved_fds: 20
cmd_get: 0
cmd_set: 0
cmd_flush: 0
cmd_touch: 0
get_hits: 0
get_misses: 0
delete_misses: 0
delete_hits: 0
incr_misses: 0
incr_hits: 0
decr_misses: 0
decr_hits: 0
cas_misses: 0
cas_hits: 0
cas_badval: 0
touch_hits: 0
touch_misses: 0
auth_cmds: 0
auth_errors: 0
bytes_read: 30
bytes_written: 2068
limit_maxbytes: 67108864
accepting_conns: 1
listen_disabled_num: 0
threads: 4
conn_yields: 0
hash_power_level: 16
hash_bytes: 524288
hash_is_expanding: 0
bytes: 0
curr_items: 0
total_items: 0
expired_unfetched: 0
evicted_unfetched: 0
evictions: 0
reclaimed: 0
21.5 memcached命令行
使用telnet可以進入memcached命令行
[root@localhost ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
使用set命令創建數據:
set key1 0 30 2
12
STORED
set key2 0 30 3
abc
STORED
命令解析:
set key1 0 30 2
set: 創建數據命令(command name)
key2: 創建一個名爲key1的key (key)
0 : 特殊標記位(flags)
30 : 定義這個數據的過期時間爲30秒(exptime)
2 : 定義這個key所能夠存儲的value長度,單位是字節 (bytes)
獲取數據:
get key1
VALUE key1 0 2
12
END
get key2
VALUE key2 0 3
abc
END
數據過期了的話就不存在了:
get key1
END
get key2
END
Memcached語法規則:
· <command name> <key> <flags> <exptime> <bytes>\r\n <data block>\r\n
注:\r\n在windows下是Enter鍵
· <command name> 可以是set, add, replace
· set表示按照相應的<key>存儲該數據,沒有的時候增加,有的時候覆蓋
· add表示按照相應的<key>添加該數據,但是如果該<key>已經存在則會操作失敗
· replace表示按照相應的<key>替換數據,但是如果該<key>不存在則操作失敗。
· <key> 客戶端需要保存數據的key
· <flags> 是一個16位的無符號的整數(以十進制的方式表示)。該標誌將和需要存儲的數據一起存儲,並在客戶端get數據時返回。客戶端可以將此標誌用做特殊用途,此標誌對服務器來說是不透明的。
· <exptime> 爲過期的時間。若爲0表示存儲的數據永遠不過期(但可被服務器算法:LRU 等替換)。如果非0(unix時間或者距離此時的秒數),當過期後,服務器可以保證用戶得不到該數據(以服務器時間爲標準)。
· <bytes> 需要存儲的字節數,當用戶希望存儲空數據時<bytes>可以爲0
· <data block>需要存儲的內容,輸入完成後,最後客戶端需要加上\r\n(直接點擊Enter)作爲結束標誌。
21.6 memcached數據導出和導入
因爲memcached的數據是存儲在內存中的,當服務需要重啓的時候,需要先讓memcached裏的數據寫到磁盤中,不然數據會丟失。所以介紹一下memcached如何導出和導入數據。
memcached中添加了如下幾個不過期的數據:
[root@localhost ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
set name 1 0 6
luoluo
STORED
set age 1 0 2
20
STORED
set key1 1 0 5
12345
STORED
添加完之後,按CTRL+]然後輸入quit退出telnet
memcached的狀態如下:
[root@localhost ~]# memcached-tool 127.0.0.1 stats
#127.0.0.1:11211 Field Value
accepting_conns 1
auth_cmds 0
auth_errors 0
bytes 219
bytes_read 192
bytes_written 3177
cas_badval 0
cas_hits 0
cas_misses 0
cmd_flush 0
cmd_get 4
cmd_set 5
cmd_touch 0
conn_yields 0
connection_structures 11
curr_connections 10
curr_items 3
decr_hits 0
decr_misses 0
delete_hits 0
delete_misses 0
evicted_unfetched 0
evictions 0
expired_unfetched 0
get_hits 1
get_misses 3
hash_bytes 524288
hash_is_expanding 0
hash_power_level 16
incr_hits 0
incr_misses 0
libevent 2.0.21-stable
limit_maxbytes 67108864
listen_disabled_num 0
pid 1245
pointer_size 64
reclaimed 0
reserved_fds 20
rusage_system 0.024226
rusage_user 0.036339
threads 4
time 1534853244
total_connections 15
total_items 5
touch_hits 0
touch_misses 0
uptime 1766
version 1.4.15
將數據導出到一個文件裏:
[root@localhost ~]# memcached-tool 127.0.0.1:11211 dump > data.txt
Dumping memcache contents
Number of buckets: 1
Number of items : 3
Dumping bucket 1 - 3 total items
查看導出文件內容:
[root@localhost ~]# cat data.txt
add name 1 1534851478 6
luoluo
add key1 1 1534851478 5
12345
add age 1 1534851478 2
20
•注意:導出的數據是帶有一個時間戳的,這個時間戳就是該條數據過期的時間點,如果當前時間已經超過該時間戳,那麼是導入不進去的,其中1534851478這樣的就是時間戳
重啓服務後將數據導入到memcached裏並獲取數據:
[root@localhost ~]# systemctl restart memcached
[root@localhost ~]# nc 127.0.0.1 11211 < data.txt
STORED
STORED
STORED
[root@localhost ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get name
END
get age
END
get key1
END
如果導入後發現沒有數據,這是因爲備份文件裏記錄的時間戳已經過期了,可以修改文件中的時間戳保證數據的有效期。可以寫一個簡單的腳本批量替換這些文件中的時間戳:
[root@localhost ~]# vi replace.sh
#!/bin/bash
hour=`date -d "+1 hour" +%s`
data_time=`cat data.txt |grep add |awk '{print $4}' |sort -n |uniq`
for i in $data_time
do
sed -i "s/$i/$hour/g" `grep $i -rl /root/data.txt`
done
[root@localhost ~]# sh replace.sh
[root@localhost ~]# cat data.txt
add name 1 1534857427 6
luoluo
add key1 1 1534857427 5
12345
add age 1 1534857427 2
20
再次導入:
[root@localhost ~]# nc 127.0.0.1 11211 <data.txt
STORED
STORED
STORED
然後再到memcached裏查看數據:
[root@localhost ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get name
VALUE name 1 6
luoluo
END
get age
VALUE age 1 2
20
END
get key1
VALUE key1 1 5
12345
END
可以看到,數據正常導入了。
21.7 php連接memcached
在LNMP、LAMP等環境下,我們需要讓php與memcached進行交互,就像php與mysql交互那樣,php也得通過某個模塊連接memcached後,才能進行數據的呈現、交互等操作。
1.先安裝php的memcache擴展,我這裏的php是LNMP環境的並且在之前已經安裝好了,所以現在進行擴展即可:
[root@localhost ~]# cd /usr/local/src/
[root@localhost src]# wget http://www.apelearn.com/bbs/data/attachment/forum/memcache-2.2.3.tgz
[root@localhost src]# tar zxf memcache-2.2.3.tgz
[root@locahost src]# cd memcache-2.2.3
[root@localhost memcache-2.2.3]# ls
config9.m4 CREDITS memcache_consistent_hash.c memcache_queue.h php_memcache.h
config.m4 example.php memcache.dsp memcache_session.c README
config.w32 memcache.c memcache_queue.c memcache_standard_hash.c
[root@localhost memcache-2.2.3]# /usr/local/php-fpm/bin/phpize # 生成config文件
Configuring for:
PHP Api Version: 20131106
Zend Module Api No: 20131226
Zend Extension Api No: 220131226
[root@localhost memcache-2.2.3]# ./configure --with-php-config=/usr/local/php-fpm/bin/php-config
[root@localhost memcache-2.2.3]# echo $?
0
[root@localhost memcache-2.2.3]# make && make install
[root@localhost memcache-2.2.3]# echo $?
0
安裝完後會有類似這樣的提示:Installing shared extensions: /usr/local/php-fpm/lib/php/extensions/no-debug-non-zts-20131226/ 會在這個路徑下生成memcache.so的文件
[root@localhost memcache-2.2.3]# ls /usr/local/php-fpm/lib/php/extensions/no-debug-non-zts-20131226/
memcache.so opcache.a opcache.so
然後修改php.ini添加一行extension="memcache.so“
[root@localhost memcache-2.2.3]# vim /usr/local/php-fpm/etc/php.ini
#找到extension=關鍵字,在其下面添加一行
extension=memcache.so
檢查/usr/local/php-fpm/bin/php -m裏是否有memcache模塊:
[root@localhost ~]# /usr/local/php-fpm/sbin/php-fpm -m|grep memcache
memcache
測試:
1.下載測試腳本:
curl www.apelearn.com/study_v2/.memcache.txt > 1.php 2>/dev/null
2.執行測試腳本:
[root@localhost ~]# /usr/local/php-fpm/bin/php 1.php
Get key1 value: This is first value<br>Get key1 value: This is replace value<br>Get key2 value: Array
(
[0] => aaa
[1] => bbb
[2] => ccc
[3] => ddd
)
出現以上數據說明沒問題
21.8 memcached中存儲sessions
在負載均衡集羣中,用戶第一次訪問的是A服務器,並且在該服務器上登錄了賬戶,這個登錄信息就保存在session中。由於是集羣的關係,所以用戶可能下一次再訪問的時候就不是訪問到A服務器上,而是訪問到B服務器上了。但是之前的session卻保存在A服務器上,那麼用戶在B服務器上就只能重新登錄一次,因爲在B服務器上並沒有session信息。所以爲了讓集羣中的服務器都能夠共享session,就可以把session存儲在一個memcached服務器中,所有的web服務器往這臺memcached服務器上讀session信息,就能做到簡單的session共享。
這裏是lnmp環境下的配置:
php服務默認會把session信息存在本地文件中,所以需要修改存儲的類型。
1.編輯php.ini配置文件添加以下兩行:如果這裏不成功在php-fpm.conf對應的pool中添加
session.save_handler = memcache # 指定session的存儲類型
session.save_path = "tcp://192.168.66.132:11211" # 指定memcached服務器的ip和端口
或者php-fpm.conf對應的pool中添加以下兩行:
php_value[session.save_handler] = memcache
php_value[session.save_path] = " tcp://192.168.66.132:11211"
2.修改完之後重啓服務:
[root@localhost ~]# /etc/init.d/php-fpm restart
Gracefully shutting down php-fpm . done
Starting php-fpm done
3.測試,下載測試文件 + 移動文件到默認站點目錄並更名 + curl測試,素質三連:
[root@localhost ~]# wget http://study.lishiming.net/.mem_se.txt
[root@localhost ~]# mv .mem_se.txt /data/wwwroot/default/1.php
[root@localhost ~]# curl localhost/1.php # 輸出內容如下格式,代表沒毛病
1514470099<br><br>1514470099<br><br>0rmd502kjrsvrnqhqtn1a0eih6
那個 0rmd502kjrsvrnqhqtn1a0eih6 就是memcached中的key,通過這個key才能去memcached裏拿值,如果沒有這個key的話,可能是配置文件沒配置對。
4.進入memcached命令行,看看有沒有存儲到對應的session數據:
[root@localhost ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get 0rmd502kjrsvrnqhqtn1a0eih6
VALUE 0rmd502kjrsvrnqhqtn1a0eih6 0 37
TEST|i:1514470099;TEST3|i:1514470099;
END
如上,可以看到get到了該鍵的值,證明沒問題,能夠正常存儲。