{"msg":"操作成功","code":200,"data":{"createBy":"admin","createTime":"2020-10-05 14:42:27","updateBy":"admin","updateTime":"2020-10-05 14:42:27","remark":null,"id":34,"articleTitle":"Redis（一）之Redis介绍","articleUrl":"redis_introduction","articleThumbnail":"https://www.asumimoe.com/imgfiles/20220908/828bf19b43f2452d8540393410257d30.png","articleFlag":"0","draftStatus":"1","reprintStatement":"1","articleSummary":"Redis 是一个开源、支持网络、基于内存、键值对的 Key-Value 数据库，使用 ANSI C 编写，并提供多种语言的 API  ，它几乎没有上手难度，只需要几分钟我们就能完成安装工作，并让它开始与应用程序顺畅协作。换句话来说，只需投入一小部分时间与精力，大家就能获得立竿见影且效果极佳的性能表现提升，就是说它是一个非常简单缓存解决方案。","articleContent":"## 了解Redis\n\nRedis 是一个开源、支持网络、基于内存、键值对的 Key-Value 数据库，使用 ANSI C 编写，并提供多种语言的 API  ，它几乎没有上手难度，只需要几分钟我们就能完成安装工作，并让它开始与应用程序顺畅协作。换句话来说，只需投入一小部分时间与精力，大家就能获得立竿见影且效果极佳的性能表现提升，就是说它是一个非常简单缓存解决方案。它支持存储的 Value 类型不仅限于字符串，支持主从同步，数据持久化等等，大家都认为 **Redis 是最流行的 Key-Value 存储数据库**\n\n## CentOS下安装Redis\n\n1. 下载解压redis\n\n   ```bash\n   wget http://download.redis.io/releases/redis-5.0.0.tar.gz\n   tar -zxvf redis-5.0.0.tar.gz\n   mv redis-5.0.0 /usr/local/redis\n   ```\n\n2. 进入目录进行编译安装\n\n   ```bash\n   cd /usr/local/redis\n   make\n   make install\n   ```\n\n   安装完成后就会在`/usr/local/bin/`目录下看到`redis-server、redis-cli`等可执行脚本。\n\n3. 配置redis.conf\n\n   ```bash\n   cd /usr/local/redis\n   vim redis.conf\n   \n   bind 127.0.0.1\t\t\t\t\t#对外提供链接的地址，只能本机连接，其他局域网内是连接不上的，可以配置多个IP\n   port 6379\t\t\t\t\t\t#默认端口号\n   daemonize no\t\t\t\t\t#守护进程，默认为弄，修改为yes后redis可以在后台运行\n   databases 16\t\t\t\t\t#(redis数据库用0,1,2,3....代表)\n   save 900 1\t\t\t\t\t\t#每900秒有1次增删改操作就同步到磁盘当中\n   dbfilename dump.rdb\t\t\t\t#rdb备份方式的文件名字（默认开启）\n   dir ./\t\t\t\t\t\t\t#备份文件存放位置\n   appendonly no\t\t\t\t\t#aof备份是否开启（默认不开启）\n   appendfilename \"appendonly.aof\"\t\t#aof备份文件名称\n   \n   appendfsync everysec\t\t\t#aof同步机制，每秒同步到磁盘当中\n   ```\n\n4. 启动\n\n   ```bash\n   redis-server /usr/local/redis/redis.conf\n   ```\n\n5. 可以连接到redis进行简单测试\n\n   ```bash\n   redis-cli -p 6379    #连接 Redis ，默认是本机的。\n   \n   keys *       \t\t#查看现在所有 key\n   set name circle    \t#设置一个key为`name`，value为`circle`的缓存对象。\n   get name     \t\t#获取key为`name`的缓存\n   ```\n\n6. 关闭redis\n\n   ```bash\n   redis-cli shutdown\n   ```\n\n## Redis存储类型\n\nredis数据类型共有5类：String（字符串）、Hash（key-field-value）、List（列表）、Set（集合）、SortedSet（有序集合）。这五种类型redis都不会直接存储，而是通过redisObject对象进行存储。\n\nredisObject对象非常重要，Redis对象的类型、内部编码、内存回收、共享对象等功能，都需要redisObject支持，下面将通过redisObject的结构来说明它是如何起作用的。\n\n### RedisObject的定义如下（不同版本的redis可能稍有不同）:\n\n```javascript\ntypedef struct redisObject {\n　　unsigned type:4;\n　　unsigned encoding:4;\n　　unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */\n　　int refcount;\n　　void *ptr;\n} robj;\n```\n\n1. type\n\n   type字段表示对象的类型，占4个比特；目前包括REDIS_STRING(字符串)、REDIS_LIST (列表)、REDIS_HASH(哈希)、REDIS_SET(集合)、REDIS_ZSET(有序集合)。\n\n   当我们执行type命令时，便是通过读取RedisObject的type字段获得对象的类型；如下图所示：\n\n   ```c\n   127.0.0.1:6379> set mystr helloworld\n   OK\n   127.0.0.1:6379> type mystr\n   string\n   ```\n\n2. encoding\n\n   encoding表示对象的内部编码，占4个比特。\n\n   对于Redis支持的每种类型，都有至少两种内部编码，例如对于字符串，有int、embstr、raw三种编码。通过encoding属性，Redis可以根据不同的使用场景来为对象设置不同的编码，大大提高了Redis的灵活性和效率。以列表对象为例，有压缩列表和双端链表两种编码方式；如果列表中的元素较少，Redis倾向于使用压缩列表进行存储，因为压缩列表占用内存更少，而且比双端链表可以更快载入；当列表对象元素较多时，压缩列表就会转化为更适合存储大量元素的双端链表。\n\n   通过object encoding命令，可以查看对象采用的编码方式，如下图所示：\n\n   ```c\n   127.0.0.1:6379> set mystr helloworld\n   OK\n   127.0.0.1:6379> object encoding mystr\n   \"embstr\"\n   ```\n\n3. lru\n\n   lru记录的是对象最后一次被命令程序访问的时间，占据的比特数不同的版本有所不同（如4.0版本占24比特，2.6版本占22比特）。\n\n   通过对比lru时间与当前时间，可以计算某个对象的空转时间；object idletime命令可以显示该空转时间（单位是秒）。object idletime命令的一个特殊之处在于它不改变对象的lru值。\n\n   ```c\n   127.0.0.1:6379> set mystr helloworld\n   OK\n   127.0.0.1:6379> object idletime mystr\n   (integer) 273\n   127.0.0.1:6379> get mystr\n   \"helloworld\"\n   127.0.0.1:6379> object idletime mystr\n   (integer) 2\n   ```\n\n   lru值除了通过object  idletime命令打印之外，还与Redis的内存回收有关系：如果Redis打开了maxmemory选项，且内存回收算法选择的是volatile-lru或allkeys—lru，那么当Redis内存占用超过maxmemory指定的值时，Redis会优先选择空转时间最长的对象进行释放。\n\n4. refcount\n\n   **refcount** **与共享对象**\n\n   refcount记录的是该对象被引用的次数，类型为整型。refcount的作用，主要在于对象的引用计数和内存回收。当创建新对象时，refcount初始化为1；当有新程序使用该对象时，refcount加1；当对象不再被一个新程序使用时，refcount减1；当refcount变为0时，对象占用的内存会被释放。\n\n   Redis中被多次使用的对象(refcount>1)，称为共享对象。Redis为了节省内存，当有一些对象重复出现时，新的程序不会创建新的对象，而是仍然使用原来的对象。这个被重复使用的对象，就是共享对象。目前共享对象仅支持整数值的字符串对象。\n\n   **共享对象的具体实现**\n\n   Redis的共享对象目前只支持整数值的字符串对象。之所以如此，实际上是对内存和CPU（时间）的平衡：共享对象虽然会降低内存消耗，但是判断两个对象是否相等却需要消耗额外的时间。对于整数值，判断操作复杂度为O(1)；对于普通字符串，判断复杂度为O(n)；而对于哈希、列表、集合和有序集合，判断的复杂度为O(n^2)。\n\n   虽然共享对象只能是整数值的字符串对象，但是5种类型都可能使用共享对象（如哈希、列表等的元素可以使用）。\n\n   就目前的实现来说，Redis服务器在初始化时，会创建10000个字符串对象，值分别是0~9999的整数值；当Redis需要使用值为0~9999的字符串对象时，可以直接使用这些共享对象。10000这个数字可以通过调整参数REDIS_SHARED_INTEGERS（4.0中是OBJ_SHARED_INTEGERS）的值进行改变。\n\n   共享对象的引用次数可以通过object refcount命令查看。\n\n5. ptr\n\n   ptr指针指向具体的数据，如以下例子中，set hello world，ptr指向包含字符串world的SDS\n\n### Redis对象类型\n\n1. 字符串\n\n   字符串是最基础的类型，因为所有的键都是字符串类型，且字符串之外的其他几种复杂类型的元素也是字符串。字符串长度不能超过512MB。\n\n   常用操作：\n\n   ```bash\n   添加：set username li\n   删除：del username\n   设置过期时间：set key value EX timeout或setex key timeout value\n   查看过期时间：ttl username\n   查看当前redis所有的key：keys *\n   ```\n\n2. 列表\n\n   列表（list）用来存储多个有序的字符串，每个字符串称为元素；一个列表可以存储2^32-1个元素。Redis中的列表支持两端插入和弹出，并可以获得指定位置（或范围）的元素，可以充当数组、队列、栈等。\n\n   常用操作：\n\n   ```bash\n   在列表左边添加元素：lpush key value\n   在列表右边添加元素：rpush key value\n   查看列表中的元素：lrange key start stop\n   移除并返回列表key的头元素：lpop key\n   移除并返回列表的尾元素：rpop key\n   指定返回第几个元素：lindex key index\n   获取列表中的元素个数：llen key\n   删除指定的元素：lrem key count value   count指定删除个数\n   ```\n\n3. 哈希\n\n   哈希（作为一种数据结构），不仅是redis对外提供的5种对象类型的一种（与字符串、列表、集合、有序结合并列），也是Redis作为Key-Value数据库所使用的数据结构。为了说明的方便，在本文后面当使用“内层的哈希”时，代表的是redis对外提供的5种对象类型的一种；使用“外层的哈希”代指Redis作为Key-Value数据库所使用的数据结构。\n\n   常用操作：\n\n   ```bash\n   添加一个新值：hset website baidu baidu.com\n   获取哈希中的field对应的值：hget website baidu\n   删除field中的某个field：hdel website baidu\n   获取某个哈希中所有的field和value：hgetall website\n   获取某个哈希中所有的field：hkeys website\n   获取某个哈希中所有的值：hvals website\n   判断哈希中是否存在某个field：hexists website baidu\n   获取哈希中总共的键值对：hlen website\n   ```\n\n4. 集合\n\n   集合（set）与列表类似，都是用来保存多个字符串，但集合与列表有两点不同：集合中的元素是无序的，因此不能通过索引来操作元素；集合中的元素不能有重复。\n\n   一个集合中最多可以存储2^32-1个元素；除了支持常规的增删改查，Redis还支持多个集合取交集、并集、差集。\n\n   常用操作：\n\n   ```bash\n   添加元素：sadd lan php java python\n   查看元素：smembers lan\n   移除元素：srem lan php\n   查看集合中的元素个数：scard lan\n   获取多个集合的交集：sinter team1 team2\n   获取多个集合的并集：sunion team1 team2\n   获取多个集合的差集：sdiff team1 team2    以左边的集合为标准\n   ```\n\n5. 有序集合\n\n   有序集合与集合一样，元素都不能重复；但与集合不同的是，有序集合中的元素是有顺序的。与列表使用索引下标作为排序依据不同，有序集合为每个元素设置一个分数（score）作为排序依据。\n\n   常用操作：\n\n   ```bash\n   添加元素：zadd zset 1.1 val1\n   查看集合：zcard zset1\n   集合排序：zrange zset1 0 2 withscores\n   ```\n\n## Redis内存统计\n\n工欲善其事必先利其器，在说明Redis内存之前首先说明如何统计Redis使用内存的情况。\n\n在客户端通过redis-cli连接服务器后（后面如无特殊说明，客户端一律使用redis-cli），通过info命令可以查看内存使用情况：\n\n```shell\ninfo memory\n# Memory\nused_memory:852896\nused_memory_human:832.91K\nused_memory_rss:3911680\nused_memory_peak:852896\nused_memory_peak_human:832.91K\nused_memory_lua:37888\nused_memory_lua_human:37.00K\nmem_fragmentation_ratio:4.82\nmem_allocator:jemalloc-5.1.0\n```\n\n其中，info命令可以显示redis服务器的许多信息，包括基本信息、CPU、内存、持久化、客户端连接信息等；memory是内存参数，表示只显示内存相关的信息。\n\n返回结果中比较重要的几个说明如下：\n\n1. used_memory：Redis分配器分配的内存总量（单位是字节），包括使用的虚拟内存（即，swap）。\n\n2. used_memory_rss：Redis进程占据操作系统的内存（单位是字节），与top命令及ps命令看到的值是一致的；除了分配器分配的内存之外，used_memory_rss还包括进程运行本身需要的内存、内存碎片等，但是不包括虚拟内存。\n\n   因此，used_memory和used_memory_rss，前者是从Redis角度得到的量，后者是从操作系统角度得到的量。二者之所以有所不同，一方面是因为内存碎片和Redis进程运行需要占用内存，使得前者可能比后者小，另一方面虚拟内存的存在，使得前者可能比后者大。\n\n   由于在实际应用中，Redis的数据量会比较大，此时进程运行占用的内存与Redis数据量和内存碎片相比，都会小得多；因此used_memory_rss和used_memory的比例，便成了衡量Redis内存碎片率的参数；这个参数就是mem_fragmentation_ratio。\n\n3. mem_fragmentation_ratio ：内存碎片比率，该值是used_memory_rss / used_memory的比值。\n\n   mem_fragmentation_ratio一般大于1，且该值越大，内存碎片比例越大。mem_fragmentation_ratio<1，说明Redis使用了虚拟内存，由于虚拟内存的媒介是磁盘，比内存速度要慢很多，当这种情况出现时，应该及时排查，如果内存不足应该及时处理，如增加Redis节点、增加Redis服务器的内存、优化应用等。\n\n   一般来说，mem_fragmentation_ratio在1.03左右是比较健康的状态（对于jemalloc来说）；上面截图中的mem_fragmentation_ratio值很大，是因为还没有向Redis中存入数据，Redis进程本身运行的内存使得used_memory_rss 比used_memory大得多。\n\n4. mem_allocator：Redis使用的内存分配器，在编译时指定；可以是 libc 、jemalloc或者tcmalloc，默认是jemalloc；截图中使用的便是默认的jemalloc。","categoryId":2,"viewCount":1301,"categoryName":"中间件","author":"球接子","authorAvatar":null,"tagIds":[1,14,17],"tagNames":["Redis","中间件","数据库"]}}