https://github.com/twtrubiks/django-docker-redis-tutorial
django-docker-redis-tutorial 📝
https://github.com/twtrubiks/django-docker-redis-tutorial
django docker redis tutorial
Last synced: 10 months ago
JSON representation
django-docker-redis-tutorial 📝
- Host: GitHub
- URL: https://github.com/twtrubiks/django-docker-redis-tutorial
- Owner: twtrubiks
- License: mit
- Created: 2018-03-31T13:52:32.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2022-06-20T08:17:28.000Z (over 3 years ago)
- Last Synced: 2025-04-15T12:12:15.776Z (10 months ago)
- Topics: django, docker, redis, tutorial
- Language: Python
- Homepage:
- Size: 148 KB
- Stars: 43
- Watchers: 3
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# django-docker-redis-tutorial
django-docker-redis-tutorial 基本教學 📝
* [Youtube Tutorial Part1 - docker 安裝 redis 以及 redis 基本指令](https://youtu.be/BhO2ADEj_EE)
* [Youtube Tutorial Part2 - django-redis 以及 redis api 介紹](https://youtu.be/fX_3UTKgjI8)
* [Youtube Tutorial Part3 - redis 應用場合以及實戰](https://youtu.be/xFNkpyd4Ues)
## 前言

Redis 是 open source,也是 in-memory data structure store ( key-value ),常被使用在 database、cache 、
message broker,像是可以透過 cache 減輕 database 的壓力 ( redis 讀寫速度比一般的 database 快非常多 ),
而 message broker 可以用在像是 Celery 的應用( Celery 的應用可參考我之前寫的 [django-celery-tutorial](https://github.com/twtrubiks/django-celery-tutorial) 以及
[docker-django-celery-tutorial](https://github.com/twtrubiks/docker-django-celery-tutorial)。
透過這篇文章,你將會學會
* [透過 docker 安裝 redis](https://github.com/twtrubiks/django-docker-redis-tutorial#%E9%80%8F%E9%81%8E-docker-%E5%AE%89%E8%A3%9D-redis)
* [redis 基本指令](https://github.com/twtrubiks/django-docker-redis-tutorial#redis-%E5%9F%BA%E6%9C%AC%E6%8C%87%E4%BB%A4)
* [django-redis 介紹](https://github.com/twtrubiks/django-docker-redis-tutorial#django-redis)
* [透過 low-level cache API 把玩 redis](https://github.com/twtrubiks/django-docker-redis-tutorial#%E9%80%8F%E9%81%8E-low-level-cache-api-%E6%8A%8A%E7%8E%A9-redis)
* [redis 應用場合](https://github.com/twtrubiks/django-docker-redis-tutorial#redis-%E6%87%89%E7%94%A8%E5%A0%B4%E5%90%88)
## 教學
* [Youtube Tutorial Part1 - docker 安裝 redis 以及 redis 基本指令](https://youtu.be/BhO2ADEj_EE)
在開始教學前,建議大家可以先閱讀官方的 [Redis Persistence](https://redis.io/topics/persistence) ,
裡面詳細的介紹了 **RDB persistence** 以及 **AOF persistence** 的觀念,這兩個觀念很重要:+1:
### 透過 docker 安裝 redis
[docker redis](https://hub.docker.com/_/redis/)
請在命令提示字元 ( cmd ) 直接執行以下指令
```cmd
docker run --name some-redis -p 6379:6379 -d redis redis-server --appendonly yes
```
如果要設定密碼
```cmd
docker run --name some-redis -p 6379:6379 -d redis redis-server --appendonly yes --requirepass "changeme"
```
以上這段指令,比較需要特別解釋的就是 `--appendonly`,當如果你沒有設定時,
假如今天斷電或是不小心意外終止 redis,可能會遺失當下的資料,如果我們設定了 Append-only file ( AOF ),
AOF 預設的 policy 是每秒寫入一次 ( 當然,還是有可能會遺失一秒的資料,但相對比 RDB ( Snapshotting,
因為預設的是存在硬碟上,binary file 為 dump.rdb,所以稱為 RDB ),AOF 比起 RDB 有更好的 Persistence 。
當選擇使用 AOF 時 ,如果重起 redis,會依照 AOF 去重新建立狀態。
更多詳細資料可參考 [append-only-file](https://redis.io/topics/persistence#append-only-file)。
或是直接使用 [docker-compose.yml](docker-compose.yml).
### redis 基本指令
確認建立完成後,即可使用 redis-cli 開始玩 redis
```cmd
docker exec -it redis-cli
```
如果你有設定密碼要加上 `-a`
```cmd
docker exec -it redis-cli -a changeme
```
更多 redis 可參考 [redis command](https://redis.io/commands/) 以及支援的 [redis data-types](https://redis.io/docs/manual/data-types/)。
```cmd
127.0.0.1:6379> ping
PONG
```
set key
```cmd
127.0.0.1:6379> set id twtrubiks
OK
```
get key
```cmd
127.0.0.1:6379> get id
"twtrubiks"
```
exists key,更多可參考 [EXISTS key](https://redis.io/commands/exists)
```cmd
# if the key exists.
127.0.0.1:6379> exists id
(integer) 1
# if the key does not exist.
127.0.0.1:6379> exists not_exist
(integer) 0
```
設定 key 一個有效時間 ( Redis 常常拿來當做是 Cache ),更多可參考 [EXPIRE key seconds](https://redis.io/commands/expire)
```cmd
127.0.0.1:6379> set name twtrubiks
OK
127.0.0.1:6379> get name
"twtrubiks"
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> get name
"twtrubiks"
# Wait for 10 seconds and try again
127.0.0.1:6379> get name
(nil)
```
刪除 key,更多可參考 [DEL key](https://redis.io/commands/del)
```cmd
127.0.0.1:6379> set num 1
OK
127.0.0.1:6379> del num
(integer) 1
127.0.0.1:6379> get num
(nil)
```
一次刪除全部的 key
```cmd
127.0.0.1:6379> flushall
OK
```
得到目前全部的 keys,更多可參考 [KEYS pattern](https://redis.io/commands/keys)
```cmd
keys *
```
TTL key,查看目前還剩多久時間會 timeout,
更多可參考 [TTL key](https://redis.io/commands/ttl)
```cmd
127.0.0.1:6379> set name twtrubiks_ttl
OK
127.0.0.1:6379> expire name 10
(integer) 1
# Wait for 4 seconds and try again
127.0.0.1:6379> ttl name
(integer) 6
```
PERSIST key,將 key 從 volatile ( a key with an expire set ) 轉變成
persistent ( a key that will never expire as no timeout is associated ),
說白話一點,就是將 key 轉變成永遠不會過期 ( timeout ),更多可參考 [PERSIST key](https://redis.io/commands/persist)
```cmd
127.0.0.1:6379> set mykey hello
OK
127.0.0.1:6379> expire mykey 20
(integer) 1
# Wait for 5 seconds and try again
127.0.0.1:6379> ttl mykey
(integer) 15
127.0.0.1:6379> persist mykey
(integer) 1
127.0.0.1:6379> TTL mykey
(integer) -1
# -1 if the key exists but has no associated expire.
```
選擇資料庫,有 16 個資料庫 ( 0-15 ),預設是第 0 個資料庫,
如下方範例為切換到第一個資料庫,
```cmd
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>
```
redis 非常適合投票這種使用情境,可參考以下範例
```cmd
# 投給 a 一票
127.0.0.1:6379> zincrby vote 1 a
"1"
# 投給 b 兩票
127.0.0.1:6379> zincrby vote 2 b
"2"
# 投給 a 三票
127.0.0.1:6379> zincrby vote 3 a
"4"
# 查看 a 總投票數
127.0.0.1:6379> zscore vote a
"4"
# 得到 a 排名 ( 由高到低 )
127.0.0.1:6379> zrevrank vote a
(integer) 0
# 得到 b 排名 ( 由高到低 )
127.0.0.1:6379> zrevrank vote b
(integer) 1
# 得到前10名 ( 由高到低 )
127.0.0.1:6379> zrevrange vote 0 9
1) "a"
2) "b"
# 得到前10名以及對應的分數 ( 從高到低 )
127.0.0.1:6379> zrevrange vote 0 9 withscores
1) "a"
2) "4"
3) "b"
4) "2"
```
由於 redis command 很多,這邊不可能一一介紹,所以更詳細的可參考 [commands](https://redis.io/commands/):smile:
## django-redis
* [Youtube Tutorial Part2 - django-redis 以及 redis api 介紹](https://youtu.be/fX_3UTKgjI8)
接下來和大家介紹 [django-redis](https://github.com/jazzband/django-redis) 這個套件,
我將簡單介紹他的使用方法,請先安裝套件
```python
pip install django-redis
```
接著在 [settings.py](https://github.com/twtrubiks/django-docker-redis-tutorial/blob/master/django_docker_redis_tutorial/settings.py) 中加入下方程式碼
```python
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://localhost:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
```
Django 預設的 session 是存放在 database 中,但這邊要將他修改成 redis ,
修改的方式很簡單,只需要將 SESSION_ENGINE 改成 `django.contrib.sessions.backends.cache` 即可,
Configure as session backend,在 [settings.py](https://github.com/twtrubiks/django-docker-redis-tutorial/blob/master/django_docker_redis_tutorial/settings.py) 中加入下方程式碼
```python
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
```
詳細的 Session 參數介紹,可參考 Django 官網的 [sessions](https://docs.djangoproject.com/en/4.0/topics/http/sessions/) 文件,
設定完成後,Session 將會儲存在 redis 中( 速度更快 ),
如果你不了解 Session ,可以參考我之前寫的這篇 [Session](https://github.com/twtrubiks/CSRF-tutorial#session),
Session 存在 redis 中的範例,後面我會再介紹給各位。
有時候我們可能需要 access 原生的 Redis 功能,所以這時候就需要採用下面的方式使用 redis,
```python
>>> from django_redis import get_redis_connection
>>> con = get_redis_connection("default")
>>> con
```
可參考 [Raw client access](https://github.com/jazzband/django-redis#raw-client-access)
## 透過 low-level cache API 把玩 redis
官方文件可參考 [The low-level cache API](https://docs.djangoproject.com/en/4.0/topics/cache/#the-low-level-cache-api),
直接使用 Python Console 操作以下指令,
`set(key, value, timeout)`
```python
>>> from django.core.cache import cache
>>> cache.set('my_key', 'hello, world!')
True
>>> cache.get('my_key')
'hello, world!'
```
`set(key, value, timeout)` and `get(key)`
timeout 如果沒設定或是設定為 None 時,資料將為 forever
```python
>>> from django.core.cache import cache
>>> cache.set('my_key', 'hello, world!')
True
>>> cache.get('my_key')
'hello, world!'
```
設定 timeout 為 10 秒
```python
>>> from django.core.cache import cache
>>> cache.set('key_test', 'hello, world test !',10)
True
## if key_test has expired ( not exist ) , show has expired
>>> cache.get('key_test', 'has expired')
'hello, world test !'
## Wait 10 seconds for 'my_key' to expire...
>>> cache.get('key_test', 'has expired')
'has expired'
```
`add()`
如果這個 key 不存在,就會設定指定的 key,如果 key 已經存在,則**不會更新**既有的 key 值
```python
>>> cache.set('my_key', 'hello, world!')
True
>>> cache.get('my_key')
'hello, world!'
>>> cache.add('my_key','demo')
>>> cache.get('my_key')
'hello, world!'
>>> cache.add('my_key_2','test2')
True
```
`get_or_set()`
可以使用這個來取得 ( 設定 ) key 值 , 假如這個 key 不存在 ,就設定 key 值 ,如果存在就將 key 值顯示出來
```python
>>> cache.get('my_new_key')
>>> cache.get_or_set('my_new_key', 'my new value')
'my new value'
```
`set_many()` and `get_many()`
```python
>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
OrderedDict([('a', 1), ('b', 2), ('c', 3)])
```
`delete()` and `delete_many()`
```python
>>> cache.delete('a')
1
>>> cache.delete_many(['a', 'b', 'c'])
2
```
`clear()`
```python
>>> cache.clear()
```
`incr()` and `decr()`
```python
>>> cache.set('num', 1)
True
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
cache.decr('num',2)
9
# A ValueError will be raised if you attempt to increment or decrement a nonexistent cache key
cache.get('test')
cache.decr('test',2)
>>> ValueError: Key ':1:tst' not found
```
### Cache versioning
[Cache versioning](https://docs.djangoproject.com/en/4.0/topics/cache/#cache-versioning)
`incr_version()` and `decr_version()`
```python
# default version =1
>>> cache.set('my_key', 'hello world!')
True
>>> cache.get('my_key',version=1)
'hello world!'
>>> cache.get('my_key',version=2)
# incr_version
>>> cache.incr_version('my_key')
2
>>> cache.get('my_key',version=2)
'hello world!'
>>> cache.get('my_key',version=1)
>>> cache.set('my_key', 'test', version=1)
True
>>> cache.get('my_key',version=1)
'test'
>>> cache.get('my_key',version=2)
'hello world!'
```
接著可以使用 redis-cli 觀看,
```cmd
127.0.0.1:6379> keys *
1) ":1:my_key"
2) ":2:my_key"
```
有沒有發現一件事情,我們明明設定的是 `cache.set('my_key', 'test', version=1)`,
但為什麼透過 django 設定的 key 都會變成 `":1:my_key"` 這樣的格式呢 ?
原因是因為 django cache 本身的機制,
[default.py#L706](https://github.com/jazzband/django-redis/blob/master/django_redis/client/default.py#L706)
```python
def make_key(
self, key: Any, version: Optional[Any] = None, prefix: Optional[str] = None
) -> CacheKey:
if isinstance(key, CacheKey):
return key
if prefix is None:
prefix = self._backend.key_prefix
if version is None:
version = self._backend.version
return CacheKey(self._backend.key_func(key, prefix, version))
```
django 使用 make_key 建立新的 key ,原始的 key 在 `_backend.key_func` 裡。
[base.py#L31](https://github.com/django/django/blob/main/django/core/cache/backends/base.py#L31)
```python
def default_key_func(key, key_prefix, version):
"""
Default function to generate keys.
Construct the key used by all other methods. By default, prepend
the `key_prefix`. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
return "%s:%s:%s" % (key_prefix, version, key)
def get_key_func(key_func):
"""
Function to decide which key function to use.
Default to ``default_key_func``.
"""
if key_func is not None:
if callable(key_func):
return key_func
else:
return import_string(key_func)
return default_key_func
class BaseCache:
_missing_key = object()
def __init__(self, params):
timeout = params.get("timeout", params.get("TIMEOUT", 300))
if timeout is not None:
try:
timeout = int(timeout)
except (ValueError, TypeError):
timeout = 300
self.default_timeout = timeout
options = params.get("OPTIONS", {})
max_entries = params.get("max_entries", options.get("MAX_ENTRIES", 300))
try:
self._max_entries = int(max_entries)
except (ValueError, TypeError):
self._max_entries = 300
cull_frequency = params.get("cull_frequency", options.get("CULL_FREQUENCY", 3))
try:
self._cull_frequency = int(cull_frequency)
except (ValueError, TypeError):
self._cull_frequency = 3
self.key_prefix = params.get("KEY_PREFIX", "")
self.version = params.get("VERSION", 1)
self.key_func = get_key_func(params.get("KEY_FUNCTION"))
```
這也就是為什麼透過 django 設定的 key 都會變成 `%s:%s:%s` 這樣的格式了。
## redis 應用場合
* [Youtube Tutorial Part3 - redis 應用場合以及實戰](https://youtu.be/xFNkpyd4Ues)
redis 可以應用的場合真的非常的多,這次的 demo 將使用到以下情境 ( 其他的情境大家可以再自行 google 了解 ),
* 統計頁面點擊數
當需要記錄頁面的瀏覽次數( 或點擊數 )時,就非常適合使用 redis,為什麼不使用 db 呢 ?
因為假如有非常大量的人瀏覽( 或點擊 )網頁時,可能會導致 db 的鎖互搶,影響到效能。
關於鎖這部份,可以稍微參考一下我之前寫的 [django-transactions-tutorial](https://github.com/twtrubiks/django-transactions-tutorial),裡面有稍微
提到部分的概念。
images/[views.py](https://github.com/twtrubiks/django-docker-redis-tutorial/blob/master/images/views.py) 中片段程式碼
```python
cache.get_or_set('click', 0, timeout=None)
total_views = cache.incr('click')
```
這段程式碼相當簡單,當你瀏覽到這個頁面時,就將 `click` 這個 key 加一,
然後就可以統計出目前有多少人瀏覽過你的頁面 ( 聰明的你現在一定想到,
那我就一直瘋狂 F5 不就可以一直刷瀏覽數量了嗎 ?沒錯,但這個問題大家
可以自行想想,這邊只是帶給大家簡單的概念 )。
* 排行榜
前面介紹 redis 適合投票這種情境,當然,也適合排行榜這種使用情境,
images/[views.py](https://github.com/twtrubiks/django-docker-redis-tutorial/blob/master/images/views.py) 中片段程式碼
```python
def index(request):
......
rank = con.zrevrange(name='images', start=0, end=9, withscores=True, score_cast_func=int)
rank_seq = [
{"url": str(r[0], 'utf-8'),
"value": r[1]}
for r in rank
]
return render(request, 'images/index.html', {
'images': images_seq,
'total_views': total_views,
'ranks': rank_seq,
})
def detail(request, image_id):
image = get_object_or_404(Image, id=image_id)
total_views = con.zincrby(name='images', amount=1 ,value=image.url)
return render(request,
'images/detail.html', {
'image': image,
'total_views': int(total_views)
})
```
在 `detail` 中,我們將圖片的 url 存到 images 這個 key 值中,並且給他加一,
在 `index` 中,透過 `zrevrange` 這個方法統計目前圖片被瀏覽次數的排名。
* Session Cache
為了方便介紹,這邊直接使用登入 admin 後台來觀察 session,直接瀏覽 [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/) ,
預設的帳號密碼為 ( twtrubiks / password123 ),
登入後你會發現,你的 redis 多了 session 的 key 值( 而 database 中沒有增加 ),如下圖
redis 中多了 session 的 key


如果沒特別另外設定,django 預設是存放在 database ( django_session 表格 ),
這邊是空的很正常,因為我們已經設定 redis 了,

* 減輕 database 壓力
這邊和大家簡單說明如何減輕 database 的壓力:satisfied:
可參考 musics/[views.py](https://github.com/twtrubiks/django-docker-redis-tutorial/blob/master/musics/views.py)
```python
# Create your views here.
class MusicViewSet(viewsets.ModelViewSet):
queryset = Music.objects.all()
serializer_class = MusicSerializer
permission_classes = (IsAuthenticated,)
parser_classes = (JSONParser,)
def list(self, request, **kwargs):
if self.request.version == '1.0':
if 'musics' in cache:
# from cache get musics
musics = cache.get('musics')
else:
musics = Music.objects.all()
serializer = MusicSerializer(musics, many=True)
musics = serializer.data
# store data to cache
cache.set('musics', musics, timeout=None)
else:
musics = Music.objects.all()
serializer = MusicSerializer(musics, many=True)
musics = serializer.data
return Response(musics, status=status.HTTP_200_OK)
```
假設 database 的 music 這張表格中有一萬筆資料,然後如果每個人每次發送 request 過來都要重新撈這一萬筆資料,
會對資料庫造成很大的壓力,也沒什麼效率,這時候就可以透過 redis 來幫助我們。
程式其實很簡單,當 redis 中沒有 musics 這個 key 的時候,我就去資料庫撈,然後將撈到的資料存進 redis 中,
這樣當下一次我還需要時,就可以直接從 redis 中取得資料( 不需要再重新從資料庫中撈資料 )。
你可能會問我為什麼要用 `self.request.version` ,原因是等等我們模擬簡單壓力測試要使用的:grinning:
Django 的 version 使用方法可參考我之前寫的 [django-rest-framework-tutorial#versioning](https://github.com/twtrubiks/django-rest-framework-tutorial#versioning)。
以下是兩個的比較,
第一次執行會從資料庫撈一萬筆的資料

第二次開始,都會從 **redis** 撈一萬筆的資料 ( 速度快很多 )

從秒數來看,速度至少快了 16 倍,你可能會說,其實還好阿:confused:
不過,假設今天有 100 個人呢? 相信就非常有感了:smirk:
讓我們透過數據說話,使用 [loadtest](https://www.npmjs.com/package/loadtest) 來簡單模擬,
先安裝 [loadtest](https://www.npmjs.com/package/loadtest)
```cmd
npm install --location=global loadtest
```
使用方法
```cmd
loadtest [-n requests] [-c concurrency] [-k] URL
```
先來測試 **沒有 redis** 的情況 ( 50 個 request )
```cmd
loadtest -H "Authorization: Basic dHd0cnViaWtzOnBhc3N3b3JkMTIz" -n 50 -k http://127.0.0.1:8000/api/musics/
```
如下圖,慢到我不想等他跑完 :sweat: ( 還在 38%)

再來測試 **有 redis** 的情況 ( 50 個 request )
```cmd
loadtest -H "Authorization: Basic dHd0cnViaWtzOnBhc3N3b3JkMTIz" -H "Accept: application/json;version=1.0" -n 50 -k http://127.0.0.1:8000/api/musics/
```
如下圖,很快就跑完了,而且花最久的時間是 229ms

可以發現,有 redis 的情況下,效能好很多:heart_eyes:
後面我有再補充另一段 code, [musics/views.py](https://github.com/twtrubiks/django-docker-redis-tutorial/blob/master/musics/views.py)
```python
def list_lock(self, request, **kwargs):
if self.request.version == '1.0':
if 'musics' in cache:
# from cache get musics
musics = cache.get('musics')
else:
if con.set("my_key", "secret", nx=True, px=1000):
musics = Music.objects.all()
serializer = MusicSerializer(musics, many=True)
musics = serializer.data
cache.set('musics', musics, timeout=None)
print('store data to cache')
else:
print("pending")
while 1:
time.sleep(0.5)
print('sleep')
if 'musics' in cache:
musics = cache.get('musics')
print('break')
break
......
return Response(musics, status=status.HTTP_200_OK)
```
主要是透過 redis 的鎖, 確保當下只有一個 request 可以進 db 拿資料,
剩下的 request 全部 pending, 直到 redis 有資料.
請搭配以下測試
```cmd
loadtest -H "Authorization: Basic dHd0cnViaWtzOjEyMw==" -H "Accept: application/json;version=1.0" -n 15 -c 15 -k http://127.0.0.1:8000/api/musics/
```
(要多測幾次, 確保有顯示 "pending" )

相信這時候大家又會問,不過這樣子 redis 裡面的資料有很高的機會是舊的,沒錯,所以更好的方法,
可以搭配 Celery 設定排成 ( Celery 可以參考我之前寫的 [docker-django-celery-tutorial](https://github.com/twtrubiks/docker-django-celery-tutorial) ),一天或
一段時間更新一次 ( 將 db 裡的資料讀出來寫進 redis 中 ),這樣就可以確保 redis 裡面的資料盡量和 db 裡面一樣。
## 執行畫面
請直接瀏覽 [http://127.0.0.1:8000/images/](http://127.0.0.1:8000/images/),你會發現有一個 View Count : 1

如果你一直重新整理 ( 狂按 F5 ),會發現 View Count 一直增加

可以點選自己喜歡的圖片進去觀看,也會有屬於這張照片的 View Count

如果你一直重新整理 ( 狂按 F5 ),也會發現 View Count 一直增加

回到首頁 ( [http://127.0.0.1:8000/images/](http://127.0.0.1:8000/images/) ),你會發現多了一個排行榜

當瀏覽越多圖片,排行榜就會向下面這樣 (也顯示每張圖片的 View Count )

## 後記
這次帶大家了解 redis 的一些基礎應用,相信大家一定覺得 redis 真的很有趣,然後 redis 可以做的事情絕對不只
這次教學所帶給大家的,他可以做的應用真非常的多,本教學只是帶給大家一個基礎,了解到底什麼是 redis,
然後可以做怎麼樣的應用,希望這教學對想了解 redis 多少有幫助,謝謝大家:relaxed:
## 執行環境
* Python 3.8
## Reference
* [Django](https://www.djangoproject.com/)
* [django-redis](https://github.com/jazzband/django-redis)
* [Redis](https://redis.io/)
* [loadtest](https://www.npmjs.com/package/loadtest)
## Donation
文章都是我自己研究內化後原創,如果有幫助到您,也想鼓勵我的話,歡迎請我喝一杯咖啡:laughing:

[贊助者付款](https://payment.opay.tw/Broadcaster/Donate/9E47FDEF85ABE383A0F5FC6A218606F8)
## License
MIT licens