• Redis数据库介绍


    引言

    redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。

    redis数据结构

    redis是一种高级的key:value存储系统,其中value支持五种数据类型:

    字符串(strings)

    字符串列表(lists)

    字符串集合(sets)

    有序字符串集合(sorted sets)

    哈希(hashes)

    而关于key,有几个点要提醒大家:

    key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;

    key也不要太短,太短的话,key的可读性会降低;

    在一个项目中,key最好使用统一的命名模式,例如user:10000:passwd。

    redis数据结构 – strings

    有人说,如果只使用redis中的字符串类型,且不使用redis的持久化功能,那么,redis就和memcache非常非常的像了。这说明strings类型是一个很基础的数据类型,也是任何存储系统都必备的数据类型。

    我们来看一个最简单的例子:

    set mystr "hello world!" //设置字符串类型

    get mystr //读取字符串类型

    字符串类型的用法就是这么简单,因为是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储。

    另外,我们还可以通过字符串类型进行数值操作:

    127.0.0.1:6379> set mynum "2"

    OK

    127.0.0.1:6379> get mynum

    "2"

    127.0.0.1:6379> incr mynum

    (integer) 3

    127.0.0.1:6379> get mynum

    "3"

    看,在遇到数值操作时,redis会将字符串类型转换成数值。

    由于INCR等指令本身就具有原子操作的特性,所以我们完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果,假如,在某种场景下有3个客户端同时读取了mynum的值(值为2),然后对其同时进行了加1的操作,那么,最后mynum的值一定是5。不少网站都利用redis的这个特性来实现业务上的统计计数需求。

    redis数据结构 – lists

    redis的另一个重要的数据结构叫做lists,翻译成中文叫做“列表”。

    首先要明确一点,redis中的lists在底层实现上并不是数组,而是链表,也就是说对于一个具有上百万个元素的lists来说,在头部和尾部插入一个新元素,其时间复杂度是常数级别的,比如用LPUSH在10个元素的lists头部插入新元素,和在上千万元素的lists头部插入新元素的速度应该是相同的。

    虽然lists有这样的优势,但同样有其弊端,那就是,链表型lists的元素定位会比较慢,而数组型lists的元素定位就会快得多。

    lists的常用操作包括LPUSH、RPUSH、LRANGE等。我们可以用LPUSH在lists的左侧插入一个新元素,用RPUSH在lists的右侧插入一个新元素,用LRANGE命令从lists中指定一个范围来提取元素。我们来看几个例子:

    //新建一个list叫做mylist,并在列表头部插入元素"1"

    127.0.0.1:6379> lpush mylist "1"

    //返回当前mylist中的元素个数

    (integer) 1

    //在mylist右侧插入元素"2"

    127.0.0.1:6379> rpush mylist "2"

    (integer) 2

    //在mylist左侧插入元素"0"

    127.0.0.1:6379> lpush mylist "0"

    (integer) 3

    //列出mylist中从编号0到编号1的元素

    127.0.0.1:6379> lrange mylist 0 1

    1) "0"

    2) "1"

    //列出mylist中从编号0到倒数第一个元素

    127.0.0.1:6379> lrange mylist 0 -1

    1) "0"

    2) "1"

    3) "2"

    lists的应用相当广泛,随便举几个例子:

    我们可以利用lists来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来进行排序。

    利用LRANGE还可以很方便的实现分页的功能。

    redis数据结构 – 集合

    redis的集合,是一种无序的集合,集合中的元素没有先后顺序。

    集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集等。我们来看例子:

    //向集合myset中加入一个新元素"one"

    127.0.0.1:6379> sadd myset "one"

    (integer) 1

    127.0.0.1:6379> sadd myset "two"

    (integer) 1

    //列出集合myset中的所有元素

    127.0.0.1:6379> smembers myset

    1) "one"

    2) "two"

    //判断元素1是否在集合myset中,返回1表示存在

    127.0.0.1:6379> sismember myset "one"

    (integer) 1

    //判断元素3是否在集合myset中,返回0表示不存在

    127.0.0.1:6379> sismember myset "three"

    (integer) 0

    //新建一个新的集合yourset

    127.0.0.1:6379> sadd yourset "1"

    (integer) 1

    127.0.0.1:6379> sadd yourset "2"

    (integer) 1

    127.0.0.1:6379> smembers yourset

    1) "1"

    2) "2"

    //对两个集合求并集

    127.0.0.1:6379> sunion myset yourset

    1) "1"

    2) "one"

    3) "2"

    4) "two"

    对于集合的使用,也有一些常见的方式,比如,QQ有一个社交功能叫做“好友标签”,大家可以给你的好友贴标签,比如“大美女”、“土豪”、“欧巴”等等,这时就可以使用redis的集合来实现,把每一个用户的标签都存储在一个集合之中。

    redis数据结构 – 有序集合

    redis不但提供了无需集合(sets),还很体贴的提供了有序集合(sorted sets)。有序集合中的每个元素都关联一个序号(score),这便是排序的依据。

    很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等

    老规矩,我们来看几个生动的例子:

    //新增一个有序集合myzset,并加入一个元素baidu.com,给它赋予的序号是1:

    127.0.0.1:6379> zadd myzset 1 baidu.com

    (integer) 1

    //向myzset中新增一个元素360.com,赋予它的序号是3

    127.0.0.1:6379> zadd myzset 3 360.com

    (integer) 1

    //向myzset中新增一个元素google.com,赋予它的序号是2

    127.0.0.1:6379> zadd myzset 2 google.com

    (integer) 1

    //列出myzset的所有元素,同时列出其序号,可以看出myzset已经是有序的了。

    127.0.0.1:6379> zrange myzset 0 -1 withscores

    1) "baidu.com"

    2) "1"

    3) "google.com"

    4) "2"

    5) "360.com"

    6) "3"

    //只列出myzset的元素

    127.0.0.1:6379> zrange myzset 0 -1

    1) "baidu.com"

    2) "google.com"

    3) "360.com"

    redis数据结构 – 哈希

    最后要给大家介绍的是hashes,即哈希。哈希是从redis-2.0.0版本之后才有的数据结构。

    hashes存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。

    我们来看一个例子:

    //建立哈希,并赋值

    127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34

    OK

    //列出哈希的内容

    127.0.0.1:6379> HGETALL user:001

    1) "username"

    2) "antirez"

    3) "password"

    4) "P1pp0"

    5) "age"

    6) "34"

    //更改哈希中的某一个值

    127.0.0.1:6379> HSET user:001 password 12345

    (integer) 0

    //再次列出哈希的内容

    127.0.0.1:6379> HGETALL user:001

    1) "username"

    2) "antirez"

    3) "password"

    4) "12345"

    5) "age"

    6) "34"

    用法

    像MySQL一样,redis是支持主从同步的,而且也支持一主多从以及多级从结构。

    主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的SORT就可以由从服务器来承担。

    redis的主从同步是异步进行的,这意味着主从同步不会影响主逻辑,也不会降低redis的处理性能。

    主从架构中,可以考虑关闭主服务器的数据持久化功能,只让从服务器进行持久化,这样可以提高主服务器的处理性能。

    在主从架构中,从服务器通常被设置为只读模式,这样可以避免从服务器的数据被误修改。但是从服务器仍然可以接受CONFIG等指令,所以还是不应该将从服务器直接暴露到不安全的网络环境中。如果必须如此,那可以考虑给重要指令进行重命名,来避免命令被外人误执行。

    同步原理

    从服务器会向主服务器发出SYNC指令,当主服务器接到此命令后,就会调用BGSAVE指令来创建一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中。

    在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。

    另外,要说的一点是,即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个下游。在redis2.8版本之前,如果从服务器与主服务器因某些原因断开连接的话,都会进行一次主从之间的全量的数据同步;而在2.8版本之后,redis支持了效率更高的增量同步策略,这大大降低了连接断开的恢复成本。

    主服务器会在内存中维护一个缓冲区,缓冲区中存储着将要发给从服务器的内容。从服务器在与主服务器出现网络瞬断之后,从服务器会尝试再次与主服务器连接,一旦连接成功,从服务器就会把“希望同步的主服务器ID”和“希望请求的数据的偏移位置(replication offset)”发送出去。主服务器接收到这样的同步请求后,首先会验证主服务器ID是否和自己的ID匹配,其次会检查“请求的偏移位置”是否存在于自己的缓冲区中,如果两者都满足的话,主服务器就会向从服务器发送增量内容。

    增量同步功能,需要服务器端支持全新的PSYNC指令。这个指令,只有在redis-2.8之后才具有。

    聊聊redis的事务处理

    众所周知,事务是指“一个完整的动作,要么全部执行,要么什么也没有做”。

    在聊redis事务处理之前,要先和大家介绍四个redis指令,即MULTI、EXEC、DISCARD、WATCH。这四个指令构成了redis事务处理的基础。

    MULTI用来组装一个事务;

    EXEC用来执行一个事务;

    DISCARD用来取消一个事务;

    WATCH用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。

    实战

    我们先抛出问题,在广告程序化交易的过程中,我们经常需要为一个广告投放计划定制人群包,其存储的形式如下:

    人群包ID => [设备ID_1, 设备ID_2 ... 设备ID_N]

    其中,人群包ID是Long型整数,设备ID是经过MD5处理,长度为32。

    在业务场景中,我们需要判断一个设备ID是否在一个人群包中,来决定是否投放广告。

    在传统的使用Redis的场景, 我们可以使用标准的KV结构来存储定向包数据,则存储方式如下:

    {人群包ID}_{设备ID_1} => true

    {人群包ID}_{设备ID_2} => true

    如果我们想使用ziplist来继续内存压缩的话,我们必须保证Hash对象的长度小于512,并且键值的长度小于64字节。 我们可以将KV结构的数据,存储到预先分配好的bucket中。

    我们先预估下,整个Redis集群预计容纳的数据条数为10亿,那么Bucket的数量的计算公式如下:

    bucket_count = 10亿 / 512 = 195W

    那么我们大概需要200W个Bucket(预估Bucket数量需要多预估一点,以防触发临界值问题)

    我们先以下公式计算BucketID:

    bucket_id = CRC32(人群包ID + "_" + 设备ID) % 200W

    那么数据在Redis的存储结构就变成

    bucket_id => {

    {人群包ID}_{设备ID_1} => true

    {人群包ID}_{设备ID_2} => true

    }

    这样我们保证每个bucket中的数据项都小于512,并且长度均小于64字节。

    我们以2000W数据进行测试,前后两者的内存使用情况如下:

    数据集大小存储模式Bucket数量所用内存碎片率Redis占用的内存
    2000W 压缩列表 200W 928M 1.38 1.25G
    2000W 压缩列表 5W 785M 1.48 1.14G
    2000W 直接存储 - 1.44G 1.03 1.48G

    在这里需要额外引入一个概念 -- 内存碎片率。

    内存碎片率 = 操作系统给Redis分配的内存 / Redis存储对象占用的内存

    因为压缩列表在更新节点的时候,经常需要进行内存重分配,所以导致比较高的内存碎片率。我们在做技术方案比较的时候,内存碎片率也是非常需要关注的指标之一。

    但有很多手段可以减少内存碎片率,比如内存对其,甚至更极端的直接重做整个Redis内存(利用快照或者从节点来重做内存)都能有效的减低内存碎片率。

    我们在本次实验中,因为存储的数值比较大(单个KEY约34个字节),所以实际节省内存不是很多,但依然能节约35%-50%的内存使用。

    在实际的生产环境中,我们根据应用场景合理的设计压缩存储结构,部分业务甚至能达到节约70%的内存使用的效果。

    总结

    以 上就是我对Java开发大型互联网Redis数据库介绍之redis数据库实战及其优化总结,分享给大家,希望大家知道什么是Java开发大型互联网Redis数据库介绍之redis数据库实战问题及其优化。觉得收获的话可以点个关注收藏转发一波喔,谢谢大佬们支持!

    1、多写多敲代码,好的代码与扎实的基础知识一定是实践出来的

    2、可以去百度搜索腾讯课堂图灵学院的视频来学习一下java架构实战案例,还挺不错的。

    最后,每一位读到这里的网友,感谢你们能耐心地看完。希望在成为一名更优秀的Java程序员的道路上,我们可以一起学习、一起进步!

    3丶想了解学习以上课程内容可加群:32848930

    Redis 密码设置和查看密码

    redis没有实现访问控制这个功能,但是它提供了一个轻量级的认证方式,可以编辑redis.conf配置来启用认证。

       1、初始化Redis密码:

       在配置文件中有个参数: requirepass  这个就是配置redis访问密码的参数;

       比如 requirepass test123;

       (Ps:需重启Redis才能生效)

       redis的查询速度是非常快的,外部用户一秒内可以尝试多大150K个密码;所以密码要尽量长(对于DBA 没有必要必须记住密码);

       2、不重启Redis设置密码:

       在配置文件中配置requirepass的密码(当redis重启时密码依然有效)。

       redis 127.0.0.1:6379> config set requirepass test123

       查询密码:

       redis 127.0.0.1:6379> config get requirepass
       (error) ERR operation not permitted

       密码验证:

       redis 127.0.0.1:6379> auth test123
       OK

       再次查询:

       redis 127.0.0.1:6379> config get requirepass
       1) "requirepass"
       2) "test123"

       PS:如果配置文件中没添加密码 那么redis重启后,密码失效;

       3、登陆有密码的Redis:

       在登录的时候的时候输入密码:

       redis-cli -p 6379 -a test123

       先登陆后验证:

       redis-cli -p 6379

       redis 127.0.0.1:6379> auth test123
       OK

       AUTH命令跟其他redis命令一样,是没有加密的;阻止不了攻击者在网络上窃取你的密码;

       认证层的目标是提供多一层的保护。如果防火墙或者用来保护redis的系统防御外部攻击失败的话,外部用户如果没有通过密码认证还是无法访问redis的。

  • 相关阅读:
    C#异步编程由浅入深(一)
    基于Jira的运维发布平台的设计与实现
    kubeadm部署K8S并使用containerd做运行时
    代码阅读
    黑客攻防 1
    Linux 常用指令篇1
    期刊管理系统总结
    docker安装RabbitMQ
    Centos7安装mysql8.0教程
    java动态编译
  • 原文地址:https://www.cnblogs.com/river2005/p/redis.html
Copyright © 2020-2023  润新知